Solana Token Market Go – Go의 온체인 풀 지표(가격, Liq, mcap, FDV)

hackernews | | 🔬 연구
#review
원문 출처: hackernews · Genesis Park에서 요약 및 분석

요약

솔라나 블록체인 기반 토큰 시장 지표를 계산하는 Go SDK가 출시됐다. 이 라이브러리는 지정된 DEX와 풀 버전, 토큰 페어 정보를 기반으로 가격, 유동성, 공급량, 시가총액, 완전희석가치(FDV) 등을 온체인에서 직접 산출하며 pumpfun, raydium, meteora, orca 등 주요 DEX를 지원한다. RPC 호출 타임아웃과 재시도 정책, 폴백 엔드포인트 패턴을 체계적으로 구성해 GetMetricsByPool 평균 지연 시간을 20~30% 절감하는 것이 목표이며, Geyser gRPC 스트림과 웹소켓을 활용한 실시간 아키텍처도 제안하고 있다.

본문

On-chain-first Go SDK for deterministic Solana token market metrics. This SDK focuses on a single responsibility: compute price, liquidity, supply, market cap, and FDV for an explicit (DEX, pool version, mintA, mintB, pool address) route. - No opaque discovery dependency for core metrics paths - Deterministic calculator routing by Dex + PoolVersion - Protocol-native account decoding for each supported market - Production-oriented: strict tests, strict coverage gate, extensible architecture go get github.com/TokensHive/solana-token-market-go market.NewClient(...) client.GetMetricsByPool(ctx, request) client.LastRequestDebug() market.WithPoolCalculatorFactory(route, factory) for custom DEX integrations market.GetMetricsByPoolRequest accepts: | Field | Type | Description | |---|---|---| Pool.Dex | market.Dex | Top-level DEX family (pumpfun , raydium , meteora , orca , or your custom value). | Pool.PoolVersion | market.PoolVersion | Protocol/pool implementation identifier inside the DEX. | Pool.MintA | solana.PublicKey | Base token mint to price and value. | Pool.MintB | solana.PublicKey | Quote token mint. Usually SOL /WSOL for direct SOL quoting. | Pool.PoolAddress | solana.PublicKey | Pool account address for the exact route. | market.GetMetricsByPoolResponse includes: | Property | Meaning | |---|---| PriceOfAInB | Spot price of MintA denominated in MintB . | PriceOfAInSOL | Spot price of MintA normalized to SOL. | LiquidityInB | Total two-sided pool liquidity expressed in MintB units. | LiquidityInSOL | Total two-sided pool liquidity normalized to SOL. | TotalSupply | Token total supply used for analytics. | CirculatingSupply | Circulating supply from configured supply provider. | MarketCapInSOL | PriceOfAInSOL * CirculatingSupply . | FDVInSOL | PriceOfAInSOL * fdv_supply . | SupplyMethod | Supply derivation strategy label. | Metadata | Protocol-specific diagnostics: reserves, raw fields, decimals, fee data, FDV method/supply, source labels. | | Pool Version | Program ID (Mainnet) | IDL/Layout | Operations | |---|---|---|---| bonding_curve | 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P | pump.json | GetMetricsByPool | pumpswap_amm | pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA | pump_amm.json | GetMetricsByPool | | Pool Version | Program ID (Mainnet) | IDL/Layout | Operations | |---|---|---|---| liquidity_v4 | 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 | raydium_amm/idl.json | GetMetricsByPool | cpmm | CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C | raydium_cp_swap.json | GetMetricsByPool | clmm | CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK | amm_v3.json | GetMetricsByPool | launchpad | LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj | layout.ts , states.rs | GetMetricsByPool | | Pool Version | Program ID (Mainnet) | IDL/Layout | Operations | |---|---|---|---| dlmm | LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo | dlmm.json | GetMetricsByPool | dbc | dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN | dynamic-bonding-curve idl.json | GetMetricsByPool | damm_v1 | Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB | damm-v1 idl.ts | GetMetricsByPool | damm_v2 | cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG | cp_amm.json | GetMetricsByPool | | Pool Version | Program ID (Mainnet) | IDL/Layout | Operations | |---|---|---|---| whirlpool | whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc | Whirlpool IDL docs | GetMetricsByPool | client, err := market.NewClient( market.WithRPCClient(rpc.NewSolanaRPCClient("https://api.mainnet-beta.solana.com")), market.WithDebugRequests(true), ) if err != nil { panic(err) } resp, err := client.GetMetricsByPool(ctx, market.GetMetricsByPoolRequest{ Pool: market.PoolIdentifier{ Dex: market.DexPumpfun, PoolVersion: market.PoolVersionPumpfunBondingCurve, MintA: solana.MustPublicKeyFromBase58("9BHt7aq3DFCb74kZjPY5epgVtsWKCeYX1tUWxYwDpump"), MintB: solana.SolMint, PoolAddress: solana.MustPublicKeyFromBase58("2ebnjcJ5f8V6NhvWQfagfo6f6Mpn8jL42x8UxKM4Gz8H"), }, }) if err != nil { panic(err) } fmt.Println(resp.PriceOfAInSOL, resp.LiquidityInSOL, resp.MarketCapInSOL, resp.FDVInSOL) Interactive mode: go run ./examples Batch mode: go run ./examples -interactive=false Useful flags: -rpc : custom RPC endpoint-timeout : request timeout (for example60s )-debug : include/excludeLastRequestDebug() output - Target: reduce average GetMetricsByPool latency by 20-30% on common pools. | Call Type | Timeout Target | Retry Strategy | Fallback Pattern | Notes | |---|---|---|---|---| GetAccount | 700ms to 1200ms | 2 retries, exponential backoff with jitter (100ms , 250ms ) | switch RPC endpoint on timeout/5xx | used for primary pool account reads | GetMultipleAccounts | 900ms to 1500ms | 2 retries, chunk-aware retries | fallback endpoint + reduce chunk size | highest impact on latency; optimize batching | GetTokenSupply | 600ms to 1000ms | 1 retry | fallback endpoint | usually lightweight but required for supply pipeline | GetSignaturesForAddress | 1200ms to 2500ms | 1 retry | fallback endpoint | mostly for optional workflows, not core metrics path | GetTransaction / GetTransactionRaw | 1500ms to 3000ms | 1 retry | fallback endpoint + lower concurrency | heavy payload path; keep isolated from metrics fast path | Recommended defaults: - keep short request deadlines ( context.WithTimeout ) - run independent RPC calls concurrently - limit in-flight RPC fanout per request - use failover RPC client for endpoint fallback - emit request telemetry and monitor P50/P95/P99 latencies type Server struct { client *market.Client } func (s *Server) HandleMetrics(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second) defer cancel() req := decodeRequest(r) // parse dex/poolVersion/mints/poolAddress resp, err := s.client.GetMetricsByPool(ctx, req) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } json.NewEncoder(w).Encode(resp) } func runIndexer(ctx context.Context, client *market.Client, pools []market.PoolIdentifier) error { ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() for { select { case impacted pools // 2) enqueue recompute jobs } func worker(ctx context.Context, client *market.Client, jobs <-chan market.PoolIdentifier) { for pool := range jobs { req := market.GetMetricsByPoolRequest{Pool: pool} resp, err := client.GetMetricsByPool(ctx, req) if err == nil { publishRealtimeStats(pool, resp) // websocket/kafka/redis stream } } } Suggested realtime architecture: - ingest: Geyser gRPC account stream - routing: account-to-pool impact map - compute: bounded worker pool calling GetMetricsByPool - output: websocket broadcast + durable store for charts/stats Use the route registry extension point: client, err := market.NewClient( market.WithRPCClient(rpc.NewSolanaRPCClient("https://api.mainnet-beta.solana.com")), market.WithPoolCalculatorFactory( market.PoolRoute{Dex: "my_dex", PoolVersion: "my_pool_v1"}, func(cfg market.Config) market.PoolCalculator { return myCalculator{cfg: cfg} }, ), ) See detailed extension guides:

Genesis Park 편집팀이 AI를 활용하여 작성한 분석입니다. 원문은 출처 링크를 통해 확인할 수 있습니다.

공유

관련 저널 읽기

전체 보기 →