Fills
A paper trading tool that fills you at the price you clicked is a toy. It teaches the one habit that loses money in the market it is pretending to be, that size is free and speed is free, and then tells you that you are good at trading.
What happens when you click
- The pool's reserves are read from the chain at that moment, and quoted.
- Nothing happens for 600 milliseconds. This is the wait a real transaction takes to land, and it is not simulated with a random number.
- The reserves are read again, and the fill is computed against the state the market is actually in by then.
So the price moves under you while you wait, exactly as much as it really moved, because it is the same market. A trade into a token that is running gets a worse price than the one on screen. That is the point.
What can go wrong, and does
A trade can be rejected outright, for the same reasons a real one is: slippage past your tolerance, price impact past the season's limit, no liquidity, a size too small to matter, or a market that graduated to a different venue while you were waiting. A rejected trade is a real outcome and it is recorded as one.
Fees are the real ones. On a bonding curve that is 125 basis points, split 95 to the protocol and 30 to the creator. Your cost basis includes them, because a position is only in profit once it has covered what it cost to open.
How accurate is it
The engine is replayed against trades that actually happened. Real swaps are pulled from the chain, ordered by slot and by position within the transaction, and each one is re-quoted from the reserves that stood immediately before it. The difference between what the engine produces and what the market produced is the error.
- Measured
- 11 August 2026
- Samples
- 129 real fills
- Median error
- 0 bps
- 95th percentile
- 0 bps
- Worst case
- 0 bps
- Exact matches
- 129 of 129
Not close. Identical, to the lamport, on every sample.
A pair is only scored when the reserves prove the two trades were consecutive, that nothing happened in between that we did not see. In that run, 91 of 222 events were skipped for that reason. Throwing away most of the data is what makes the number mean anything: a sample that quietly included gaps would be measuring our bookkeeping rather than the engine.
The date matters because the harness reads live history, so the sample grows as those tokens keep trading. Rerunning it will not reproduce these counts exactly, and a page that stated them as though it would was inviting the reader to think the number had been rounded in our favour. The counts move; the errors have not.
After graduation. Every sample above is a curve fill, because that harness walks a token’s bonding curve and stops where the curve does. There is now a second one for PumpSwap, where a token trades once it graduates. On a liquid graduated coin it reproduces real swaps at 0 bps for the median, 95th percentile and worst case, across buys and sells, most of them identical to the smallest unit and the rest within one of it.
Most graduated pools price against more SOL than they actually hold. Three of five were sampled that way, and the surplus is recorded in the pool’s own account. Quoting the vault balance alone made the engine wrong on those by thousands of basis points: 8,937 on one, 7,501 and 26,589 on two others. Counting it brings all three to 28. The figure was solved from real fills on one pool and then confirmed on the other two, which is what separates a model from a curve fit.
Twenty-eight is where it stops, and the reason is worth stating plainly. Those pools quote against roughly 19 SOL while holding 1.5, so an ordinary trade barely moves the price and the quote is very nearly a straight line, and on a straight line a fee and a reserve are indistinguishable. The same fills come out exact either from this reserve with a 0.02% fee, or from a reserve 0.3% larger with the real 0.30% one. Nothing measurable here tells those apart, so the published fee stays and the difference is left where it falls against you rather than for you. Pools with nothing recorded, the liquid ones, remain exact.
What a graduated token costs. PumpSwap publishes its fee schedule in an account on chain, and that is where these numbers come from rather than from anything written in this repository: 0.20% to the pool’s liquidity providers, 0.05% protocol, 0.05% to the coin’s creator. Thirty basis points, read live and re-read if it changes.
Until recently the engine charged 1.25% here, the bonding curve’s rate, copied across and never checked, because the harness that checks fees only ever walked the curve. Every migrated coin was four times too expensive to trade, and since a season ranks curve traders and migrated-coin traders against each other for the same prize, that was a handicap on one of them rather than only an inaccuracy. It is measured now, two independent ways: the published schedule sums to 30 bps, and replaying real buys off the chain recovers 29.
Check it yourself
The harness is in the repository and runs against mainnet:
RPC_VALIDATION=1 npx vitest run packages/validation/test/mainnet.test.ts
npx tsx scripts/replay-pumpswap.mts <mint>It reads live trades, so it measures whatever the market did today rather than a fixture chosen to look good. The sample differs on every run and the error does not.
What this does not cover
- It measures the pricing, not the delay. Whether 600 milliseconds is the right wait is a judgement; the fee and curve arithmetic is not.
- A token with no readable pool cannot be quoted at all, and trading is refused rather than estimated.
- Your order never affects the real market, so it never moves the price for anyone else. At the sizes a practice balance allows, that difference is small; at large size it would not be.