DEV Community

Superwavvy
Superwavvy

Posted on

My Arbitrage Journey.

If you spend enough time on CT (Crypto Twitter), you’ll see threads promising that you can make passive income by building a simple arbitrage bot. "Just buy on DEX A, sell on DEX B, and pocket the difference", lol.

I've actually built one, check out the article & code.

Here is how I built a fully automated, event-driven flashloan arbitrage bot on Arbitrum, and why I eventually had to turn it off.

The Setup

I didn't want to build a toy script, I wanted a production-grade MEV (Maximal Extractable Value) bot.

The Stack:

· Node.js + Ethers v6: To monitor prices in real-time.
· WebSockets (WSS): Instead of polling RPC nodes every minute (which is too slow), I used WSS to listen directly to Swap events on Uniswap V3 and Camelot V3.
· Balancer V2: I used Balancer for 0% fee flashloans. This means I could borrow $10,000+ with zero collateral, execute the trade, repay the loan, and keep the profit.
· Foundry: To write and test the Solidity ArbitrageExecutor contract.
· Botkeep: A $0/month VPS to run the bot 24/7.

I built a Solidity contract that could hold the logic: request a flashloan, swap on DEX 1, swap back on DEX 2, repay the loan, and sweep the profit.

The Mirage

After deploying the contract and linking it to my Node.js bot, I ran my Foundry tests against a fork of the Arbitrum mainnet.

The test executed on a historical block and printed this:

SUCCESS!
pnl (wei): 21742847117914354 (A profit of ~$52)

I was happy, I thought I had found a massive 2.1% gap between Camelot and Uniswap.

But then my co-developer(Claude of course ) and I decided to run an isolated test. We created three separate test functions, each starting from a fresh fork, to verify the trade size.

The result was a crazy:

· Camelot to Uni500 (0.25 WETH): -0.0013 WETH (-0.52%)
· Camelot to Uni500 (1 WETH): -0.0174 WETH (-1.7%)
· Uni500 to Camelot (0.25 WETH): -0.0016 WETH (-0.63%)

The $52 profit was actually false.

The Testing Trap (State Contamination)

Here is why the test lied to me: In my original test, I ran all three trading legs back-to-back in the same fork state.

  1. Leg 1 swapped 1 WETH into Camelot, pushing the price of ARB down artificially (causing a loss).
  2. Leg 2 then bought that artificially cheap ARB back from Camelot.

Little did I know that I wasn't arbitraging the market, I was arbitraging my own artificial slippage. Once we isolated the tests, the truth came out: every single path lost money.

The Brutal Mathematics of Fees

Once I accepted that my test was flawed, I built a Node.js scanner to check the real-time price differences across Uniswap V3, Camelot V3, and PancakeSwap for multiple mid-cap Arbitrum tokens (GMX, PENDLE, MAGIC, etc.).

I was expecting to find opportunities, instead, I found that the market is ruthlessly efficient.

· GMX: Had a 59 bps (0.59%) spread. One of the Uniswap pools for GMX charges a 1% fee & the 1% fee clears the 0.59% spread.
· PENDLE: Had a 20 bps (0.20%) spread but one side of the trade had a 0.3% fee that means you’d lose 0.1% on every trade.
· wstETH: 1 bps which was fully efficient.
· MAGIC & RDNT: These pools were so thin that any trade over $50 would cause massive slippage, making it impossible to execute.

The reality of DEX-to-DEX arbitrage is that high-frequency trading (HFT) bots are watching the exact same blockchain as you & they're co-located near the sequencer, they also have 0ms latency, and eat any gap the moment it opens.

If a gap is sitting there long enough for your Node.js bot to spot it and send a transaction, it's usually because the pool doesn't have enough liquidity to make the trade worth executing, or the DEX fees eat the entire profit.

The Execution (What would actually happen)

Before I paused the bot, I added staticCall simulation to prevent losing money on failed transactions.

When the bot sees a gap, it first simulates the transaction. If the minPnl guard (the minimum profit required) fails, the simulation would revert then the bot cancels the transaction and it won't cost me any gas fees.

If I hadn't added that, every time the bot detected a gap, it would send the transaction to the Arbitrum network, fail on-chain, and burn gas. At $0.10 per failed transaction, it might look small, but it's still data.

Epilogue

Going Live with this bot was actually an educational experience, cos I was able to:
· Use WebSockets to listen for on-chain events in real-time.
· Write a Solidity contract that executes Balancer flashloans.
· Protect the contract against callback hijacking (using an _expected hash).
· Implement safe testing practices (never run multiple legs in the same fork state!).

But the biggest lesson was realizing that the "easy money" of DEX arbitrage is either a myth or requires high tech & The landscape is dominated by HFT bots and the math is dominated by DEX fees.

Top comments (0)