Skip to content

PumpSwap ExactIn quote ignores virtual_quote_reserves in v7.0.17 (deterministic 39.99% overquote) #132

Description

@jpmediadev

Summary

Self-hosted Metis v7.0.17 ignores the appended PumpSwap
Pool::virtual_quote_reserves field when quoting an ExactIn SOL-to-token swap.

The self-hosted output is exactly reproduced by pricing against the raw quote
vault. The public Jupiter output is exactly reproduced by PumpSwap's required
effective reserve:

effective_quote_reserves =
    pool_quote_token_account.amount + Pool::virtual_quote_reserves

This is a deterministic calculation issue rather than route selection or normal
quote staleness.

Environment

Architecture: x86_64
Metis: metis-binary 7.0.17 b1541702f205071fc532f269a4f4c4699a700406
Binary SHA-256: 3d01df17075d79cd6d5b3f1d333c30e906ada9999e2491545e15f24da55f60fb

The executable is byte-identical to the official v7.0.17 release asset.

Reproduction

QUERY='inputMint=So11111111111111111111111111111111111111112&outputMint=FsFVoqydZ8iHupxPz7VS6s5c9HJmsikyaQkdunCHpump&amount=100000000&swapMode=ExactIn&slippageBps=50&onlyDirectRoutes=true&restrictI
ntermediateTokens=true'

curl -s "http://127.0.0.1:8080/quote?${QUERY}" |
  jq '{contextSlot,outAmount,otherAmountThreshold,routePlan}'

curl -s "https://lite-api.jup.ag/swap/v1/quote?${QUERY}" |
  jq '{contextSlot,outAmount,otherAmountThreshold,routePlan}'

Both responses selected exactly one direct route through the same pool:

ammKey: 2eF6z1oJfr6WTHecH5QxHLEN4m3iQ6x3Dq6bQguxEyjM
label: Pump.fun Amm

Captured responses:

Self-hosted Metis v7.0.17:
  contextSlot:         440268500
  outAmount:           825974682488
  otherAmountThreshold: 821844809076

Public Jupiter:
  contextSlot:         440268500
  outAmount:           589999391290
  otherAmountThreshold: 587049394334

The live amounts will naturally change as the pool trades. The exact captured
state and deterministic reconstruction are provided below.

Exact on-chain reconstruction

Pool:

2eF6z1oJfr6WTHecH5QxHLEN4m3iQ6x3Dq6bQguxEyjM

The latest successful pool transaction before contextSlot=440268500 was at
slot 440268476:

https://explorer.solana.com/tx/2r67PrsGL3w2UgjADLCY7YTaGcWk3D4K4uFsqBV4VL7Rsh6wKnTP36gwhDJQHqfV9TzqhKCryokYQwJbR9eJjvND

Its post-transaction state was:

base reserve B:              367685895388406
raw quote-vault reserve Q:       43867055758
virtual_quote_reserves V:        17584505358
effective quote reserve Q+V:     61451561116
gross ExactIn amount:               100000000
fee-adjusted CPMM input x:            98765429

x=98765429 follows PumpSwap SDK v1.19.0 fee and conservative rounding logic.

Self-hosted result: raw vault only

floor(B * x / (Q + x))

= floor(
    367685895388406 * 98765429
    / (43867055758 + 98765429)
  )

= 825974682488

This exactly equals the self-hosted Metis outAmount.

Required PumpSwap result: effective reserve

floor(B * x / (Q + V + x))

= floor(
    367685895388406 * 98765429
    / (43867055758 + 17584505358 + 98765429)
  )

= 589999391290

This exactly equals the public Jupiter outAmount.

Therefore:

self_hosted / correct - 1
= 825974682488 / 589999391290 - 1
= 39.995853331654%

The exact integer equality shows that self-hosted v7.0.17 uses Q, while the
required calculation uses Q + V.

Expected behavior

Metis should decode the appended signed Pool::virtual_quote_reserves field and
use the effective quote reserve required by the current PumpSwap specification.

Expected output for the captured state:

589999391290

Actual behavior

Self-hosted Metis prices against the raw quote vault and returns:

825974682488

This is a deterministic 39.995853% overquote.

Impact

At slippageBps=50, Metis produces the following minimum output:

821844809076

But the correct quote for the captured state is:

589999391290

A transaction built from the self-hosted quote therefore has an unattainable
minimum output and can fail in the Jupiter JUP6... program with error 6001
(SlippageToleranceExceeded).

Retrying a fresh quote from the same binary does not resolve the issue because
the same reserve calculation is repeated. Increasing slippage enough to cover
this difference is not a safe workaround.

Relevant chronology

Official PumpSwap formula:

https://github.com/pump-fun/pump-public-docs/blob/main/docs/PUMP_SWAP_README.md#quoting-effective-quote-reserves

Official PumpSwap SDK v1.19.0 buy calculation:

https://unpkg.com/@pump-fun/pump-swap-sdk@1.19.0/src/sdk/buy.ts

Jupiter error reference:

https://developers.jup.ag/docs/swap/v1/common-errors

Requested fix

Please:

  1. Decode the appended signed Pool::virtual_quote_reserves field.
  2. Use raw_quote_vault + virtual_quote_reserves in PumpSwap quote calculations.
  3. Use checked signed arithmetic and reject invalid effective reserves.
  4. Audit both buy and sell quote paths.
  5. Add a regression test with a non-zero virtual reserve.
  6. Publish an updated self-hosted Metis binary containing the fix.

A useful regression test would compare quote and simulation results for the pool
above and verify that:

  • a non-zero virtual reserve affects the quote;
  • the result does not equal the legacy raw-vault-only calculation;
  • pools with virtual_quote_reserves == 0 remain unchanged.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions