Skip to content
Builder106Public

About

An automated currency price-matching bot that spots temporary price differences across crypto exchanges and balances them instantly.

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

138 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Quarry: A bare-metal MEV arbitrage simulator

CI Solidity Foundry Bun Yul runtime Two-hop gas License Live site

An automated currency price-matching bot. Quarry spots temporary price differences across crypto exchanges and balances them instantly.

πŸ’‘ What is Quarry?

If a store across town sells an item for $10 and another buys it for $12, a quick courier can make a $2 profit while helping keep prices fair. Quarry monitors decentralized exchanges around the clock, spotting momentary price differences and executing instant trades with zero upfront capital using flash loans.

Quarry operates as a hybrid MEV arbitrage engine. A TypeScript scanner watches Ethereum's public mempool for swaps about to land on a Uniswap-V2-shaped DEX. For each candidate, it computes the post-trade price impact, runs a closed-form optimal-input solver against the reserves, and packs a 220-byte calldata payload whenever round-trip profit covers fees and gas. The executor is a custom Yul contract (492 bytes of runtime bytecode) that performs the full two-hop arbitrage and reverts atomically if realized profit falls short.

Scope: Quarry targets cross-DEX arbitrage (closing price gaps to improve market price efficiency). Predatory strategies such as sandwiches or JIT attacks against retail traders are strictly out of scope. See CONTRIBUTING.md.

How it works

The executor keeps a trade only when its own balance grows by at least minProfit. Otherwise the transaction reverts and neither swap takes effect.

Off-chain, the bot decodes a pending swap, sizes the trade, and signs a transaction. On-chain, the executor swaps on the victim pool, then on the counter pool, then checks whether its profit covers minProfit. If it does, it keeps the profit. If not, it reverts and both swaps are undone.

The scanner decodes a pending Uniswap V2 swap, reads both pools through Multicall3, and sizes the trade with a closed-form solver. A signed EIP-1559 transaction carries 220 bytes of packed calldata to the Yul executor, which swaps on the victim pool with to=Pool2 and then on the counter pool with to=this. Editable HTML source.

The executor is built in monolithic Yul bytecode without heavy framework overhead. Data is read directly as a tightly packed byte string. Pool 1's output flows directly to pool 2 (a classic decentralized exchange optimization where tokens transfer straight between liquidity pools, skipping intermediate transfers to save transaction fees). A balance snapshot at entry and exit guards the trade: if the price difference disappears before the transaction completes, the entire transaction cancels automatically with zero loss of funds.

Repo layout

Quarry/
β”œβ”€β”€ contracts/                    # Foundry project: smart contract executor + tests
β”‚   β”œβ”€β”€ src/Executor.yul          # 492-byte runtime, two-hop executor
β”‚   β”œβ”€β”€ test/Executor.t.sol       # Tests, mock pools, safety assertions
β”‚   β”œβ”€β”€ test/ExecutorFork.t.sol   # Tests against real network state
β”‚   └── foundry.toml
β”œβ”€β”€ bot/                          # TypeScript scanner (Bun runtime)
β”‚   β”œβ”€β”€ src/amm.ts                # Constant-product pricing math + solver
β”‚   β”œβ”€β”€ src/decode.ts             # Transaction decoder + exchange registry
β”‚   β”œβ”€β”€ src/pairs.ts              # Address derivation utilities
β”‚   β”œβ”€β”€ src/reserves.ts           # Multi-pool reserve fetcher
β”‚   β”œβ”€β”€ src/score.ts              # Opportunity scoring engine
β”‚   β”œβ”€β”€ src/gas.ts                # Gas price estimator
β”‚   β”œβ”€β”€ src/bundle.ts             # Compact payload builder
β”‚   β”œβ”€β”€ src/sign.ts               # Transaction signing
β”‚   β”œβ”€β”€ src/scanner.ts            # Live transaction stream scanner
β”‚   β”œβ”€β”€ scripts/demo.ts           # End-to-end demo runner
β”‚   └── test/                     # 60 automated tests, 2,121 assertions
β”œβ”€β”€ JOURNAL.md                    # Architecture and incident log
β”œβ”€β”€ CONTRIBUTING.md               # Scope and contribution guidelines
└── LICENSE

The two subtrees are intentionally independent. They communicate only through deployed contract addresses and standard binary interfaces. See JOURNAL.md for details.

