fix: honour the response cache on paid GETs - #17
Merged
Conversation
doGetWithPayment never touched bc.cache. doGet reads it on the way in and writes it on the way out; the paid path did neither, so WithCache(true) was a silent no-op on exactly the endpoints that cost money. cache.go gives /v1/pm/ a 30-minute TTL and prediction_market.go routes through doGetWithPayment, so every repeat call re-signed and re-paid USDC while the caller believed caching was on. Consult the cache before issuing the request and populate it on both the free-200 and the post-payment branch. The cache keys on (endpoint, body), and this path has a query map doGet does not. Passing nil would make every query on an endpoint share one entry and serve another query's response — worse than not caching at all — so cacheQueryBody folds the query into the key. Endpoints without a configured TTL are untouched: ttlFor returns 0 for /v1/zerox/, /v1/defillama/ and /v1/surf/, so those still hit the network every time rather than serving stale quotes. Tests, all offline: a repeat paid GET is served from cache with zero further requests and exactly one payment; two queries on one endpoint stay distinct; a client with no cache is unaffected; a TTL-less endpoint is not cached. Mutation-checked — reverting the cache read fails only the repeat test, keying on the endpoint alone fails only the collision test. Fixes #10.
VickyXAI
pushed a commit
that referenced
this pull request
Aug 4, 2026
Ships the paid-GET cache fix from #17 (fixes #10). doGetWithPayment never consulted bc.cache, so WithCache(true) was a silent no-op on the endpoints that cost money — /v1/pm/ has a 30-minute TTL yet every repeat call re-signed and re-paid. Also on main since 0.19.3, test-only: #18 pins that a paid GET with a server-provided blockhash makes zero client RPC calls.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #10.
What
doGetWithPaymentnever touchedbc.cache.doGetreads it on the way in (base_client.go:305) and writes it on the way out (:338); the paid path did neither.So
WithCache(true)was a silent no-op on exactly the endpoints that cost money.cache.go:16gives/v1/pm/a 30-minute TTL andprediction_market.go:22routes throughdoGetWithPayment— every repeat call re-signed and re-paid real USDC while the caller believed caching was on.This consults the cache before issuing the request and populates it on both the free-200 branch and the post-payment branch.
The part that isn't obvious
The cache keys on
(endpoint, body), and this path carries aquerymap thatdoGetdoesn't have. Passingnilfor the body would make every query on an endpoint share one entry —?q=bitcoinwould serve?q=ethereum's response. That is strictly worse than no caching, socacheQueryBodyfolds the query into the key.Endpoints without a configured TTL are untouched.
ttlForreturns 0 for/v1/zerox/,/v1/defillama/and/v1/surf/, so those still hit the network every time rather than serving stale swap quotes. Only/v1/pm/and/v1/searchhave TTLs today; whether the others should is a product call, not this PR's.Impact
Repeat prediction-market queries stop costing money. This became live in v0.19.2, which opened these endpoints to Solana clients for the first time (#8, #9).
Testing
All offline, verified with egress blocked:
TestPaidGetServesRepeatFromCache— second identical paid GET makes zero further requests, exactly one payment,GetSpending()reports one call at $0.001.TestPaidGetCacheKeyIncludesQuery— two queries on one endpoint stay distinct and each pays once.TestPaidGetWithoutCacheStillWorks— a client with no cache is unaffected.TestPaidGetNoTTLEndpointNotCached—/v1/zerox/still hits the network every time.Mutation-checked: reverting the cache read fails only the repeat test; keying on the endpoint alone fails only the collision test.
Full suite green with
-race -count=2 -shuffle=onand no network.go vetclean.sync-brand-numbers.mjs --checkpasses.VERSION/CHANGELOG untouched — left for the release.
Provenance
Surfaced by the adversarial pass during
/reviewof #9.