mirror of
https://github.com/labrinyang/lp-terminal.git
synced 2026-08-13 20:58:04 +00:00
fix(indexer): stop a manipulated pool from minting prices
A price used to propagate from any pool with enough depth on the priced side, which let a thin pool with one inflated side mint a price for its other token and carry it across the graph. Depth is now measured on the side that is already credibly priced, and a pool-priced side may claim at most 100x that depth. Guard rails around it, all in one place: prices travel at most 3 hops from a GT/anchor seed, |tick| beyond 700k is a broken init rather than a market, stored prices must land in a plausibility band, and a TVL above $1B is treated as corrupt instead of as a whale. Adds a log tail so pool stats follow chain events instead of polling, a demand-gated frontpage, and a last-good fallback so a failed refresh serves the previous stat rather than a hole. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
3a392cb141
commit
d73eaf873a
+16
-10
@@ -1,5 +1,5 @@
|
||||
import { createPublicClient, defineChain, http, type PublicClient } from 'viem'
|
||||
import { log, PUBLIC_RPC, TUNE, rpcUrl, sleep } from './config'
|
||||
import { createPublicClient, defineChain, fallback, http, type PublicClient } from 'viem'
|
||||
import { log, PUBLIC_RPC, TUNE, rpcUrls, sleep } from './config'
|
||||
|
||||
// duplicated from src/config/chain.ts — that module imports src/config/env.ts
|
||||
// (import.meta.env, vite-only) so it can't be loaded under node
|
||||
@@ -11,20 +11,26 @@ const robinhood = defineChain({
|
||||
contracts: { multicall3: { address: '0xcA11bde05977b3631167028862bE2a173976CA11' } },
|
||||
})
|
||||
|
||||
const url = rpcUrl()
|
||||
export const usingPrivateRpc = url !== PUBLIC_RPC
|
||||
const urls = rpcUrls()
|
||||
export const usingPrivateRpc = urls.some((url) => url !== PUBLIC_RPC)
|
||||
// timeout is deliberately tight: a healthy 400-call aggregate answers in 2-4s
|
||||
// (measured 2026-07-16); a stalled attempt should fail fast and retry, not
|
||||
// pin the whole boot for 30s. Bad chunks degrade to sub-chunks in mc().
|
||||
export const pc: PublicClient = createPublicClient({
|
||||
chain: robinhood,
|
||||
transport: http(url, { timeout: 10_000, retryCount: 2, retryDelay: 400 }),
|
||||
transport: fallback(
|
||||
urls.map((url) => http(url, { timeout: 10_000 })),
|
||||
{ retryCount: 2, retryDelay: 400 },
|
||||
),
|
||||
})
|
||||
|
||||
/** error text safe to log — the RPC url (secret) is redacted */
|
||||
const redact = (e: unknown) =>
|
||||
String(e instanceof Error ? `${e.name}: ${e.message.split('\n')[0]}` : e)
|
||||
.replaceAll(url, '<rpc>')
|
||||
export const safeError = (e: unknown) =>
|
||||
urls
|
||||
.reduce(
|
||||
(text, url) => text.replaceAll(url, '<rpc>'),
|
||||
String(e instanceof Error ? `${e.name}: ${e.message.split('\n')[0]}` : e),
|
||||
)
|
||||
.slice(0, 120)
|
||||
|
||||
// loose call shape — abi fragments come from parseAbi, results are narrowed by ok<T>()
|
||||
@@ -49,7 +55,7 @@ export async function mc(calls: Call[]): Promise<McRes[]> {
|
||||
try {
|
||||
out.push(...(await agg(chunk)))
|
||||
} catch (e) {
|
||||
log('[rpc] chunk failed, retrying:', redact(e))
|
||||
log('[rpc] chunk failed, retrying:', safeError(e))
|
||||
await sleep(600)
|
||||
try {
|
||||
out.push(...(await agg(chunk)))
|
||||
@@ -59,7 +65,7 @@ export async function mc(calls: Call[]): Promise<McRes[]> {
|
||||
try {
|
||||
out.push(...(await agg(part)))
|
||||
} catch (e2) {
|
||||
log(`[rpc] dropped ${part.length}-call sub-chunk:`, redact(e2))
|
||||
log(`[rpc] dropped ${part.length}-call sub-chunk:`, safeError(e2))
|
||||
out.push(...part.map(() => ({ status: 'failure' as const })))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user