A pool shows a large total value locked and your trade still moves the price several percent. The number was real; it was answering a different question than the one you had.
★TVL describes the pool. Depth describes what the pool does to your size.★
Why TVL Is the Wrong Number
Total value locked sums both reserves. A trade only interacts with one side of the pair — and specifically, ★your buy is limited by the reserve of the token you are receiving.★
// ★A "large" pool can be thin on the side you need.★
const reserveSol = 500_000n * LAMPORTS_PER_SOL; // deep
const reserveToken = 1_000n * 10n ** 9n; // ★thin★
Buying tokens draws down the token reserve. A pool with plenty of SOL and few tokens gives you a bad fill regardless of how impressive the headline figure looks.
The asymmetry matters for exits too. A position you entered comfortably can be difficult to exit if the reserve on the other side has since been drawn down by everyone else doing the same thing.
Depth Is a Curve, Not a Number
The honest way to answer "how much can this absorb" is to compute the impact at several sizes rather than looking for a single threshold:
function impactAt(amountIn, reserveIn, reserveOut, feeBps) {
const inAfterFee = amountIn * (10_000n - feeBps) / 10_000n;
const out = (inAfterFee * reserveOut) / (reserveIn + inAfterFee);
const spot = (inAfterFee * reserveOut) / reserveIn; // ★zero-impact price★
return Number(spot - out) / Number(spot);
}
for (const size of [0.1, 0.5, 1, 5, 10]) {
console.log(size, impactAt(toLamports(size), reserveIn, reserveOut, 25n));
}
★The output is a curve, and its shape is the answer.★ A pool where impact stays flat to 5 SOL and then rises steeply has a usable size of roughly 5 SOL — a fact no single TVL figure conveys.
Constant-product impact grows faster than linearly, so doubling your size more than doubles your impact. That is why "just trade smaller" works better than most people expect.
If your sizing is right and transactions still miss, a free BoltTx key is one line to test the submission path.
Size From the Exit, Not the Entry
★The mistake that costs the most is sizing a position by what you can buy.★
// ★Wrong question: how much can I get in?★
const maxEntry = sizeForImpact(reserves, MAX_IMPACT);
// ★Right question: how much can I get out?★
const maxExit = sizeForImpact(reservesAfterEntry, MAX_IMPACT);
const size = min(maxEntry, maxExit);
Entering moves the reserves against you. Your own buy makes the pool thinner on the side you will need when selling, so the exit is always harder than the entry at the same nominal size.
★A pool that comfortably absorbs your entry may not absorb your exit — and you find out at the worst moment.★
Concentrated Liquidity Changes the Arithmetic
Not every pool is constant product. Concentrated liquidity pools place liquidity in price ranges, which produces behaviour the formula above does not describe.
★Inside an active range, depth can be far better than a constant-product pool of the same TVL. Outside it, there may be nothing at all.★
The practical consequence: a concentrated pool's depth depends on where the price currently sits relative to the ranges providers chose. Depth measured now does not predict depth after a price move, because the move can push price out of the populated range entirely.
Check which pool type you are trading before applying any formula. The owner field returned by getAccountInfo on the pool account tells you which program it belongs to.
What Aggregators Hide
An aggregator route reports a good price by splitting across venues, which is genuinely useful and also obscures a risk.
★You may be entering through five pools and exiting through one.★ The route optimiser solves for price on the trade in front of it — it has no view on whether you can leave the same way.
Two habits worth having:
Check depth on the specific pool you would realistically exit through, not on the aggregate route.
Prefer a route whose largest leg is a pool deep enough to handle your full exit alone.
Measuring the Thing That Actually Bites
★Total liquidity matters less than how quickly it changes.★
// ★Track reserve deltas, not just levels.★
connection.onAccountChange(poolVault, (info, ctx) => {
const now = info.data.readBigUInt64LE(64);
history.push({ slot: ctx.slot, reserve: now });
}, "processed");
A pool that is deep and stable is tradeable. A pool that is deep right now but has lost a large share of its reserve in the last few minutes is a pool someone is leaving — and depth measured at a single instant cannot distinguish them.
For new tokens this is the more informative signal, because early liquidity is often both large and temporary.
Turning It Into a Rule
function maxTradeSize(reserves, maxImpactBps) {
let lo = 0n, hi = reserves.in / 2n;
while (hi - lo > PRECISION) { // ★binary search the curve★
const mid = (lo + hi) / 2n;
if (impactAt(mid, reserves.in, reserves.out, reserves.feeBps) * 10_000 > maxImpactBps) hi = mid;
else lo = mid;
}
return lo;
}
★A binary search on the impact curve gives you a size limit derived from the pool rather than picked by habit.★ Set the impact tolerance once, and every pool tells you its own maximum.
Then take the smaller of that and your exit-side limit, which is the number that should actually govern the trade.
What Landing Looks Like
Real transactions through our delivery nodes: median confirmation 336ms — under one slot.
★Depth is measured at the slot you read it; you execute later.★ On a thin pool, a few slots of delay is enough for someone else's trade to change what yours receives — which is why sizing conservatively and landing quickly address the same problem from two directions.
Where BoltTx Fits
We handle submission. Depth analysis and position sizing stay entirely in your code.
Submissions route through our own delivery nodes in four regions with stake-weighted routing and no public mempool exposure, so a transaction is not observable in transit before it lands. Your pool reads keep whatever endpoint you already use — the paths are independent.
You sign locally. We never hold funds, never sign, and never modify transaction contents, including the size and minimum output you computed. The tip travels inside the transaction, paid on chain from your own wallet, and reverts with the transaction if it fails, because that is how Solana handles atomic transactions. You pay only on transactions that reach the chain.
Get a free API key. No monthly fee:
const sender = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");
FAQ
How do I measure liquidity depth on a Solana pool? Compute price impact at several trade sizes against the pool's reserves. The resulting curve shows where impact starts rising steeply, which is the pool's usable size.
Why is TVL a poor measure of what I can trade? It sums both reserves, while your trade is limited by the reserve of the token you are receiving. A pool with a large headline figure can be thin on exactly the side you need.
How do I calculate price impact? Compare the output your size actually receives against the output an infinitesimally small trade would receive at the same reserves. The difference is impact caused by your own size.
Why is my exit harder than my entry? Because your entry moved the reserves. Buying makes the pool thinner on the side you will sell into, so the same nominal size faces worse depth on the way out.
Should I size a position by entry or exit capacity? Exit. Take the smaller of your entry limit and your exit limit computed against reserves after the entry, since being unable to leave is the more expensive failure.
Does price impact grow linearly with size? No, faster than linearly on constant-product pools. Doubling your size more than doubles your impact, which is why reducing size helps more than people expect.
How is concentrated liquidity different? Liquidity sits in price ranges rather than across the whole curve. Depth can be excellent inside an active range and nearly absent outside it, so the constant-product formula does not apply.
How do I know which pool type I am trading? The program that owns the pool account identifies it. Check that before applying any depth formula, since the arithmetic differs between pool designs.
Do aggregators solve the depth problem? Partly. They optimise price on the trade in front of them, which can mean entering through several pools and exiting through one. Check depth on the pool you would realistically exit through.
What is a reasonable price impact limit? It depends on your strategy, but the useful practice is deriving size from a fixed impact tolerance rather than picking a size by habit. Every pool then tells you its own maximum.
How do I find the maximum size for a given impact? Binary search the impact curve between zero and half the input reserve. That produces a size limit derived from the pool rather than assumed.
Is a deep pool always safe to trade? No. Depth right now says nothing about depth in a minute. Tracking reserve changes over time distinguishes a stable pool from one that people are currently leaving.
Why does liquidity matter more for new tokens? Because early liquidity is often both large and temporary. A pool can look deep at launch and lose most of its reserve shortly after, which a single-instant measurement cannot reveal.
Can I cache depth measurements? No. Reserves change with every trade, which is the whole reason depth is a live measurement rather than a property. Cache the pool's vault addresses and fee rate instead.
Does splitting a trade reduce impact? Not on the same pool within the same block — the reserves move as each part executes. Splitting across genuinely separate pools does help, since each absorbs a smaller share.
What should I monitor on a pool I trade regularly? Reserve levels, their rate of change, and the impact your standard size produces. The second is what warns you before the third becomes a problem.
Related Reading
- Reading Solana Pool State
- Solana Price Impact Calculation
- Solana Slippage and Routing
- Solana Token Safety Checks
- Solana Transaction Landing