A couple of weeks ago I set out to build an arbitrage bot for Solana. Not a big one. No colocated servers, no validator relationships, no eight figure market making desk behind it. Just me, a laptop, a public RPC endpoint, and an idea that gets reinvented by every new trader who discovers DeFi: the same token is sometimes priced differently on two exchanges at the same moment, and if you're careful about it, you can buy low on one and sell high on the other with no directional exposure at all.

I want to walk through the whole thing in detail, not just the conclusion, because the interesting part isn't "it didn't work." Plenty of ideas don't work. The interesting part is why, mechanically, it didn't work, and that reason turned out to be the same reason across five completely different strategies, three different automated market maker designs, and three separate exchanges. Once you see the pattern it's obvious in hindsight. It wasn't obvious going in.

What atomic arbitrage actually means, and why Solana specifically

On most chains, arbitrage between two exchanges is a two step trade with real risk in between. You buy on exchange A, wait for that transaction to confirm, then sell on exchange B, and in the gap between those two steps the price can move against you, gas can spike, or someone can beat you to the second leg and leave you holding a bag you didn't want.

Solana's block building setup, specifically Jito's bundle auction, removes that gap entirely. You can construct both legs of a trade, the buy and the sell, as a single transaction (or a tightly ordered bundle of transactions) and submit the whole thing to a block builder along with a tip. Critically, you can attach an on chain assertion that checks whether the round trip was actually profitable, and if it wasn't, the entire bundle simply fails to land. Nothing executes. You paid no fee, because you never actually sent the transaction into a landed block. The downside of a wrong guess isn't a loss, it's just wasted compute time on your own machine checking something that turned out not to be there.

That single property is what made this feel worth a serious attempt rather than a weekend toy. Structurally, you cannot lose capital doing this. You can only fail to find capital to make. Which meant the actual project was never "build a trading bot." It was "find out, as cheaply and honestly as possible, whether there is any real edge here at all, and if there isn't, prove that and stop." I wrote that framing down before touching a line of trading logic, because I've watched enough people (myself included, on other projects) build the exciting part first, then quietly lower their own bar for what counts as "working" once they're three weeks in and emotionally invested. I wanted the version of this project where quitting early, with a clean writeup, counted as success.

The rule I set before writing a line of code

Before building anything, I wrote down exact thresholds for what would count as a real, tradeable edge for each candidate strategy, and I froze them. Estimated net dollars per day above some floor, minimum number of opportunities per day so a single lucky outlier couldn't carry the whole result, a cap on how much of the total value could come from the top five best observations. I also wrote down what a negative control should look like: a strategy that everyone already knows is dead, run through the exact same measurement pipeline, which should come back reading zero.

This matters more than it sounds like it should. The single easiest way to fool yourself in a project like this is to look at disappointing numbers, notice you could nudge a threshold or a cost assumption slightly, and convince yourself that's a reasonable correction rather than moving the goalposts. Pre registering the bar before collecting data is the standard defense against that in any serious empirical field, and there's no reason trading research should get to skip it just because it's a personal project instead of a published study.

Before trusting a zero, prove your ruler works

Here's the trap specific to this kind of measurement: "no opportunities found" and "my code is broken" look identical from the outside. Both produce a wall of zeros. Before I let myself believe any negative result, I built what amounts to a fire drill for the measurement code itself. Take two real, live pool states fetched back to back, artificially shift one venue's price by a large, unambiguous two percent, and check whether the scanner finds the resulting arbitrage, in the correct direction, at a sane magnitude.

It did, in both directions, landing at roughly one hundred ninety eight basis points and one hundred forty six basis points respectively (a basis point is a hundredth of a percent, so those are meaningfully large, unmissable numbers), with the wrong direction correctly showing as a loss both times. Good. But building that check caught two real bugs on the way, on top of a third one I'd already found earlier that's worth mentioning because of how easy it is to miss.