Quick start

# Toolchain (macOS)
brew install foundry oven-sh/bun/bun

# Clone + install deps
git clone https://github.com/Builder106/quarry.git
cd quarry
cp .env.example .env   # fill in MAINNET_RPC_URL for fork tests

# On-chain side
cd contracts
forge install
forge test                               # 7 mock tests, fork test skips without RPC
MAINNET_RPC_URL=https://... forge test   # full suite incl. real-pool fork test

# Off-chain side
cd ../bot
bun install
bun run typecheck
bun test                                 # 60 tests, 2,121 assertions

Demo: end-to-end against forked mainnet

The full pipeline runs against a local anvil fork β€” including the Aave V3 flashloan that funds the back-run. The bot holds zero inventory; Aave fronts the WETH and gets repaid atomically + 5 bp premium inside the same transaction.

Quarry demo β€” back-run pipeline scoring a 1M-USDC victim swap, borrowing 5.3 WETH from Aave V3, and netting 0.557 WETH against real Uniswap V2 + Sushiswap pools

To reproduce locally:

# Terminal 1 β€” fork mainnet at HEAD
anvil --fork-url https://ethereum-rpc.publicnode.com

# Terminal 2 β€” run the pipeline
cd bot
bun run demo
Full trace β€” click to expand the verbatim output of bun run demo
[demo] anvil reachable at http://localhost:8545
[demo] bot account: 0xf39F…2266

━━━ DEPLOY ━━━
[demo] executor deployed at 0xabab79ac4a0ea727dd012cb1b843fe84baed64b6
[demo] deploy gas used: 181484

━━━ SCORE (pre-victim) ━━━
[demo]   UniV2  0xB4e1…C9Dc  USDC=9141845.119648 USDC  WETH=4454.860040 WETH
[demo]   Sushi  0x397F…ACa0  USDC=127232.684254 USDC  WETH=61.999499 WETH
[demo] hypothetical victim: 0x0000…bEEF sells 1000000.000000 USDC for WETH on UniV2
[demo] scored uniswap-v2 β†’ sushiswap
[demo]   base:         WETH (0xC02a…6Cc2)
[demo]   intermediate: USDC (0xA0b8…eB48)
[demo]   amountIn:        5.301040 WETH
[demo]   intermediateOut: 13326.704675 USDC
[demo]   expectedOut:     5.862323 WETH
[demo]   predicted profit: 0.561283 WETH
[demo]   gas estimate:    163085461950000 wei

━━━ VICTIM EXECUTION ━━━
[demo] dealt 1000000.000000 USDC to victim
[demo] victim swap mined: 0x55c951d0984ccfc4c0dc8dd187dba8e30d46ab41ce4dd8cf7cbbf645cd6b1c36
[demo]   UniV2  0xB4e1…C9Dc  USDC=10141845.119648 USDC  WETH=4016.792843 WETH
[demo]   Sushi  0x397F…ACa0  USDC=127232.684254 USDC  WETH=61.999499 WETH

━━━ FLASHLOAN BUNDLE ━━━
[demo] borrowing 5.301040 WETH from Aave V3 (0x8787…A4E2)
[demo] Aave premium (5 bp on WETH): 0.002650 WETH
[demo] expected net (profit βˆ’ premium): 0.558632 WETH
[demo] target: 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2
[demo] calldata: 0x42b0b77c000000000000…00000000 (420 bytes)
[demo] signed tx: 0x02f9021301821920843b…5eb979a7
[demo] flashloan tx mined: 0x24a799fedb42d8c23dfb42a987d8cece89fffe76bc96d5b21cdb077332edc4bd
[demo] tx gas used:        266694

━━━ VERIFY ━━━
[demo] executor's final WETH balance: 0.558046 WETH
[demo] gross predicted profit:        0.561283 WETH
[demo] aave premium:                  0.002650 WETH
[demo] net predicted profit:          0.558632 WETH
[demo] net realized profit:           0.558046 WETH
[demo] prediction accuracy: 99.89% of expected

[demo] βœ“ end-to-end flashloan-funded pipeline complete.

The 0.11% drift between net predicted and net realized is exactly the 2 bp safety margin baked into the calldata builder for Uniswap V2's K-invariant integer-arithmetic check β€” see JOURNAL.md for why.

