mirror of
https://github.com/labrinyang/lp-terminal.git
synced 2026-08-17 06:38:04 +00:00
LP TERMINAL — terminal-style LP frontend for Robinhood Chain
Static SPA over UP33 (ve(3,3)) + the official Uniswap v2/v3 deployments: browse and add liquidity across all three, manage CL/v2 positions, one-token ZAP adds, aggregator vs native swaps, and LP-based limit orders. Ships with a zero-dependency pool indexer for uniswap discovery. Browser-wallet signing only; no key material anywhere.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
// GeckoTerminal enrichment — volume/liquidity/txn stats + token USD price
|
||||
// seeds for the pricing waterfall. GT fully covers this chain's Uniswap
|
||||
// deployments (network `robinhood`, per-dex top lists) but each list is capped
|
||||
// at 10 pages × 20 = top 200 — the long tail keeps chain-derived TVL only.
|
||||
// Free tier is 30 calls/min: calls are paced ≥ TUNE.gtPaceMs apart and the
|
||||
// whole cycle (≤30 calls) runs every TUNE.statsMs.
|
||||
//
|
||||
// NOTE: GT has no UP33 dex entry — UP33 pool stats stay on the frontend's
|
||||
// existing dexscreener path; this indexer only serves the Uniswap catalog.
|
||||
import { GT, TUNE, log, sleep } from './config'
|
||||
import { poolRow, setTokenPrice, upsertStats } from './store'
|
||||
|
||||
const LISTS = [
|
||||
{ path: '/networks/robinhood/pools', label: 'network' },
|
||||
{ path: '/networks/robinhood/dexes/uniswap-v2-robinhood/pools', label: 'uni-v2' },
|
||||
{ path: '/networks/robinhood/dexes/uniswap-v3-robinhood/pools', label: 'uni-v3' },
|
||||
]
|
||||
|
||||
type GtPool = {
|
||||
attributes?: {
|
||||
address?: string
|
||||
reserve_in_usd?: string
|
||||
volume_usd?: { h24?: string }
|
||||
transactions?: { h24?: { buys?: number; sells?: number } }
|
||||
base_token_price_usd?: string
|
||||
quote_token_price_usd?: string
|
||||
}
|
||||
relationships?: {
|
||||
base_token?: { data?: { id?: string } }
|
||||
quote_token?: { data?: { id?: string } }
|
||||
}
|
||||
}
|
||||
|
||||
let lastCall = 0
|
||||
async function gtJson(url: string): Promise<{ data?: GtPool[] } | null> {
|
||||
const wait = lastCall + TUNE.gtPaceMs - Date.now()
|
||||
if (wait > 0) await sleep(wait)
|
||||
lastCall = Date.now()
|
||||
try {
|
||||
const r = await fetch(url, { headers: { accept: 'application/json', 'user-agent': 'up33-lp-indexer/0.1' } })
|
||||
if (!r.ok) return null
|
||||
return (await r.json()) as { data?: GtPool[] }
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const num = (x: unknown): number | null => {
|
||||
const n = Number(x)
|
||||
return Number.isFinite(n) ? n : null
|
||||
}
|
||||
const tokenOfId = (id?: string): string | null =>
|
||||
id?.startsWith('robinhood_0x') ? id.slice('robinhood_'.length).toLowerCase() : null
|
||||
|
||||
function ingest(p: GtPool): boolean {
|
||||
const a = p.attributes
|
||||
const addr = a?.address?.toLowerCase()
|
||||
if (!a || !addr || !poolRow(addr)) return false // catalog is the gate — unknown pools are ignored
|
||||
const reserve = num(a.reserve_in_usd)
|
||||
const h24 = a.transactions?.h24
|
||||
const txns = h24 ? (h24.buys ?? 0) + (h24.sells ?? 0) : null
|
||||
upsertStats(addr, num(a.volume_usd?.h24), txns, reserve, 'geckoterminal')
|
||||
// token price seeds: ground truth while fresh; depth = half the pool's reserve
|
||||
const depth = (reserve ?? 0) / 2
|
||||
if (depth > 0) {
|
||||
const base = tokenOfId(p.relationships?.base_token?.data?.id)
|
||||
const quote = tokenOfId(p.relationships?.quote_token?.data?.id)
|
||||
const bp = num(a.base_token_price_usd)
|
||||
const qp = num(a.quote_token_price_usd)
|
||||
if (base && bp && bp > 0) setTokenPrice(base, bp, depth, 'gt')
|
||||
if (quote && qp && qp > 0) setTokenPrice(quote, qp, depth, 'gt')
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/** one enrichment cycle over the three GT top lists */
|
||||
export async function gtCycle(): Promise<void> {
|
||||
let matched = 0
|
||||
let seen = 0
|
||||
for (const list of LISTS) {
|
||||
for (let page = 1; page <= 10; page++) {
|
||||
const j = await gtJson(`${GT}${list.path}?page=${page}`)
|
||||
const items = j?.data
|
||||
if (!items?.length) break
|
||||
seen += items.length
|
||||
for (const it of items) if (ingest(it)) matched++
|
||||
if (items.length < 20) break
|
||||
}
|
||||
}
|
||||
log(`[stats] gt cycle: ${matched}/${seen} list entries matched catalog`)
|
||||
}
|
||||
Reference in New Issue
Block a user