The earlier bug: one of the two exchanges internally orders a trading pair's tokens differently than the pair's canonical listing does, so a WIF against wrapped SOL pair might be stored as (SOL, WIF) on one venue's account structure and (WIF, SOL) on the other. USDC uses six decimal places; most SPL tokens use nine. If you apply the decimal conversion using the wrong side's ordering, a perfectly reasonable looking price calculation comes out off by roughly nine orders of magnitude. My first version of this did exactly that, and it produced a spread reading in the billions of basis points on one pair, an obviously broken number, but the kind of obviously broken number that's easy to explain away as "must be a weird token" if you're not looking closely. It wasn't a weird token. It was a sign and ordering bug in my own code, three lines deep in a function that looked correct at a glance.

The two bugs the positive control itself caught: first, my draft "no dislocation" baseline reused two pool snapshots captured about fifteen minutes apart during earlier decoder testing. SOL genuinely moved a little over one percent against USD in that window, which is nothing in absolute terms, except it showed up as a sizeable false arbitrage reading in a control that was specifically supposed to show zero. I'd accidentally recreated, in miniature, the exact failure mode the whole project was designed to guard against: comparing two venues' prices from different moments in time and mistaking ordinary drift for a real dislocation. Fixed by capturing both venues within under a second of each other. Second, when I hand constructed a synthetic perturbed pool state for testing, I only changed the stored price field and forgot that a concentrated liquidity pool's "current price bucket" pointer has to stay mathematically consistent with that price. Real on chain accounts are always internally consistent because the program enforces it on every write. A hand built fake one isn't, unless you enforce that yourself. It crashed with a division by zero the first time I ran it.

None of these three were bugs in the trading logic itself. They were bugs in my test of the trading logic, which is a distinction that matters enormously here, because without catching them I would have trusted a zero that could have meant either "no edge exists" or "my harness is blind to one that does," and those two conclusions call for completely different next steps.

Round zero: the test that is supposed to fail

With the instrument validated, the first live run was a control group on purpose: SOL against USDC, WIF against SOL, SOL against a meme coin called Fartcoin, the pairs everyone in this space already knows are hyper contested. These are the pairs where professional searchers run colocated bare metal machines with direct shred streaming from validators, competing on tip size down to the microsecond. If my scanner had found a juicy spread sitting there unclaimed, that wouldn't have meant I'd found an edge. It would have meant something was wrong with the scanner.

Fifteen hundred and twenty observations, zero net positive, at every trade size I tested from fifty dollars up to five thousand. Gross price gaps existed, in the range of seven to thirteen basis points, nothing dramatic, but once you account for both venues' swap fees and the price impact of actually executing the trade, every single one came out a loss. The best case across the entire run, on SOL against USDC, still lost about six basis points. This is exactly what should happen on the most competitive pairs in the ecosystem, and it did, which told me the harness was measuring the real thing rather than a mirage.

Round one: the real thesis, and the first genuine disappointment

The entire premise of doing this on Solana rather than, say, Ethereum, is the long tail: thousands of small, newly listed, thinly traded pairs that are too small for professional market makers to bother watching individually. Big, well capitalized operations don't chase pennies. A small, careful operator theoretically can, if the coverage and correctness are good enough to make up for the lack of speed.

I built a discovery pipeline that pulled pool listings from Raydium and Orca, matched pairs that existed on both, and filtered down to a genuine long tail band: pools with somewhere between two thousand and three hundred thousand dollars of liquidity on their thinner side, well below what counts as a major pair. Building this turned up a real bug worth a quick technical aside. Orca's public pool listing API returns its pagination cursor in a JSON field literally called "cursor," but the request parameter it actually reads to advance to the next page is called "after." Passing the field named "cursor" back, which is the obvious, natural souning choice, silently does nothing at all and re serves you page one forever. I only caught it because fifteen consecutive "pages" of results came back with an identical first entry. Left unfixed, that bug would have quietly capped every future discovery run at around a hundred Orca pools no matter how deep I asked it to search. It's the kind of bug that never crashes anything. It just lies to you slowly, and you don't find out until you go looking for why your sample looks smaller than it should.

With that fixed, discovery found thirty eight genuinely long tail pairs, mostly meme coins and small cap tokens with names like GOAT, POPCAT, MOODENG, FWOG, and ZEREBRO, with median liquidity in the low tens of thousands of dollars on their thinner side. I scanned all thirty eight roughly every fifteen seconds for about twenty minutes, computing the real, executable round trip profitability of buying on one venue and selling on the other at four different trade sizes, using each venue's actual swap curve rather than a naive midpoint price estimate.

Fifty six hundred and twenty six observations. Zero net positive.

But this run taught me something more useful than a plain "no." The gross price gaps out here really were much wider than on the majors, up to a mean of over two hundred basis points on some pairs versus single digits for SOL against USDC. That part of the original thesis was correct: prices genuinely do drift further apart on thin, under monitored pairs. The problem is what happens the moment you try to act on it. The pairs with the widest gross spreads were, almost without exception, exactly the pairs where price impact was worst, and price impact won every single time, at every size from fifty dollars to five thousand, across all thirty eight pairs.

Here's the mechanical reason why, and it's worth actually understanding rather than just accepting. A standard constant product pool, the kind Raydium runs, prices trades along a curve where the product of the two reserves stays fixed. The deeper those reserves are relative to your trade size, the flatter that curve looks near the current price, and the closer your execution price stays to the quoted price. On a thin pool, the curve is steep, so even a modest trade walks you a meaningful distance along it, and your average execution price ends up well away from where you started. Concentrated liquidity pools like Orca's Whirlpools make this even sharper. Instead of spreading liquidity smoothly across every possible price the way a classic constant product pool does, liquidity providers choose specific price ranges to concentrate their capital in, which makes their capital far more efficient while price stays inside that range, but means depth can fall off a cliff the moment price exits it or if few providers bothered to cover the range you're trading through. A pool can report a healthy total value locked number while having almost none of that value anywhere near the price you'd actually trade at.

The most vivid single example of this was a pair called WSOL against a token called pippin, where a fifty dollar trade lost close to eleven percent of its value to price impact alone. I didn't just trust that number because it looked extreme. I went and checked the pool's raw on chain liquidity state directly, and it's real: there were only one to three actual funded price ranges anywhere near the current price, so a fifty dollar trade walked straight across two of them. There was nothing wrong with the math. The pool genuinely is that thin.

There's a second finding buried in this run that's worth flagging for anyone else evaluating pools by their headline numbers: reported total value locked is not a reliable proxy for how much liquidity actually sits near the current tradeable price on a concentrated liquidity venue. Several pools reporting five to twenty thousand dollars of total value locked had almost none of it positioned where trades actually happen, so even small trades blew straight through multiple price ranges. If you're sizing a strategy, or honestly even sizing a simple swap, off a total value locked figure without checking where that liquidity is actually concentrated, you're sizing it wrong.

I also checked the more romantic version of this story: were these real dislocations that existed briefly and decayed away before a bot slower than the fastest searchers could react? They weren't. Median price drift from the moment of detection out through several subsequent blocks came out to exactly zero across the thirty eight pairs. These weren't opportunities I was too slow to catch. They were never positive to begin with, at any point I measured them.

Round two: rebuilding a decoder from a dead API, for the same answer

Raydium and Orca are the two most obvious venues, but Meteora runs a meaningfully different pool design, a discrete liquidity bin model rather than a continuous curve, that the original research thesis specifically flagged as a richer potential source of dislocations, precisely because it's less served by the standard tooling most people default to.

There was one problem: Meteora's public API returned a not found error on every endpoint I tried. So testing this meant decoding their on chain account layout from first principles, using their program's own published interface definition rather than trusting any third party library, and then proving the decoder was correct before trusting a single number that came out of it. I checked it two separate ways. First, structurally: the byte sizes I computed field by field for their pool and bin array accounts matched the real on chain account sizes exactly, down to the byte, and a bin array's own stored index matched what I'd used to derive its address in the first place, which only happens if the layout is genuinely right rather than accidentally close. Second, and more convincingly: I ran the decoder's swap math against a frozen real pool state and compared the result to Jupiter's live simulated quote for the identical trade. They matched to within six thousandths of a basis point, a difference of forty three raw token units out of roughly seventy four million.

One honest gap worth flagging rather than quietly smoothing over: Meteora also charges a variable fee that scales with recent price volatility, layered on top of a static base fee. I could confirm from their published interface that this fee exists and is genuinely nonzero on real pools, but I couldn't confidently reconstruct the exact formula from memory alone, so I didn't implement it. The decoder models the static base fee only, and I documented that as a known approximation rather than a silent gap. It didn't end up changing the result, since the venue was already deeply unprofitable on the base fee alone, but it's a real limitation, not an invisible one, and I'd want it fixed before trusting this decoder for anything beyond a directional read.

With the decoder validated, I found fifteen pairs that existed on both Meteora and Raydium, using Jupiter's own quote routing as a discovery mechanism since Meteora's API was a dead end, and ran the same round trip scan.

Twenty nine hundred and seventeen observations. Zero net positive. The average round trip actually lost more here than on the Raydium against Orca run, roughly twelve percent of position value on average. Same underlying story: real gross price gaps, up to a mean of nearly one hundred eighty basis points on some pairs, completely swallowed by price impact on liquidity that sits in tight bins clustered right around the current price.

Round three: the one that almost worked

This is the part of the project that genuinely got my hopes up, and it's the part where the final answer ends up more interesting than a plain yes or no.

The thesis here is different in kind from the first two. Instead of a standing spread between two venues, it's about the first handful of trades into a pool that was created minutes ago. A brand new pool, freshly seeded with liquidity, has no real price discovery yet. The first trades against it can push the price around dramatically, and if you can catch that early enough, there's supposedly real money in the overreaction and the reversion that follows it.

Finding a genuinely new pool turns out to be much harder than it sounds. Every discovery method available to me ranks pools by trading volume or total liquidity, which structurally excludes anything that's only minutes old, since it hasn't accumulated enough activity yet to show up on any ranked list. Out of roughly four hundred thirty established looking Raydium pools I checked by walking their complete transaction history back to genesis, exactly one turned out to be genuinely new: about five hours old at the time I found it, with its entire lifetime fitting inside a couple thousand transactions.

I replayed that pool's real price history and simulated backrun trades against every meaningful price move in it. At a twenty dollar trade size, nineteen out of fifty three simulated round trips came back profitable, some substantially, with the single best trade gaining just over nineteen percent on top of the position size. That's a completely different shape of result from anything else in this project. It evaporated almost entirely by fifty dollars and was gone completely by two hundred, because the pool was thin, but a thirty six percent hit rate at any size, on real historical data, was the first genuinely exciting number I'd seen in weeks of mostly disappointing ones.

I want to be honest about what I did with that number at the time, which was nothing. One pool is one data point, not a strategy, and I've watched enough people generalize from exactly this kind of early promising result to know better. So instead of acting on it, I built a live monitor that watches Raydium's program activity in real time for genuine new pool creation events, rather than relying on a retrospective search of an already stale leaderboard, and let it run across several sessions.

