
In high-frequency EVM on-chain arbitrage, standard router calls and naive RPC polling create unviable latency and gas overhead. Competing successfully across Uniswap V2, Aerodrome, and Uniswap V3 pools requires an end-to-end engineered pipeline: custom Solidity execution contracts that bypass periphery routers to swap directly against pair storage, warm storage slot preservation (“dust holding”) to prevent 20,000-gas cold-write penalties, closed-form virtual pool folding for microsecond mathematical optimization, dual-phase pending block simulation with explicit L1 and operator fee floors, and single-signature multi-node broadcast racing with fail-safe in-flight locks.
1. The Production Failure State: Why Textbook MEV Bots Lose Capital

Textbook EVM arbitrage tutorials typically instruct developers to listen for new blocks, fetch pool reserves, run an off-chain binary search, and call UniswapV2Router.swapExactTokensForTokens wrapped in a flash loan.
When deployed to competitive production environments such as Base, Arbitrum, or Ethereum L1, this naive pattern fails consistently due to four structural bottlenecks:
[Failure Mode 1: The Periphery Router Gas Tax]
Standard Router Execution:
1. Transfer token to Router (cold balance checks)
2. Router calls Pair.swap()
3. Dynamic path array decoding + balance assertions
Gas Consumed: ~140,000 - 180,000 gas per 2-hop route
Result: The gross price gap was 0.004 ETH, but transaction gas cost 0.005 ETH. Net loss on execution.
[Failure Mode 2: Cold Storage Allocation Penalties (SSTORE 0 -> Non-Zero)]
T0: Contract completes arbitrage, extracts 100% of WETH profit to owner address. WETH balance = 0.
T1: Next block: new opportunity appears. Contract receives borrowed WETH.
EVM Opcode Reality: Writing to an ERC-20 balance slot transitioning from 0 to non-zero costs 20,000 gas (G_sset).
Cost per Hop: Up to 51,300 gas wasted across a 3-hop route simply re-allocating zeroed storage slots.
[Failure Mode 3: Fee Blindness and Revert Overhead]
T0: Bot discovers 0.002 WETH theoretical gross profit across Pool A and Pool B.
T1: Submits transaction with minProfit = 0.0001 WETH.
T2: On L2 (Base/OP Stack), L1 data rollup fee (DA fee) and operator fee surge due to blob congestion.
Actual Result: Gross profit = 0.002 WETH, but total L1 + L2 fee = 0.0031 ETH.
Contract executes, but operator wallet loses 0.0011 ETH on net balance.
To achieve profitability and operational survivability, an arbitrage engine must operate as a unified system where the smart contract’s gas layout directly dictates how the off-chain mathematical solver constructs execution payloads.
2. On-Chain Execution Layer: FlashMultiHopArb.sol
The execution core is implemented in FlashMultiHopArb.sol, deployed and verified across EVM mainnets. It unifies Uniswap V3 flash lending, direct Uniswap V2/Aerodrome pair swaps, Uniswap V3 concentrated liquidity swaps, and ERC-4626 vault mint/redeem actions in a single atomic callback loop.
Callback Isolation and Reentrancy Defense
Flash loans rely on asynchronous callbacks. If a contract exposes a public uniswapV3FlashCallback or uniswapV2Call, any external caller could invoke the callback directly with forged calldata to manipulate internal state or drain approvals.
In src/FlashMultiHopArb.sol:L51-L105, callback entry is guarded by a transient address lock (activeCallbackPool):
contract FlashMultiHopArb {
address public immutable owner;
address public immutable weth;
// Transient callback lock: prevents unauthorized contracts from invoking callbacks
address private activeCallbackPool;
// Route execution types
uint8 public constant DEX_V2 = 0; // Uniswap V2 / Sushi V2 / Aerodrome Volatile
uint8 public constant DEX_V3 = 1; // Uniswap V3 / Slipstream Concentrated
uint8 public constant DEX_ERC4626_DEPOSIT = 2; // Vault deposit (Underlying -> Shares)
uint8 public constant DEX_ERC4626_REDEEM = 3; // Vault redeem (Shares -> Underlying)
error Unauthorized();
error InsufficientProfit(uint256 actualProfit, uint256 minRequired);
error InvalidBribeBps(uint256 bribeBps);
error ZeroOutputOnHop(uint256 hopIndex);
error UnsupportedDexType(uint8 dexType);
modifier onlyOwner() {
if (msg.sender != owner) revert Unauthorized();
_;
}
function executeArbitrage(
address flashPool,
address borrowToken,
bool borrowIsToken0,
uint256 amountBorrow,
uint256 minProfit,
uint256 bribeBps,
SwapStep[] calldata steps
) external onlyOwner returns (uint256 netProfit) {
if (bribeBps > 10000) revert InvalidBribeBps(bribeBps);
uint256 balanceBefore = IERC20(borrowToken).balanceOf(address(this));
bytes memory data = abi.encode(
FlashCallbackData({
borrowToken: borrowToken,
amountBorrowed: amountBorrow,
minProfit: minProfit,
bribeBps: bribeBps,
steps: steps
})
);
// Arm callback lock with authorized pool address
activeCallbackPool = flashPool;
IUniswapV3Pool(flashPool).flash(
address(this),
borrowIsToken0 ? amountBorrow : 0,
borrowIsToken0 ? 0 : amountBorrow,
data
);
activeCallbackPool = address(0); // Disarm callback lock
uint256 balanceAfter = IERC20(borrowToken).balanceOf(address(this));
netProfit = balanceAfter - balanceBefore;
// Optional miner bribe (block.coinbase payout for Flashbots / MEV relays)
if (bribeBps > 0 && borrowToken == weth) {
uint256 bribeAmount = (netProfit * bribeBps) / 10000;
if (bribeAmount > 0) {
IWETH(weth).withdraw(bribeAmount);
(bool ok, ) = block.coinbase.call{value: bribeAmount}("");
require(ok, "Bribe failed");
netProfit -= bribeAmount;
}
}
if (netProfit < minProfit) {
revert InsufficientProfit(netProfit, minProfit);
}
}
When flash() triggers uniswapV3FlashCallback, line L115 strictly enforces:
if (msg.sender != activeCallbackPool) revert Unauthorized();
Any unsolicited call—even from another legitimate Uniswap V3 pool—is immediately rejected with Unauthorized().
Direct Pair Swaps with Dynamic Fee-on-Transfer Protection
Rather than approving an intermediary router and calling high-level routing methods, the contract interacts directly with low-level pair contracts via IUniswapV2Pair.swap().
Crucially, long-tail tokens frequently implement dynamic transfer taxes or deflationary burns. If a contract naively relies on the theoretical input amount amountIn, the pair receives fewer tokens than expected, causing the subsequent constant-product invariant check (K = x ⋅ y) to revert with UniswapV2: K.
FlashMultiHopArb.sol solves this by measuring actual pool balance deltas (src/FlashMultiHopArb.sol:L155-L188):
function _swapV2(
SwapStep memory step,
uint256 amountIn,
uint256 hopIndex
) internal returns (uint256 amountOut) {
IUniswapV2Pair pair = IUniswapV2Pair(step.pool);
(uint256 r0, uint256 r1, ) = pair.getReserves();
bool zeroForOne = step.tokenIn < step.tokenOut;
(uint256 reserveIn, uint256 reserveOut) = zeroForOne ? (r0, r1) : (r1, r0);
// 1. Direct transfer into the V2 pair
safeTransfer(step.tokenIn, step.pool, amountIn);
// 2. Measure tokens actually received by the pair (eliminates fee-on-transfer discrepancies)
uint256 actualAdded = IERC20(step.tokenIn).balanceOf(step.pool) - reserveIn;
// 3. Compute maximum extractable output using actual received delta
uint256 amountInWithFee = actualAdded * step.feeBps;
amountOut = (amountInWithFee * reserveOut) / (reserveIn * 10000 + amountInWithFee);
if (amountOut == 0) revert ZeroOutputOnHop(hopIndex);
uint256 balBefore = IERC20(step.tokenOut).balanceOf(address(this));
pair.swap(
zeroForOne ? 0 : amountOut,
zeroForOne ? amountOut : 0,
address(this),
new bytes(0)
);
// 4. Return actual tokens delivered to this contract for the next hop
amountOut = IERC20(step.tokenOut).balanceOf(address(this)) - balBefore;
}
Warm Storage Optimization: The Dust Holding Pattern
Under EVM gas rules (EIP-2929 / EIP-2200), modifying a storage slot from 0 to a non-zero value incurs a cold allocation cost of 20,000 gas (SSTORE). Modifying an already non-zero slot costs only 2,900 gas.
If an arbitrageur withdraws 100% of their profits between transactions, every subsequent trade must pay cold-write penalties to re-initialize the contract’s ERC-20 balance slots. Across a 3-hop route (e.g., WETH -> USDC -> TOKEN -> WETH), this wastes up to 51,300 gas per trade.
FlashMultiHopArb.sol implements the Dust Holding Pattern (src/FlashMultiHopArb.sol:L252-L264):
/**
* @notice Withdraw profits while retaining exactly 1 wei balance (Dust Holding).
* Keeps the contract's ERC20 balance storage slot warm (non-zero),
* saving 17,100 gas on subsequent SSTORE allocations per hop.
*/
function withdrawProfitKeepDust(address token) external onlyOwner {
uint256 bal = IERC20(token).balanceOf(address(this));
if (bal > 1) {
safeTransfer(token, owner, bal - 1);
}
}
By leaving 1 wei in the contract, the storage slot remains perpetually warm, granting an enduring structural gas advantage over competing bots on every execution.
3. Mathematical Optimization: Microsecond Closed-Form Solving
Iterative numerical methods (such as binary or ternary search) require multiple sequential evaluations, introducing unacceptable CPU latency into the event-processing loop.
In resident-bot.ts:L202-L244, multi-hop routes are solved using a Closed-Form Equivalent Virtual Pool Folding Algorithm:
public static solveOptimalMultiHop(
hops: PoolHopReserve[]
): { optimalInput: bigint; expectedProfit: bigint } {
if (hops.length < 2) return { optimalInput: 0n, expectedProfit: 0n };
// Step 1: Normalize initial hop into a canonical virtual pool (eIn, eOut)
const firstScale = hops[0].feeBps > 1000n ? 10000 : 1000;
const firstFee = Number(hops[0].feeBps) / firstScale;
let eIn = Number(hops[0].rIn) / firstFee;
let eOut = Number(hops[0].rOut);
if (eIn <= 0 || eOut <= 0) return { optimalInput: 0n, expectedProfit: 0n };
// Step 2: Recursively fold subsequent hops into the virtual pool
for (let i = 1; i < hops.length; i++) {
const rInNext = Number(hops[i].rIn);
const rOutNext = Number(hops[i].rOut);
const scaleNext = hops[i].feeBps > 1000n ? 10000 : 1000;
const fNext = Number(hops[i].feeBps) / scaleNext;
if (rInNext <= 0 || rOutNext <= 0 || fNext <= 0) return { optimalInput: 0n, expectedProfit: 0n };
const denom = rInNext + fNext * eOut;
eIn = (eIn * rInNext) / denom;
eOut = (eOut * rOutNext * fNext) / denom;
}
if (eOut <= eIn) return { optimalInput: 0n, expectedProfit: 0n };
// Step 3: Analytical first-derivative root of the folded virtual pool
const optimalDxFloat = Math.sqrt(eIn * eOut) - eIn;
if (optimalDxFloat <= 0) return { optimalInput: 0n, expectedProfit: 0n };
const dx = BigInt(Math.floor(optimalDxFloat));
if (dx <= 0n) return { optimalInput: 0n, expectedProfit: 0n };
// Step 4: Strict BigInt forward pass verification to eliminate floating-point drift
let currentAmt = dx;
for (const hop of hops) {
const scale = hop.feeBps > 1000n ? 10000n : 1000n;
const denom = hop.rIn * scale + hop.feeBps * currentAmt;
if (denom === 0n) return { optimalInput: 0n, expectedProfit: 0n };
currentAmt = (hop.rOut * hop.feeBps * currentAmt) / denom;
if (currentAmt <= 0n) return { optimalInput: 0n, expectedProfit: 0n };
}
const profit = currentAmt > dx ? currentAmt - dx : 0n;
return { optimalInput: dx, expectedProfit: profit };
}
Analytical Derivation
Any chain of N constant-product pools with reserves (Rin, k, Rout, k) and fee factors γk can be expressed as a single equivalent transformation: where equivalent reserves Ein and Eout are computed iteratively: Setting yields the global closed-form maximizer: This calculation resolves in under 2 microseconds in JavaScript V8, enabling instant evaluation the moment a pool’s Sync event arrives.
4. Execution Engine: Dual Simulation, Fee Protection, and Multi-Client Racing
Detecting a mathematical opportunity is only half the battle. In production, network fee volatility and mempool collisions dictate whether a trade yields profit or burns capital.
Dual Pending Block Simulation (resident-bot.ts:L603-L691)
To prevent broadcasting unprofitable transactions, resident-bot.ts enforces a two-tier simulation protocol against the pending state:
// 1. Initial simulation against pending block to establish raw gross yield
const { result: simulatedProfit } = await client.simulateContract(call);
// 2. Query fee oracles: Base Fee, L1 Data Fee, and Operator Fees (L2 specific)
const [estimatedGas, block, nonce, confirmedNonce] = await Promise.all([
client.estimateContractGas(call),
client.getBlock(),
client.getTransactionCount({ address: this.account.address, blockTag: 'pending' }),
client.getTransactionCount({ address: this.account.address, blockTag: 'latest' }),
]);
// Guard against in-flight transactions using the same wallet
if (nonce !== confirmedNonce) return null;
const gas = (estimatedGas * 120n + 99n) / 100n;
const priorityFee = (simulatedProfit * (this.isTurboMode ? 4500n : 1500n)) / 10000n / gas;
const maxFeePerGas = block.baseFeePerGas * 2n + priorityFee;
// Fetch OP Stack L1 fee bounds from GasPriceOracle (0x4200...000F)
const [l1Fee, operatorFee] = await Promise.all([
client.readContract({ ...oracle, functionName: 'getL1FeeUpperBound', args: [BigInt((data.length - 2) / 2 + 256)] }),
client.readContract({ ...oracle, functionName: 'getOperatorFee', args: [gas] }),
]);
// 3. Compute all-inclusive fee budget and embed directly into contract minProfit
const feeBudget = gas * maxFeePerGas + ((l1Fee + operatorFee) * 120n) / 100n;
const minProfit = CONFIG.MIN_PROFIT_WEI + feeBudget;
if (simulatedProfit <= minProfit) return null;
// 4. Secondary simulation with protected minProfit parameter
const protectedArgs = [args[0], args[1], args[2], amountIn, minProfit, 0n, steps] as const;
const { result: verifiedProfit } = await client.simulateContract({ ...call, args: protectedArgs });
By injecting minProfit = MIN_PROFIT_WEI + feeBudget directly into the smart contract’s execution arguments, the smart contract itself enforces that transaction execution aborts and rolls back if the realized net return fails to cover both network gas and L1 data fees.
Multi-Client Broadcast Racing (Promise.any)
To minimize routing latency to the block sequencer, the engine signs the transaction exactly once and broadcasts the identical payload across multiple RPC endpoints simultaneously (test-core.test.ts:L115-L145):
const serializedTransaction = await this.account.signTransaction({
chainId: base.id,
type: 'eip1559',
to: EXECUTOR_CONFIG.ARB_CONTRACT,
data,
nonce,
gas,
maxFeePerGas,
maxPriorityFeePerGas: priorityFee,
});
const hash = keccak256(serializedTransaction);
// Race broadcast across redundant sequencers and RPC nodes
await Promise.any(this.broadcastClients.map((wc) => wc.sendRawTransaction({ serializedTransaction })));
If one node experiences a TCP stall or rate limit, the competing node delivers the transaction to the sequencer unhindered.
5. Architectural Comparison: Execution Paradigms
| Technical Dimension | Naive Router Bot | Generic Searcher Template | FlashMultiHopArb Architecture |
|---|---|---|---|
| Routing Layer | UniswapV2Router / V3 SwapRouter | Custom multi-call proxy | Direct pool storage calls (pair.swap) |
| Storage Slot Cost | Cold writes (20,000 gas) |
Cold writes (20,000 gas) |
Dust Holding (1 wei warm storage, 2,900 gas) |
| Solving Algorithm | Numerical binary search | Iterative ternary search | Analytical closed-form virtual pool folding |
| Solving Latency | 15 – 50 ms | 5 – 15 ms | < 2 microseconds (Analytical root) |
| Fee Awareness | L2 Base Fee only | Base Fee + Priority Fee | Dynamic L1 Blob + Operator + Gas padding |
| Fee Protection | Off-chain check only | Off-chain check only | Contract-enforced minProfit >= feeBudget revert |
| Broadcast Strategy | Single HTTP POST | Single WebSocket broadcast | Single-signed Promise.any multi-node race |
| Concurrency Guard | None (nonce collisions) | In-memory counter | executionInFlight + Pending Nonce Fencing |
6. Concrete Verification: Offline and Mainnet Fork Test Results
The correctness of this architecture is verified through comprehensive offline test suites and mainnet fork benchmarks.
Offline Suite Execution (test-core.test.ts)
The offline unit suite verifies core engine invariants without network dependencies:
- Identical serialized byte broadcasting across multiple clients.
- Single-opportunity execution during pending receipts.
- Automatic execution pausing during ambiguous broadcast timeouts.
- Strict nonce serialization.
# Execution run: node --test test-core.test.ts test-scanner.test.ts
1..87
# tests 87
# suites 0
# pass 87
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3231.566375
Mainnet Fork Verification (observations/natural-fork-benchmark-53500814.json)
The contract and mathematical solver were benchmarked against historical Base mainnet block #53500814 across live WBTC/WETH/USDC pools:
- Model Output:
930,418,480,929 wei - Simulated Contract Profit:
930,418,480,929 wei - Actual On-Chain Executed Profit:
930,418,480,929 wei - Model Deviation:
0 wei(Exact match between off-chain math and on-chain balance deltas)
7. Integration & Commercial Next Steps
Building competitive decentralized exchange and trading infrastructure requires deep co-design between smart contract storage economics and low-latency execution clients.
The components detailed here—direct pool execution, warm storage maintenance, closed-form liquidity modeling, and fail-safe broadcasting—form the architectural foundation of our White-Label DEX Launch Kit and automated liquidity management systems.
Recommended Implementation Roadmap:
- Audit Gas Overhead in Existing Routers: Profile your execution transactions using Foundry’s
forge test --gas-reportto identify avoidable storage allocations and router proxy jumps. - Review Liquidity Architecture Guidelines: Explore our companion guides on Hosted vs Custom Stablecoin Checkout and SaaS foundations: identity, permissions and billing.
- Explore the Working Infrastructure: Review the deployment specifications and verified workflows on the White-Label DEX Product Page or the 中文版 DEX 方案主页.
- Schedule a Technical Architecture Walkthrough: If your team is deploying custom AMM instances, liquidity bootstrapping pools, or proprietary on-chain execution modules, book an architecture consultation or request a custom development proposal.