Bot Titles
PLS-MHOP-ARB-V1Performance So Far
PLS-MHOP-ARB-V1Recent Fire Attempts
PLS-MHOP-ARB-V1Token Reverters (24h)
PLS-MHOP-ARB-V1Biggest Block
PLS-MHOP-ARB-V1The bot was wired to the wrong V3 contracts for its entire existence — the V3 path never produced a settled win because every V3 sim reverted at the dispatch layer, not the strategy layer. Discovery cascade on 2026-05-27 (full trace in plan §4.AA, /home/green/.claude/plans/squishy-zooming-lemon.md):
(1) Bottleneck #1 — wrong callback selector. debug_traceCall on rpc.pulsechain.box revealed the V3 pool calls our Quoter with selector 0x23a69e75, not Uniswap's 0xfa461e33. Lookup confirmed: pancakeV3SwapCallback — 9mm V3 is a PancakeSwap V3 fork, NOT a Uniswap V3 fork. Our V3 contracts had uniswapV3SwapCallback. Renamed.
(2) Bottleneck #2 — wrong SwapRouter address. Canonical Uniswap V3 SwapRouter at 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45 exists on PulseChain but reverts every exactInputSingle call with empty data. Cause: the deployed bytecode has the canonical Uniswap V3 factory 0x1f98431c… hardcoded; that factory doesn't exist on PulseChain, so the router can't find any pool. The actual 9mm V3 SwapRouter is at 0x7bE8fbe502191bBBCb38b02f2d4fA0D628301bEA (from github.com/9mm-exchange/deployments/pulsechain/v3.json). Updated scanner + runner.
(3) Bottleneck #3 — wrong exactInputSingle ABI. Even with the correct router, calls reverted with empty data. Bytecode inspection of 0x7bE8fbe5… showed it accepts selector 0x414bf389 (exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160)) — 8-tuple with deadline), not 0x04e45aaf (Uniswap V3 SR02-style 7-tuple). Updated the ABI to include deadline: ethers.constants.MaxUint256.
(4) Bottleneck #4 — scanner's V3 math is broken. With dispatch fixed, the trace shows the V3 swap CHAIN executes successfully but the final WPLS-back-to-vault transfer reverts because the closed-loop output is less than the flash-loan principal. **Reason: the scanner's calcV3Out_* is a within-tick approximation that over-estimates V3 outputs by 100-1000× for trades that cross tick boundaries. Every signal it claims is +500,000bps profitable is actually a -50,000 to -500,000 PLS loss. Added Quoter-based profit verification at the end of buildMultiHopCalldata — if the chained accurate output < firstAmount + MIN_PROFIT_PLS, the route is rejected before sim. Now every single phantom is correctly caught: quoter_unprofitable(-48539<200), quoter_unprofitable(-500000<200), etc.
Result**: the pipeline is now functionally correct. The bot CAN trade real V3 arbs end-to-end (verified via debug_traceCall — every step of the V3 swap chain works), and it correctly refuses to trade phantoms (every candidate in current market is unprofitable per Quoter). 0 emissions today is data, not a defect — the market simply doesn't have closed-loop arb opportunities at sizes 1K-500K WPLS right now. The runner is alive in tmux mhop-runner, polling the signal file, waiting for a real opportunity.
Fantasy
PLS-MHOP-ARB-V1If the scanner used the Uniswap V3 Quoter as the *primary* V3 output estimator instead of the within-tick approximation, every candidate it generates would already be accurate — no phantom signals, no wasted sim attempts, and BF/DFS scoring would correctly rank profitable routes vs noise. Closest real-world thing: the 9mm V3 QuoterV2 at 0x500260dD7C27eCE20b89ea0808d05a13CF867279 (or our own custom Quoter at 0xee75F8abABec477510d26ab1DCe666fB8eb9c9Bb) — both are deployed, both work, both return real outputs. The blocker today is *architectural*: BF and DFS are synchronous in-memory graph walks; calling Quoter is an async RPC. A pre-fetch pass that batches all V3 hop quotes via Multicall3.aggregate3 at the start of each scan cycle would close that gap without breaking the sync hot path. Days of refactor; converts the bot from "broken scanner caught by Quoter at sim time" to "scanner sees the truth from the first candidate". The downstream effect: scan-cycle time drops from 30-160s to ~5s, and the bot wakes up faster than ~80% of the public-mempool competition.
Suggestions for Improvement
PLS-MHOP-ARB-V1- 01CRITICALLive-watch the first emission. V3 dispatch fully fixed 2026-05-27 — scanner is now correctly identifying that current market has no real V3 arb opportunities at trade sizes 1K-500K WPLS (Quoter validates every candidate, all real outputs are negative). The first emission validates the entire pipeline end-to-end. Tail logs/pulse-multihop-arb-signals.ndjson + the runner log and inspect the first signal manually before letting it execute.
- 02HIGHReplace scanner's within-tick V3 math with Quoter pre-fetch (B-NEW7). Current calcV3Out_zeroForOne/oneForZero over-estimates V3 outputs by 100-1000× for trades that cross ticks — every emitted candidate looks +50,000bps profitable to the scanner but is -50,000 PLS in reality. Quoter chain in buildMultiHopCalldata correctly catches this before sim, but the wasted candidate-generation work makes scans take 30-160s instead of ~10s. Cost: must refactor BF/DFS hot path to be async. Defer until emissions are flowing.
Conclusion
PLS-MHOP-ARB-V1Status 2026-05-27 (verified): RESURRECTED · V3 DISPATCH FULLY FIXED · WAITING FOR MARKET.
Verdict: keeper. Bot had been catalogued as "GAS DRAIN — stopped 2026-04-20" because every V3 dispatch attempt was reverting. A full debugging session today identified four cascading bugs (callback selector, SwapRouter address, exactInputSingle ABI, scanner V3 math) and resolved all of them. End-to-end V3 swap chain now works in callStatic; runner is alive in tmux mhop-runner polling for signals.
The remaining question is purely market, not implementation: at trade sizes 1K-500K WPLS the current PulseChain V3 pool population doesn't produce closed-loop arbs that close profitably after gas + flash-loan repayment. The scanner correctly identifies this — every candidate it generates is Quoter-checked and rejected with negative real profit (quoter_unprofitable(-48539<200) and similar). 0 emissions today is correct behavior, not a defect.
Place in fleet: Other / standby. Distinct from PLS-AGG-ARB-V5 (cross-aggregator, uses 9X/Piteas/Switch routers) and PLS-TRI-ARB-V1 (mempool-triggered 3-leg) — this bot scans raw pool reserves for closed loops and acts on its own atomic contracts. Different signal surface, different execution path. Cheap to keep alive: scanner runs at <50% CPU on one core, runner polls a file. Gas burn while waiting is zero (no signals → no submits).
Promotion criteria from standby to active fleet slot: first 5 settled trades demonstrate the V3 path lands on-chain successfully (settles green) AND the scanner-to-runner pipeline produces at least 2 emissions per day sustained over a week. Until then, keep observing.
Active Tests & Monitors
PLS-MHOP-ARB-V1Compromises
PLS-MHOP-ARB-V1| Decision | Aggressive ← | Current Position | → Conservative | Notes |
|---|---|---|---|---|
| V3 candidate filtering (Quoter at sim vs scanner) | Quoter at sim time · scanner uses fast within-tick math | Quoter as scanner primary · accurate from start | Today the scanner uses a fast (sync, in-memory) within-tick V3 approximation; Quoter validates downstream in buildMultiHopCalldata. Accepts wasted candidate generation (every V3 candidate gets Quoter-rejected) in exchange for keeping BF/DFS sync. Right call for now; refactor to async Quoter pre-fetch when emissions are flowing and scan latency matters. | |
| Trade size ladder | Single size · simple, predictable gas | 9 sizes (500K..1K) · maximize discovery | TRADE_AMOUNTS spans 500K, 200K, 100K, 50K, 20K, 10K, 5K, 2K, 1K. Each candidate is evaluated at every size and the best is picked. 9× scan work per candidate but covers the size-where-arb-exists question; without this, we'd miss small arbs in deep pools and large arbs in thin pools. | |
| Min profit floor 200 PLS (~$0.006) | Tight floor · only fat edges | Loose floor · capture marginal arbs · more tx_fail | Dropped from 400 to 200 PLS on 2026-05-27 per the original plan. Aggressive — implies we're willing to risk gas burn on edges that may not survive submission drift. Acceptable because the runner's circuit-breaker would auto-halt if tx_fail rate spikes; conservative would be 500-1000 PLS once emissions are flowing. | |
| Honeypot heuristic (in-memory phantomTokens) | Permanent token blacklist · safer | Per-process · resets on restart | phantomTokens accumulator is in-memory; rebuilds from sim-fail history each session. A token blacklisted in one session gets a fresh chance after the next restart. Accepts re-discovery cost on the rare chance pool conditions changed; alternative (persistent blacklist) would hold ghosts forever. | |
| Flash-loan vs self-funded execution | Self-funded multiSwap · contract holds capital | Flash-loan multiHopArb · borrow from Phux 0% fee | V2-only routes use PulseAtomicArb V1 (self-funded with 3M WPLS). V3-touching routes use PulseMultiHopArb flash-loan via Phux. Self-funded path is cheaper gas but caps trade size at contract balance; flash-loan is more flexible but requires successful repayment in the same tx (which is exactly what unprofitable closed loops fail at). Mixed path is correct — match execution mode to route type. |
Parameters
PLS-MHOP-ARB-V1| Parameter | Value | Reasoning | Potential Adjustment(s) |
|---|---|---|---|
| MIN_PROFIT_PLS | 200 WPLS | Floor below which signals are dropped before sim. Lowered 400 → 200 on 2026-05-27 per the original plan. Aggressive: implies we're willing to risk gas on edges that may not survive submission drift. The Quoter-based profit check in buildMultiHopCalldata enforces this floor against ACCURATE V3 amounts, not the scanner's broken within-tick estimate. | Raise back to 400-500 if the first 5 settled trades show >30% tx_fail rate. Drop to 100 only after the V3 math is fixed (B-NEW7) so the scanner doesn't generate phantoms that the Quoter then rejects (wasted work). |
| MIN_PROFIT_BPS | 15 bps | Bps-side profit threshold (independent of MIN_PROFIT_PLS). 15 bps ≈ 0.15% which clears 3× gas at typical PulseChain prices. Scanner rejects candidates with theoretical bps below this. | Lower to 10 bps if the surviving candidate set is too small to produce any signals. Raise to 25 bps for stricter pre-sim filtering once emissions are flowing. |
| TRADE_AMOUNTS | [500K, 200K, 100K, 50K, 20K, 10K, 5K, 2K, 1K] | Nine sizes; each candidate evaluated at every size, best picked. Covers deep liquid pools (500K) down to thin pools (1K). The 9× scan work is acceptable because the scanner is the cheap stage; sim cost only kicks in for surviving candidates. | Trim to [200K, 50K, 10K] once we know which sizes actually win. Re-add granularity if size-specific drift profile emerges from the win history. |
| MAX_DEPTH (DFS) | 4 | Reduced from 6 because 5-6 hop gas cost exceeds profit on PulseChain (per scanner line 959 comment). Each extra hop adds ~200-400K gas. 4 covers the practical universe of profitable closed loops on this chain. | Raise to 5 if we observe missed 5-hop opportunities in real arb data. Stay at 4 until proven otherwise. |
| MAX_SIMS_PER_CYCLE | 150 | High ceiling per cycle. Each candidate that survives filters costs one Quoter call (V3) or one local computation (V2) + potentially one callStatic. Repeat-failures are auto-skipped via simFailCache (SIM_FAIL_SKIP_THRESHOLD=3, retry after 100 cycles). | Raise if cycle is sim-budget-starved (currently no evidence of this). Lower only if RPC pressure forces it. |
| MIN_RESERVE_WPLS | 500,000 WPLS (~$3.50) | Illiquid-pair filter applied at graph-build time. Pairs with less than 500K WPLS-equivalent reserves are dropped before BF. Today removes ~3,516 of 8,171 pairs (43%); keeps the graph in liquid territory only. | Lower to 200K only if we observe missed micro-pool arbs that would clear gas. Otherwise leave alone — illiquid pools produce phantom signals more than real ones. |
| SIM_GAS_LIMIT (scanner) | 5,000,000 | callStatic gas budget per sim attempt. 5M covers up to ~7-hop V3 routes (700K base + 600K/hop). Matches the runner's GAS_LIMIT. | Raise to 8M only for routes that exceed 7 hops (currently capped at 10 calls = 5 hops by contract). Lower won't help — sim cost is per-call, not budget-bound. |
| V3_SWAP_ROUTER | 0x7bE8fbe502191bBBCb38b02f2d4fA0D628301bEA | 9mm V3 SwapRouter on PulseChain (from github.com/9mm-exchange/deployments). Selector for exactInputSingle is 0x414bf389 (SR01-style 8-tuple WITH deadline). Updated 2026-05-27 from canonical Uniswap address (0x68b3465…) which had wrong factory hardcoded. | No tuning needed. If 9mm ever upgrades to a new SwapRouter, update this constant AND verify the selector is still 0x414bf389. |
| V3_QUOTER | 0xee75F8abABec477510d26ab1DCe666fB8eb9c9Bb | Custom V3Quoter deployed 2026-05-27 (contracts/src/V3Quoter.sol). Uses pancakeV3SwapCallback (selector 0x23a69e75 — 9mm V3 is a PancakeSwap V3 fork). Round-trip verified at exactly 2× fee tier slippage. Alternative: official 9mm QuoterV2 at 0x500260dD7C27eCE20b89ea0808d05a13CF867279. | Switch to the official QuoterV2 if our custom Quoter ever misbehaves. Both work; ours is simpler (single function, no factory dependency). |
| MULTIHOP_ARB_ADDRESS | 0xBa102faCf64A8B0ad60f25feEE854d1E36f7902e | PulseMultiHopArb contract. Flash-loans WPLS from Phux Vault (0% fee), executes arbitrary targets[]/calldatas[] (so V3 SwapRouter calls can be inlined), verifies WPLS profit ≥ minProfit before returning. Routes touching any 9mm V3 hop dispatch here. | No tuning needed. Contract handles its own min-profit gate via the minProfit param passed in executeArb. |
| ATOMIC_ARB_ADDRESS | 0x03E2497d4bACB78AC68aCB9F2e1f790711756d66 | PulseAtomicArb V1, deployed 2026-04-10. V2-only multiSwap (10-call limit). 3M WPLS funded. 13 lifetime wins from the V2-only path — the proven champion contract. Pure-V2 routes dispatch here. | No tuning. PulseAtomicArbV2.sol exists in source (hardened, per-leg slippage, relative-bps minProfit) but NOT deployed; per Chef's decision 2026-05-26, defer V2 contract deploy until V1 actually fails in a way V2 would prevent. |
| MAX_TRADES_PER_HOUR | 10 | Runner-side rate cap. Lets the runner fire up to 10/hr even if the scanner emits more. Originally part of the Phase D ramp (D-1=3, D-2=6, D-3=10, D-4=10); currently at the D-3/D-4 max because emissions are 0 anyway. | Drop to 3 (Phase D-1) if the first 5 settled trades show concerning behavior. Raise above 10 only after 100+ clean fires. |
| SIGNAL_MAX_AGE_MS | 120000 (2 min) | Discards signals older than 2 min before sim. Wide buffer; scanner emits roughly every 30-60s in steady state. Originally was tightened to 30s in the plan but kept at 120s for safety while the V3 path is unproven. | Tighten to 30-45s once emissions are flowing and we know the scanner-to-runner round-trip latency. |
| HEX/eHEX blacklist | hardcoded | HEX ↔ eHEX trades hardcoded blacklist after a 2026-04-07 incident lost 18.5K PLS to a fee-on-transfer surprise. Cheap insurance. Removes a known recurring loss pattern. | No removal until/unless we have a proven safe-path for these specific tokens (e.g., a probe step that verifies the receive amount matches the swap input). |
Revisions
PLS-MHOP-ARB-V1| Revision | Summary |
|---|---|
| V1.0 (launch) | 2026-04-10 — PulseAtomicArb contract deployed (0x03E2…6d66), V2-only multiSwap (10-call limit), self-funded 3M WPLS. Scanner finds closed-loop arbs across PulseX V1/V2 + 9inch + 9mm V2; runner sims and submits. |
| V1.1 | PulseMultiHopArb deployed (0xBa10…902e) for V3-touching routes — flash-loans WPLS from Phux Vault (0% fee), executes arbitrary targets[]/calldatas[] so V3 SwapRouter exactInputSingle can be inlined. Two-contract dispatch: V2-only → AtomicArb, V3-touching → MultiHopArb. |
| V1.2 | 13 settled V2-only wins through 2026-04-12. Zero V3-touching settled wins despite the V3 contract existing — every V3 sim reverted with opaque "missing revert data" errors (root cause hidden until 2026-05-27). |
| STOPPED 2026-04-20 | Catalogued as "GAS DRAIN" and removed from active fleet: 45+ reverts/cycle, 3 days of zero wins, every fire dying at InsufficientProfit. The opaque revert pattern made diagnosis impossible without trace access. |
| V2.0 RESURRECTED 2026-05-27 (CURRENT) | Full V3 dispatch rebuild after a deep debugging session unlocked debug_traceCall on rpc.pulsechain.box. Discovery cascade: (a) pool calls pancakeV3SwapCallback not uniswapV3SwapCallback — 9mm V3 is a PancakeSwap fork; (b) canonical Uniswap V3 SwapRouter (0x68b3465…) doesn't work — wrong factory hardcoded; (c) 9mm's actual SwapRouter is 0x7bE8fbe5…0bEA from github.com/9mm-exchange/deployments; (d) it uses SR01-style ABI with deadline field (selector 0x414bf389); (e) the scanner's within-tick V3 math over-estimates by 100-1000× so a Quoter pre-fetch + chain in buildMultiHopCalldata is required for accurate amounts. |
| V2.0 ships current | In one session: indirect WPLS-conversion fallback (B-NEW1) · MIN_PROFIT_PLS 400→200 (B-5) · sanity gate 100× output cap (B-NEW5) · V3 sim dispatch port + per-hop chaining (B-NEW4) · custom V3Quoter deployed at 0xee75F8abABec477510d26ab1DCe666fB8eb9c9Bb (B-NEW6) · pancakeV3SwapCallback fix · 9mm SwapRouter address fix · exactInputSingle ABI fix with deadline · Quoter-based profit verification (rejects routes whose accurate output is unprofitable before sim). |
Remaining work from original plan
PLS-MHOP-ARB-V1Temporary section — tracks the deferred items from the PLS→PLS self-arb plan (squishy-zooming-lemon). Each row lists why we deliberately did not ship it yet. To be removed when the work is complete.
| # | Item | Reasons to NOT do it |
|---|---|---|
| 1 | B-NEW7: replace scanner’s within-tick V3 math with Quoter pre-fetch |
|
| 2 | B-2: WS pulse on newHeads |
|
| 3 | B-3: honeypot recalibration |
|
| 4 | V3 cache schema fix |
|
| 5 | Phase D ramp |
|
| 6 | Lower MIN_PROFIT_PLS 30 → 15 (paired scanner + runner) |
|
| 7 | Quoter pre-fetch via Multicall3 (re-evaluation of B-NEW7) |
|
Synthesis: all 7 are “do later” items, not “skip forever” items. The current bottleneck is the underlying market, not bot code or thresholds — per 2026-05-27 investigation, scanner found 6,100 candidates in 50 cycles but only 1 route ever crossed +16 PLS profit (still below the 30 PLS gas-margin floor). Until PulseChain V3 multi-hop arb produces ≥30 PLS opportunities, optimizing latency, math accuracy, ramp infrastructure, or threshold tuning is rearranging deck furniture on a stable ship.