It caught two real bugs on the way that are worth walking through because they're the kind of thing that quietly costs you data without ever throwing an error. First, the live monitor picks up new pool events through a streaming subscription that fires slightly ahead of when a plain lookup for that same transaction's details would actually succeed, because of a gap between two different commitment levels Solana RPC nodes expose. My original code treated "not found yet" as "this isn't actually a real pool" and discarded it silently, with no retry logic and no log line marking the drop. That meant the first two genuine launches the monitor saw were thrown away without a trace. I only caught this because I noticed the log showed a launch being detected with no follow up line ever appearing after it. The absence of expected output was the actual tell, not an error message, which made it much easier to miss. Second, the backrun simulation assumed the reference asset, wrapped SOL, was always labeled a specific way inside the pool's internal account structure, when that labeling is actually just a convention the pool's creator picks freely at the moment they set the pool up. Two of the first three real launches had it labeled the other way around, which silently produced a swap calculation mirrored around the wrong axis without ever throwing an exception.

With both of those fixed and the sample grown from one pool to three, then to four, the picture didn't firm up toward the exciting early number. It moved the opposite direction. At twenty dollar size, the net positive rate across the combined sample dropped from thirty six percent down to about fifteen percent, and once I weighted in the original pool's much longer observed history, the typical round trip across the full combined dataset of nearly five thousand simulated trades came out losing somewhere between roughly seventy percent and essentially the entire position, depending on size.

But the number wasn't actually the most interesting finding. I went back and asked a more specific question: does this get better as a pool ages, the way you'd expect if the early chaos were simply "price discovery hasn't caught up yet" and would naturally resolve once real liquidity arrives? I pulled the actual liquidity depth at every observed moment across each pool's life to check. The answer is unambiguous. It does not get better. It gets worse.

The original pool peaked at somewhere around eleven hundred dollars of usable liquidity on its SOL side about an hour after launch, then declined steadily from there. By about twenty four hours old, it held somewhere between nothing and thirteen cents. I checked the live on chain vault directly rather than trusting the historical replay, and at the time of writing it currently holds about fifty eight cents. It once held over a thousand dollars. The other three pools told the same underlying story on different timelines. One bled from around fifty dollars of depth down to fractions of a cent within about ninety minutes. Another went from around a hundred dollars down to near nothing within roughly five hours. A fourth crashed from around sixty five dollars down to a floor of ten to thirty dollars within the first eighty seconds of its existence, consistent with someone pulling liquidity almost immediately after launch, then just sat at that floor for the rest of its observed life without either recovering or fully zeroing out. Every single one of the observations across all four pools falls into the thinnest liquidity bracket I track. None of them ever crossed into a range where meaningful trade size could actually clear.

That finding reframes the entire idea. This isn't a maturing opportunity where you catch a brief mispricing on a pool that's on its way to becoming a real, liquid market. It's much closer to a race against a pool that is actively dying, usually within hours and sometimes within minutes of being born. The counterparty on the other side of most of these launches looks less like "the market hasn't caught up to fair value yet" and more like "someone is actively pulling their liquidity back out," which is a materially worse and more adversarial framing than the one I started with. I'm calling this strategy dead too, not because the early number was fake, but because more data made the honest picture worse, not better, and that direction is the one that actually matters when you're deciding whether to trust an early result.

Round four: the last untested idea

There was one idea left on the list, and it's the most specific one. Concentrated liquidity pools like Orca's don't hold liquidity smoothly across every possible price. Providers set discrete ranges, and the pool's available depth changes in steps right at the edges of those ranges. The theory is that when a trade pushes price across one of those edges, the pool can be briefly mispriced relative to a reference venue in a way that's structurally different from the ordinary standing spread I'd already ruled out twice.

My first attempt at measuring this was wrong, and I want to walk through why rather than skip past it, because catching it is a good example of the same discipline that ran through this entire project. Orca's on chain tick data is organized into fixed size storage pages, each spanning a wide chunk of consecutive price ticks, purely so the program doesn't have to load an enormous account just to check the price. My first version of this test flagged a "boundary crossing" any time price moved into a different storage page, which sounds reasonable until you realize that's a bookkeeping convenience with no economic meaning whatsoever. The prices that actually matter are wherever a liquidity provider genuinely set a position edge, which Orca marks with an explicit flag on that specific tick, and those are far more numerous and far more frequent than the storage page boundaries. I ran the flawed version live for a few minutes, watched it find zero crossings in a window where crossings should plausibly happen, got suspicious of a suspiciously clean zero (a lesson this project had already taught me twice by that point), and rebuilt the detector to check the real per tick flag before letting it run for real.

With that fixed, I watched forty four pairs for about forty minutes and caught twenty five genuine crossing events across eight distinct pairs, out of close to eleven thousand total observations. None of the trades placed immediately after one of those crossings were profitable, at any size, matching the failure pattern of everything else in this project. But there was a real, smaller magnitude pattern hiding in the losses: net losses on the polls immediately following a genuine crossing were consistently around two to three times smaller than the general baseline, at every size I tested. And the single closest to breakeven trade in this entire project, across every strategy and every venue, showed up here: a fifty dollar round trip on SOL against USDC right after a tick crossing, losing a little under six and a half basis points. For context, the very first control run of this whole project, the one specifically designed to fail, on that exact same pair, came in at a loss of about six basis points. Those two numbers landing within a hundredth of a percent of each other, from two completely different measurement approaches run about twelve days apart, is a small but genuinely satisfying piece of evidence that I was measuring something real and stable the entire time, not chasing statistical noise.

I don't think this pattern is actual evidence of a real, exploitable tick boundary edge, though, and it's worth explaining why, because the reasoning generalizes well beyond this one test. A pool only registers a genuine tick crossing if it trades often enough, inside a forty minute window, to actually push price across a real position edge. That selection criterion picks out almost exclusively the deepest, most actively traded pairs in the entire watchlist, the same small handful that every other test in this project had already found were the least bad pairs to trade against, crossing or no crossing. The thirty six genuinely thin pairs in the watchlist, the ones where a real tick boundary dislocation would actually be worth something if it existed, produced zero crossing events in forty minutes, simply because they don't trade often enough to generate one. The mechanism this strategy needed, real edges on pools too thin for anyone else to bother watching, turns out to require a level of trading activity that only the pools too deep to be worth arbing against actually have. That's not a coincidence I can wave away. It's the same wall as everywhere else in this project, approached from a slightly different angle.

What actually killed this

Five different strategies, three distinct automated market maker designs (constant product, concentrated liquidity ranges, and discrete liquidity bins), three separate exchanges, more than twenty thousand real observations combined, and the same failure mode every single time. That's a far more useful result than a scattered pile of unrelated dead ends would have been, because it points at one mechanism instead of five unrelated ones.

Spread and price impact are not independent variables here. They come from the same root cause. A pool's price can only drift away from a fair reference value if there isn't enough liquidity sitting near the current price to hold it in place, but that exact same thinness guarantees your own trade, however small, pushes the price against you by more than the gap you were trying to capture. Wide spread and bad execution aren't two separate problems you might get lucky and only have one of. Across every pool I tested, on every venue and every strategy shape, they were the same underlying problem described two different ways. The pairs with the widest spreads were, with almost mechanical consistency, the worst pairs to actually trade against.

There's a slightly more formal way to say this that I think is worth stating plainly, because it generalizes past this specific project. Two markets connected by an arbitrageur who faces some real cost to correct a mispricing will let their prices drift apart by roughly that cost before it becomes worth acting on. When that cost is dominated by a fixed component, like a flat fee, the tolerable drift band stays roughly constant regardless of size. But when that cost is dominated by price impact, which scales directly with how thin the pool is, the band that prices are allowed to drift within before someone corrects it widens in exactly the same proportion as the thinness that makes correcting it expensive. You end up in a situation where the width of the spread and the cost of capturing it aren't just correlated. They're mathematically tied to the same underlying quantity, liquidity depth, which is why I never found a pool where one was favorable and the other wasn't.