Historical-arb replay (bun run replay)

A second script reads the current WETH/USDC reserves on Uniswap V2 + Sushiswap (no synthetic victim), runs the bot's standing-arb math in both possible round-trip directions, and either executes via Aave V3 flashloan when a real profitable gap exists, or prints a diagnostic gap analysis showing each direction's predicted loss-to-fees.

# Terminal 1 β€” anvil at HEAD (no archival needed)
anvil --fork-url https://ethereum-rpc.publicnode.com

# Terminal 1, alternate β€” pin to a known-arb block (needs archival RPC)
anvil --fork-url $ALCHEMY_URL --fork-block-number 15990000

# Terminal 2
cd bot && bun run replay

At any random recent block, the typical cross-DEX gap on WETH/USDC is well below the combined ~0.6% round-trip fee β€” competing MEV bots close real gaps within the same block they form. The script's diagnostic mode shows that:

━━━ RESERVE SNAPSHOT ━━━
[replay] UniV2  0xB4e16d…  USDC=9152516.072170 USDC  WETH=4449.682244 WETH
[replay] Sushi  0x397FF1…  USDC=127338.484262 USDC  WETH=61.948146 WETH
[replay] cross-DEX gap: 0.060% (round-trip fee floor is ~0.60%)
…
━━━ DIAGNOSIS ━━━
[replay] no profitable arb at block 25189150.
[replay] this is normal at any random recent block β€” competing
[replay] MEV bots close cross-DEX gaps within the same block they
[replay] form. To find a real historical opportunity, pin anvil
[replay] to a block of high volatility (large liquidations, mempool
[replay] congestion, etc.) and re-run with archival RPC access.

Pinning FORK_BLOCK to a moment of high volatility (liquidation cascades, large mempool trades that landed without a back-runner) converts the diagnostic into a real historical-arb replay.

Performance gates

Surface Gate Current
Two-hop arb total gas (real pools) ≀ 130,000 on forked mainnet 110,780
Yul runtime bytecode size ≀ 512 B 492 B
Mocks-test gas regression ceiling ≀ 50,000 48,976
Scanner p99 tx-to-decision ≀ 20 ms on 1-hour sample β€” (not yet benched)
Scanner hot path 0 heap allocations per pending-tx event β€”

Forked-mainnet gas measurement uses real Uniswap V2 + Sushiswap WETH/USDC pairs at HEAD. The Yul executor's own opcodes contribute ~6k of that β€” the remainder is the unavoidable cost of the two pool swap() calls themselves. A vanilla Solidity equivalent of this contract typically lands at 150–250k total under the same conditions.

Snapshots checked into contracts/.gas-snapshot. PRs that regress these need justification β€” see CONTRIBUTING.md.

What's in V0 (and what isn't)

Works today

  • Yul executor: chained two-hop swap on Uniswap-V2-shaped pools with balance-snapshot revert guard. Real-pool fork test green at 110k gas.
  • Scanner: WebSocket pending-tx β†’ router filter β†’ calldata decode β†’ multicall reserves β†’ back-run scoring (apply victim β†’ solve optimal input) β†’ 220-byte calldata.
  • Gas gate: refuses to surface opportunities where profit (in WETH terms) doesn't beat the bundle's gas cost at current gas prices.
  • EIP-1559 signing + Flashbots-shape eth_sendBundle envelope.
  • End-to-end demo runs against forked mainnet, asserts realized profit matches prediction within 1%.

Known V0 limitations (documented in JOURNAL.md)

  • Scanner only scores the first hop of a victim's path β€” multi-hop paths fan out the search surface but aren't yet expanded.
  • Only exactInForTokens victim swaps trigger scoring; ETH-side variants (swapExactETHForTokens, etc.) decode but don't yet plumb tx.value through.
  • Gas gate only fires for WETH-base trades; non-WETH-base profits would need a WETH/baseToken conversion via a third reserve fetch.
  • No flashloan integration β€” the demo simulates the flashloan via setStorageAt. Production deployment would add an Aave V3 flashloan tx in bundle position 0.
  • No actual Flashbots relay submission. The bundle is shaped correctly but the demo submits to a local anvil, not a real relay.

License

MIT β€” see LICENSE.

About

An automated currency price-matching bot that spots temporary price differences across crypto exchanges and balances them instantly.

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Contributors

Languages