Slippage & Price Impact
The price a taker receives differs from the oracle mark by four separate quantities, and only one of them is what most traders mean by price impact. This page factors that difference term by term, separates the terms a quote already knows from the stochastic remainder, and gives the bounds a caller can set against that remainder.
1. The decomposition
Every AIMM quote is a product of four factors applied to the oracle mark, for a single leg with the trade size:
| Term | Definition | Depends on size | Owned by |
|---|---|---|---|
| , mid over mark | Inventory skew offset, evaluated at zero volume | No | Inventory Management §3, Liquidity Shaping §5 |
| Curve traversal, the VWAP over the interval the trade sweeps | Yes | Liquidity Shaping §8 | |
| Convex coverage toll on the drained output leg | Yes | Inventory Management §6 | |
= SwapQuote.spreadPbps | Path spread; the realized fee is of the pre-fee output | No | Spread & Fees |
Price impact in the AIMM sense is alone, together with when the output leg is under-covered. Everything else is a level or a fee and is present at zero size.
The pool publishes both the mark and the mid (SwapQuote.markPrice, midPrice, both exact WAD
(1e18), tokenOut per tokenIn, chained across every leg). Deliberate: it lets an indexer split
, the genuinely extractable value, from ,
the inventory skew, instead of conflating them into one execution-versus-oracle number. See
IPool.sol.
1.1. What each term is, and what it is not
Canonical vocabulary. Each word names exactly one object; none is a synonym for another.
| Term | Is | Kind | Symmetric in direction? | Site |
|---|---|---|---|---|
| mark | the oracle price, as attested by the keeper in the child’s anchor unit | level | yes | FeedMathLib.mark, consumed at Pricing.sol |
| skew | the signed dimensionless inventory index, int8 in , a function of coverage alone. An input, never a price | index | n/a | computeInventorySkew, Pricing.sol |
| skew offset | the price displacement that index produces, in bps | level | yes: one number, both directions quote off it, and it cancels on a round trip | _legMid, Pricing.sol; surfaced at IPool.sol and by @btr-protocol/sdk |
| mid | the mark carrying that offset, evaluated at zero volume | level | yes | _legMid, Pricing.sol |
| price impact (= curve traversal) | the cost of traversing the liquidity shape from the skew anchor, | one-directional cost | no, see §1.2 | _traverseCurve, Pricing.sol |
| spread | the round-trip fee width, SwapQuote.spreadPbps, symmetric about the mid | width | yes, by construction (Spread & Fees §4) | _pathSpread, Pricing.sol |
| fee | what a swap actually pays: half the spread, once, on the output | charge | yes | Pricing.sol |
| coverage toll | the convex charge on a drained output leg | one-directional, charge-only | no | _covToll, Pricing.sol |
| slippage | the stochastic gap between the quote and the fill (§3), not a pool term at all | risk | n/a | minAmountOut, §4.1 |
Four usages to reject on sight:
- “spread” for total cost. The spread is one of four terms and is usually not the largest. Total cost is the whole product above.
- “fee” for the spread.
minFeePbpsis a floor on the spread, not on the fee; the fee is half the spread. Nothing caps the spread from above (Spread & Fees §3.1). - “premium”, for anything. The word carries three objects at once: the mid displacement, a generic price-versus-mark ratio, and a component of the spread - the level-versus-width collision this table exists to stop. The mid displacement is the skew offset (a level); the spread’s parts are bands and surcharges (widths). “Premium” and “discount” are admissible only as informal words for the sign of a skew offset, never as a quantity and never as a parameter.
- “skew” for a price.
skewis the dimensionless index; the price effect is the skew offset. One map, two objects, two names.
Naming, settled: the Avellaneda-Stoikov reservation price is this codebase’s mid, and “mid” is the only name used for it. AIMM carries no absolute price band on an asset; the depeg gate is the feed-relative
refFeedId/refBandBpspair plus the base parity halt (Anchor Path Pricing §7.1).
1.2. Total cost is direction-asymmetric; the width and the level are not
The spread is direction-invariant: both sides quote the same width about the same mid (Spread & Fees §4). The skew offset is a level: the same number for both directions, and it cancels on a round trip. Price impact is neither, so total cost is not.
Measured on an exact integer port of Pricing.sol + NUQuartic.sol, bit-verified against forge
on shipped preset 5. WETH: dispRefPbps 500, so dispersion is the
minDispersionPbps of 2034 PBPS — still WETH’s live floor — spread 1032 PBPS (half-spread 5.16 bps),
mark 1. The leg’s minFeePbps has since been raised, so the absolute spread column reads low against
the chain today; the impact and skew columns, which is what the table is for, do not depend on it. Notional is a fixed
percentage of liabilities, held constant across coverage states, so the rows are comparable.
slip is in bps. The skew column is the shipped fixed law,
draining and filling, saturating at and ; and
are where the law clamps, not a configurable band, and no per-asset dial enters these rows.
Measurement note: the row reads / from the fixture. The shipped law gives exactly and at . One skew unit of fixture drift, worth 50 bps of the spline domain; the slippage figures either side of it are unaffected at this resolution.
| skew | skew offset | notional | slip sell | slip buy | gap (buy sell) | ||
|---|---|---|---|---|---|---|---|
| 0.70 | 8000 | bps | 1% | 0.290 | 0.280 | ||
| 0.70 | 10% | 2.770 | 3.050 | ||||
| 0.70 | 25% | 6.600 | 4.420 | (buy clipped) | |||
| 0.85 | 6500 | bps | 1% | 0.210 | 0.210 | ||
| 0.85 | 10% | 2.070 | 2.160 | ||||
| 0.85 | 25% | 5.100 | 5.710 | ||||
| 1.00 | 5000 | 1% | 0.170 | 0.170 | |||
| 1.00 | 10% | 1.710 | 1.710 | ||||
| 1.00 | 25% | 4.360 | 4.360 | ||||
| 1.15 | 4300 | bps | 25% | 3.890 | 3.720 | ||
| 1.30 | 3500 | bps | 25% | 3.610 | 3.350 |
- At the gap is exactly zero at every size. The shipped
wQvectors are perfectly antisymmetric about , so their density median is , the traverse starts there at , and a symmetric inventory state gives symmetric slippage by construction. The asymmetry is a property of being off par, not of the machine. On an asymmetric preset moves and the zero-gap point moves with it, since the anchor is the median and not the domain midpoint (Liquidity Shaping §5.1). - Off-center and unclipped, it is real and correctly signed. At and 25% of liabilities, the direction that worsens inventory pays 5.710 bps against 5.100 bps for the direction that heals it. The 0.610 bps gap is about 12% of the 5.16 bps half-spread.
- Above parity the sign flips, as it must: at and the buy side is now the healing direction and pays less.
- The row is not a discount, it is the buy-side traverse hitting the domain clip at . Past that point marginal impact is zero. See §2.5.
- The level dominates. At and 1% of liabilities the skew offset is 10.880 bps against 0.29 bps of traverse, so the mid, not the curve, is what moves the executed price off the mark.
- A round trip costs the same whichever way you start: 10.663, 10.700 and 10.669 bps across the inventory states above. That is the proof that the skew offset is a level that cancels, and that what remains is the spread plus two traverse terms.
(Pricing.sol`), so both directions of one edge use the same child, the same curve and the
same depth. Single-leg asymmetry comes from three things instead:- the off-center start , where
is the curve’s density median from the header and equals 5000 on every shipped preset
(
_skewToDepth,Pricing.sol); - the convexity of the shape either side of that start, since a sell integrates and a buy integrates over a curve that is not locally linear;
- domain clipping at and (
Pricing.sol).
The “depleted side traverses further” intuition does hold one level up, on a two-leg spoke-to-spoke path, where the two directions traverse different assets’ curves at different depths.
So a round trip is not two halves of one number: it pays the full spread once (Spread & Fees §5.5) plus two traverse terms that differ off par, plus the toll on whichever direction drains a walled leg.
1.3. Worked, verified on chain
A shipped stable pool (addresses: 2. Deployments), USDT to USDC, one leg. Read from
Pool.getSwapQuote at capture time. spreadPbps = 177, covToll = 0 on all three, because the USDC
output leg read at that block. That is not the shipped state: every Arc leg
carries (600 stables / 1,500 FX / 1,800 metals / 2,000 crypto / 2,500
equities, hub at the maximum of its own spokes), so the toll column of a quote taken today is
non-zero the moment the output leg sits below parity — Parametrization §6.2.
Historical capture, before the 2026-08-21 adaptive-dispersion change. of pbps is the old / state. At HEAD the same live inputs give , and a mid offset near pbps (Liquidity Shaping §8.6). The rows are kept because they are the measured verification of the decomposition, not today’s quote.
amountIn (USDT) | Total exec/mark | ||||
|---|---|---|---|---|---|
| 1 | pbps | pbps | 0 | pbps | pbps |
| 1,000 | pbps | pbps | 0 | pbps | pbps |
| 10,000 | pbps | pbps | 0 | pbps | pbps |
Read off: the fee is 62% of the total cost at 1 USDT and 53% at 10,000 USDT. The skew offset is 38% and 32%. The curve, the term most people mean by “price impact”, is 0% to 15%: on a tight stable leg it is the smallest of the four terms.
2. Price impact: deterministic
Impact is knowable before execution: a pure function of on-chain state the quote already reads.
2.1. AIMM
with ( on every shipped preset), , and , all as defined in Liquidity Shaping §8.
is the leg’s raw reserves. uint256 depth = reserves == 0 ? 1 : uint256(reserves) in
Pricing.quoteSwap and _priceEdgeHop: the zero-guard exists only because the traverse divides by
it. There is no coverage-dependent adjustment on top, and no “effective” or “virtual” depth
anywhere in the decomposition. Impact therefore scales with and nothing else: at a given size, a
thinner leg is a deeper traverse, with no state-dependent discount in either direction. A denominator
that grew as coverage fell would shorten the traverse on exactly the leg an informed flow is
draining, which is how a two-block round trip becomes trader-positive: an LP leak, not a depth
benefit.
Three structural consequences:
- Impact is bounded. The whole book spans , with the preset’s half-swing at its reference dispersion. No trade of any size can move the price beyond that edge, because clamps at the domain edge and clamps at BPS.
- Impact is zero below the quantum. is an integer count of bps, so any trade smaller than executes exactly at the mid. Live USDT: 6.0036 USDT.
- Impact is asymmetric. on every live leg, so the sell side and the buy side of the same leg have different remaining room. See Liquidity Shaping §5.2.
2.2. Against a constant-product pool
For , executing against reserve gives , so impact is and grows without bound. AIMM’s impact is a traversal of a bounded offset curve. Measured on the USDT leg at preset 2, selling. These rows are a pre-adaptive-dispersion capture ( pbps, ): the band sat at the bare floor and the skew read the old symmetric slope. Under the current law the same live inputs give and (§2.6, and Liquidity Shaping §8.6). The ratio the table is making, bounded traversal against unbounded curvature, is unchanged by that:
| Size as % of depth | (bps) | AIMM | Constant-product impact |
|---|---|---|---|
| 0.1% | 10 | pbps | bp |
| 0.5% | 50 | pbps | bp |
| 1% | 100 | pbps | bp |
| 5% | 500 | pbps | bp |
| 10% | 1000 | pbps | bp |
| 20% | 2000 | pbps | bp |
At 10% of depth AIMM charges 0.15 bp of curve impact where a constant-product pool charges 909 bp, a factor of about 6,000. That is not a free lunch: USDT’s half-swing at this capture is 1.61 bp, so the whole book spans 3.22 bp end to end and it cannot charge more. The defense against adverse selection sits in the spread, the coverage toll and the oracle push policy rather than in curvature. A constant-product pool prices size; AIMM prices information, and lets size run cheap until the reserve itself binds.
2.3. When the reserve binds
Beyond the curve, two hard limits apply and neither is a smooth impact term:
Pricing._legScaleOut(Pricing.sol) clamps a leg’s output to the destination asset’s reserves and sets aclampedflag. When it fires,SwapQuote.amountOutreports the clamped number and the per-hop execution price is suppressed. This is visible live: a 100,000 USDT quote returnshopAmountsequal exactly to the USDC reserve balance (66,369.84), so the quoted rate silently degrades to a partial fill at full price rather than reverting. ReadhopAmountsagainst reserves before trusting a large quote.PoolIOLib.settle(PoolIOLib.sol) revertsInsufficientAmountwhen cannot coveramountOut + protoFee + minLiquidity. This is the failure that governs at settlement.
2.4. The coverage toll
When the output leg is coverage-walled (; on the reference roster that is every spoke) a drain below parity pays
evaluated at clamped to . It is convex and it diverges as , so the marginal cost of the last of a reserve is unbounded even though the curve impact is bounded. This, not the spline, is what stops a drain. Full statement: Inventory Management §6.
2.5. The buy-side traverse saturates, and depth cannot stop a drain
A consequence of the bounded book, not a defect, and not to be fixed inside the traverse.
Pricing._traverseCurve clamps the volume fraction at BPS and the buy-side end depth at BPS.
Once reaches BPS the averaged offset over stops changing with size, so
marginal price impact past that clip is exactly zero, and the saturated value falls as the pool
depletes, because a higher leaves a shorter interval to average over. The clip is on the spline
domain, not on liquidity, so the clip percentages below are percentages of raw reserves (§2.1),
the only depth quantity there is.
| Coverage | Clips at (% of reserves) | Saturated slippage |
|---|---|---|
| 50% | 9.29 bps | |
| 35% | 6.96 bps | |
| 20% | 4.42 bps |
Draining the entire WETH reserve costs about 14.45 bps at and 20.46 bps at in
traverse plus half-spread, and those two terms alone can never exceed bps at any size.
That is a bound on the bounded terms, not on the price. The coverage toll sits on top of it and is
unbounded: Pricing._covToll charges on the drained output leg once , and as
, so at the shipped the marginal cost of the last of the reserve
diverges (§2.4). A drain that stays above parity pays the 20.46 bps ceiling; one that crosses the peg
does not. Same numbers as the row in §1.2, which is that clip and not a discount.
The ceiling is correct for an oracle AMM. A pool that quotes the external mark must not price-discover; if size could move its quote without bound, the quote would stop being the mark. The consequence:
Depth cannot stop a drain. The curve is a bounded offset book, so no amount of size exhausts it. The only two mechanisms that stop one are the convex coverage toll (§2.4) and the hard reserve floor
minLiquidity(§2.3), whichinitAssetwrites as 0 and no deploy script raises (PoolConfig.sol).
A leg left with the wall off is a config defect, not a design choice:
- any leg carrying
kappaCovBps = 0has onlyminLiquidityand the reserve clip as its whole outflow defense. Operator rule: every listed asset including the hub must have κ>0 (Invariants §I-9). - Where the wall is on it dominates everything else by orders of magnitude: USDT at charged 43.84 bps of toll against a 0.61 bps spread on a pre-Arc reference-roster capture at . The toll is linear in , so at the live stable value of 600 the same drain costs 263 bps, and 1,053 bps on an equity leg at 2,500.
The saturation offers no atomic profit: what bounds a drain is the coverage toll of §2.4 and the
reserve clamp of §2.3, plus minLiquidity wherever a deploy config raises it above the initAsset
zero.
2.6. Dispersion
Dispersion sets the vertical scale of the curve and therefore the magnitude of :
Pricing._calculateDispersion (Pricing.sol; adaptive dispersion since the 2026-08-21 adaptive-dispersion change).
= minDispersionPbps is the additive base, not a post-hoc clamp; only the
protocol-wide MAX_DISPERSION_PBPS = 900000 ceiling clamps - there is no per-asset ceiling field.
At — the live value on stable legs and on every hub — this reduces to pbps: impact IS volatility-adaptive, one-for-one with the feed’s σ above the floor (live stable PBPS widens USDT from 161 to 237 pbps). The volatile classes run below 1.0x (0.30x FX, 0.40x crypto, 0.45x metals, 0.35x equities), so there the slope is that fraction of σ, deliberately: it is what keeps their book inside the interior swing cap (Parametrization §4.2). Full argument with numbers: Liquidity Shaping §6.4.
3. Slippage: stochastic
Slippage is the difference between the quote a taker saw and the price they got: the part that is not a function of the state the quote read.
3.1. Sources
| Source | Mechanism | Direction | AIMM-specific note |
|---|---|---|---|
| Mark update | A keeper push lands between quote and inclusion | Either | The pool quotes off the keeper mark (FeedMathLib.mark), so a push moves the whole quote, not just the tail |
| Inventory drift | Another swap changes , hence and | Either | Discrete: moves in steps of 50 bps of the domain |
| Coverage drift | Another swap changes on the output leg | Negative if worsened | Changes convexly |
| Staleness ramp | Feed ages past the keeper grace, widening | Negative | Deterministic given the age; see §3.2 |
| Front-running | Searcher trades ahead | Negative | Bounded by the book: they cannot push you past |
| Sandwich | Bot trades both sides | Negative | Round trips lose (Liquidity Shaping §8.3), so the sandwicher pays too |
| Reserve clamp | Output reserve drained by an earlier tx | Negative | §2.3 |
| Halt | Feed goes stale, confident, or depegged | Revert | Fails closed, never degrades silently |
3.2. The staleness ramp is not random
The spread carries a keeper-lag term (Pricing._staleTerm, Pricing.sol):
with the feed’s age in seconds, its ttl, and = STALE_Z (Pricing.sol). is 0
inside the grace and the surcharge grows as beyond it. The
observed live USDT spread of 177 pbps decomposes as 61 (path floor) + 0 (volatility term,
truncates to zero at ) + 100 (1 bp of feed confidence) + 16 (staleness, about 470 s of
age against a 30 s grace). USDT anchors to the base, so this is a one-leg path and the floor is
USDT’s own 61 alone; a two-leg cross sums both spokes’ floors
(Spread & Fees §9). A taker who
quotes and then waits sees the spread widen, predictably.
Past the hard TTL the feed does not widen further: FeedMathLib.gate reverts, and the leg does not
quote at all.
3.3. Positive slippage
Slippage can be favorable if the market moves your way between quote and inclusion. On a public mempool it rarely reaches the trader: searchers monitor pending transactions, favorable movements are captured by backrunners, and the unfavorable ones pass through. That asymmetry is the whole argument for private order flow.
4. Protections
4.1. minAmountOut
function swap(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 minAmountOut, // slippage bound
address recipient,
uint256 deadline // staleness bound; type(uint256).max opts out
) external payable returns (uint256 out);Pool.sol. The single state-mutating swap entry is Pricing.swap, and
the check is the last thing it does before pushing the output:
if (out < minAmountOut) revert Err.ThresholdViolation(out, minAmountOut);Two ordering facts:
- The threshold check runs after
PoolIOLib.settle. That is state-identical in an all-or-nothing transaction, but it means a violated threshold burns the full settlement gas. - A zero-delivery swap reverts first (
Pricing.swap,Err.ZeroValue), before the threshold. A fully-tolled coverage-wall drain therefore reverts asZeroValue, not asThresholdViolation.
Sizing the bound, given §1.1: for a stable leg the fee and skew are already about 145 pbps (1.45 bp) at zero size and are visible in the quote, so a bound set from the quote rather than from the mark can be tight.
| Tolerance below quote | Use |
|---|---|
| 0.05% to 0.1% | Stable legs, fresh feed, private order flow |
| 0.1% to 0.5% | Stable legs, public mempool |
| 0.5% to 1% | Volatile legs, normal conditions |
| 1% to 3% | Volatile legs, stressed tape; expect the spread term to move, not the curve |
4.2. deadline
Pool.beforeDeadline (Pool.sol) reverts Err.Expired when block.timestamp > deadline. The
deadline is inclusive (equality succeeds), there is no zero sentinel, and the opt-out is
type(uint256).max. Every value-moving entry point carries it: swap, withdraw, withdrawTo,
swapLiability. deposit and donate do not, because they mint at the current index against no
minimum output.
4.3. MEV-protected RPCs
Hide the transaction from the public mempool. The endpoints below are examples of widely used public providers, not a complete or endorsed list; verify any endpoint before routing value through it.
Ethereum L1
| Provider | Endpoint | Notes |
|---|---|---|
| MEV Blocker | https://rpc.mevblocker.io | CoW Protocol |
| Flashbots Protect | https://rpc.flashbots.net/fast | Fast mode trades some protection for inclusion |
| Merkle | https://eth.merkle.io | Free tier |
| Llama Nodes | https://eth.llamarpc.com | Free tier |
| BlockRazor | https://eth.blockrazor.xyz | Protection on by default |
BNB Chain: https://rpc.48.club, https://bsc.merkle.io, https://binance.llamarpc.com,
https://bsc.blockrazor.xyz.
Base: https://base.llamarpc.com.
Multi-chain private networks
| Provider | Coverage | MEV protection |
|---|---|---|
| Alchemy | Ethereum, Polygon, Arbitrum, Optimism, Base | Always on |
| BlockPI | Multi-chain | Enable in dashboard |
| dRPC | Multi-chain | Enable in dashboard |
| Helius | Solana | Optional Jito integration |
4.4. Timing and sizing
| Strategy | Effect | Trade-off |
|---|---|---|
| Trade shortly after a keeper push | Minimizes and mark drift | Requires watching the feed |
| Keep size below | Exactly zero curve impact | Only useful for small orders |
| Split across blocks | Lets and recover between clips | Higher gas; each clip pays again |
| Trade the coverage-restoring direction | Favorable side of , near-zero | Direction is dictated by pool state, not preference |
| Avoid draining a walled leg below parity | Avoids the convex toll entirely | Check on the output asset first |
Splitting is not a free win: the spread is charged per swap and is size-independent, while the curve impact is the small term. On a stable leg, splitting a trade into clips multiplies the dominant cost by and only saves the minor one.
5. Chain-specific ordering
5.1. Ethereum L1
Public mempool: every pending transaction is visible. Ordering is a gas auction. Protection is a private RPC to a trusted builder.
5.2. Arbitrum and OP Stack
Transactions go to a centralized sequencer with full visibility, expected to order fairly. Original ordering was FCFS; Arbitrum now runs a Timeboost auction for the priority lane, Base and OP run FCFS with priority-fee escalation. Third-party MEV is limited by the sequencer’s control of ordering.
5.3. Solana
No public mempool; transactions go directly to slot leaders, who control ordering. Jito provides bundle auctions at the validator level. Helius offers optional Jito integration.
5.4. Sui
DAG mempool with object-based execution. MEV concentrates on shared objects such as AMM pools. Parallel execution of independent transactions removes ordering MEV for simple transfers but not for contended pool state.
6. Practical guidance
6.1. Takers
- Set
minAmountOutfrom the quote, not from the mark. The gap between them is the skew plus the fee and is legitimate, not slippage. - Set a real
deadline.type(uint256).maxopts out of the only staleness bound the caller controls. - Check the output leg’s coverage before a large trade. The convex toll is the term that grows without bound.
- Compare
hopAmountsto the destination reserves on large quotes (§2.3). - Use a private RPC on L1 and BNB Chain.
6.2. Integrators
- Quote at display time with
Pool.getSwapQuote; it is aviewand takes no cache writes. - Show the decomposition, not one number: skew, curve, toll and fee are four different things with
four different risk profiles.
SwapQuoteexposesmarkPrice,midPrice,covToll,spreadPbps,protoFeeandlpFeeprecisely so this is possible without re-deriving anything. - Credit 100% of the fee to
tokenOut. There is no input-leg fee (IPool.sol). - The realized fee is
spreadPbps/2of the pre-fee output, notspreadPbps. - Handle
Err.Expired,Err.ThresholdViolation,Err.ZeroValue,Err.InsufficientAmount,Err.BaseDepeggedandErr.FeatureDisableddistinctly. They mean different things and only two of them are worth retrying.
6.3. Large traders
- Intent-based systems (CoW Protocol, UniswapX) for execution above the reserve scale.
- Private mempools or direct builder connections.
- Time-weight across blocks only if the spread cost of extra clips is smaller than the impact saved, which on a stable leg it usually is not (§4.4).
- Monitor markout against
midPrice, not againstmarkPrice. The mid is where the book was.
7. Research and further reading
- Flashbots: MEV in Eth2: MEV taxonomy and measurement
- Private MEV Protection RPCs: effectiveness analysis
- MEV on Polygon
- Cross-chain Sandwich Attacks
- High-Frequency Trading on DEXes
- UniswapX Price Improvement
- Protected Order Flow Relay
- Flashbots Docs, Jito Documentation
8. Related documentation
- 1.1.1. Inventory Management: and the coverage toll
- 1.1.2. Liquidity Shaping: , the curve and dispersion
- 1.1.3. Anchor Path Pricing: how the terms compose over multiple legs
- 1.1.4. Spread & Fees: and its components
- 1.1.6. Toxic Flow Mitigation
- 1.1.7. Parametrization
- 3.3. Flow Guards
- Glossary: Slippage, Price Impact, MEV-Protected RPC