What this means if you're thinking about trying this yourself

If you're an intermediate or advanced participant in this space evaluating whether to build something like this, I'd offer three specific, non generic points, based on what actually happened here rather than on general priors about market efficiency.

First, the contested, high volume pairs are genuinely unwinnable at retail scale, and it's worth understanding precisely why rather than just accepting it. Jito client validators represent the overwhelming majority of Solana's staked infrastructure at this point, and the searchers competing for the profitable bundles on major pairs are running colocated hardware with direct access to shred streaming from validators, competing down to fractions of a millisecond and bidding tips that reflect exactly how much margin is actually available. That's not a knock on retail participants. It's simply a different infrastructure tier, and no amount of clever strategy design closes a latency gap measured in single digit milliseconds against someone sitting physically next to the validator.

Second, and this is the part that surprised me most, going down the liquidity curve into the long tail doesn't route around that problem the way the standard pitch for this strategy suggests it does. It trades one hard problem, being outraced by professional infrastructure, for a different hard problem that turns out to be just as fatal: on pools thin enough that professional searchers genuinely aren't watching them, the same thinness that lets a real price gap form is exactly what makes that gap uncapturable once fees and execution costs are honestly accounted for. This isn't a coverage problem you can fix by building a bigger watchlist or a faster scanner. It's a structural property of how these specific automated market maker designs price trades relative to available depth.

Third, even setting aside whether an edge exists at all, it's worth asking honestly whether a real one would be a business rather than a curiosity. A genuine edge worth twenty dollars per opportunity, showing up unpredictably and requiring careful manual risk framing around directional exposure, isn't something you can quietly scale into meaningful income just by running more instances of the same bot. Capacity and frequency matter as much as the existence of the edge itself, and it's worth doing that arithmetic before writing any code, not after.

None of this means arbitrage as a category is dead, or that DeFi markets in general are perfectly efficient. It means this specific slice, constant product and concentrated liquidity automated market makers, accessed through public retail infrastructure, on a chain with a mature and well capitalized MEV ecosystem, is already spoken for at every liquidity tier I could reach, right down into pools worth a few thousand dollars.

Where this leaves things

I pre registered the thresholds for this entire project before looking at a single result, specifically so I couldn't talk myself into moving the goalposts once I saw disappointing numbers roll in. Every strategy in that original plan has now been run against real, live, on chain data, using decoders individually verified to reproduce actual historical swap outputs exactly, cross checked against a positive control that proves the measurement harness can find a real opportunity when one genuinely exists. Every single one came back the same way.

The honest call here is a clean stop, on this chain, at this scale, with this infrastructure. Not "the idea was a bad one." The underlying logic was sound, and I'd make the same bet again given the same starting information. It's that this specific slice of it, retail RPC access with no colocated infrastructure, competing against a mature professional MEV ecosystem on two of the most common automated market maker designs in the industry, is fully priced already. There's a version of this that could theoretically still work on a newer chain with a less mature searcher ecosystem, and I might poke at that eventually as a genuinely separate project with its own fresh validation from scratch, but I want to be honest that the actual mechanical reason this failed, spread and impact being tied to the same underlying quantity, isn't a fact about Solana specifically. I'd expect it to travel to most chains running similar automated market maker designs, once their own MEV ecosystem matures enough for professional infrastructure to show up.

Total cost of finding this out: no capital at risk at any point, since every single trade across the entire project was simulated against real historical or live data and never actually submitted on chain. A couple of weeks of research time, one very specific kind of sadness watching a pool's liquidity tick down from four figures to under a dollar in close to real time, and a working measurement harness with individually verified decoders that I'm glad exists and equally glad I don't have to pretend is the foundation of a real business. Finding this out cheaply, rather than convincing myself of something false expensively, was the actual goal from day one. On that measure, this went exactly right.