Investigate Bitget as an additional USTC/LUNC external oracle source #520
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#520
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Investigate whether Bitget should be added as a fourth poll source for the indexer external USD oracle (
ustc/lunc), alongside KuCoin, MEXC, and CoinGecko.This is a research / go-no-go ticket, not an implement-by-default request. Feeds remain advisory (invariant X5) — not settlement.
Related: #515 (ticker-scoped routes + distinct CEX symbols). Runbook:
docs/runbooks/indexer-external-oracle.md. Fetcher:indexer/src/indexer/oracle.rs.Why look at Bitget
Today
fetch_all_sourcesis a simple average of whatever KuCoin + MEXC (+ CoinGecko on alternate ticks) return. Quality and availability degrade when:Bitget is a large spot venue that already lists both TerraClassic pairs on a public, unauthenticated ticker API. A live probe (2026-08-15) confirmed both symbols resolve and print in the same order of magnitude as KuCoin/MEXC:
USTCUSDT/LUNCUSDTUSTC-USDT/LUNC-USDTUSTCUSDT/LUNCUSDTCandidate public endpoints (no API key):
GET https://api.bitget.com/api/v2/spot/market/tickers?symbol=USTCUSDT— fieldlastPrGET https://api.bitget.com/api/v3/market/tickers?category=SPOT&symbol=USTCUSDT— fieldlastPriceDocs: Bitget Get Tickers (v3). Success
codeis"00000".Investigation questions
USTCUSDTandLUNCUSDTare the TerraClassic pairs (not a namesake / wrapped / futures contract). Reject any cross-wire of LUNC into USTC storage.sum / nwith no median or outlier clip. Adding Bitget changes the average and may double-count if CoinGecko already includes Bitget. Decide: add as a CEX peer, replace CoinGecko on some ticks, or introduce median / max-deviation before adding any venue.data, non-"00000"codes, non-finite prices (X6), 429 mapping (reuseOracleError::RateLimited). ToS / scraping constraints for unauthenticated polling at the existing oracle interval.sources[]onGET /api/v1/oracle/price/{ticker}would grow abitgetrow. Confirm Protocol UI / integrators do not assume a fixed source set. No API contract change if we only add a source name.Go / no-go
Go if Bitget symbols are TerraClassic, public ticker is stable, 24h volume is competitive with (or better than) the weaker of KuCoin/MEXC, and we have a written aggregation rule that does not let a thin print dominate the mean.
No-go / defer if volume is persistently thin, prints diverge from KuCoin/MEXC by more than a small band without a reject rule, v2/v3 is deprecated or geo-blocked from our indexer hosts, or CoinGecko already covers Bitget well enough that a fourth poll only adds correlation.
Out of scope (unless go)
oracle.rs, new unit/integration tests, runbook/skill updates,make verify-issue-515extensionSuggested investigation output
A short comment on this issue with: recommended API + symbols, volume/spread table vs KuCoin/MEXC, aggregation recommendation (mean vs median vs outlier reject), and an explicit go or no-go.
marked as related to #515
Investigation findings (2026-08-15)
Research probe from a US egress (
187.13.182.64, Los Angeles / PacketHub). Production indexerindexer.dex.cl8y.comresolves to168.119.120.255(Hetzner, Nuremberg, DE). Single-day live snapshot (~09:39–09:41 UTC), not a multi-day volume series.Verdict: no-go / defer as a fourth peer in today’s unweighted
sum/nmean.Conditional go only after a venue-generic aggregation change (median of CEX lasts, or max-deviation reject). Then Bitget is a reasonable third CEX vote. Do not add it to the current mean.
1. Listings / symbols (X3) — pass
USTCUSDT,LUNCUSDT(status=online)USTC/USDT,LUNC/USDTGET /api/v2/spot/public/coins?coin=USTC|LUNC→chain=Terra, deposit/withdraw enabledUST(coinId 290,transfer=false) andLUNA(coinId 280). Do not use those ids.USTCUSDT/LUNCUSDT(last ~0.004877 / 0.00004736). Coin-M / USDC-M empty.TerraClassic spot pairs are real. Any v3 client must send
category=SPOTor it can silently take a perp print.2. Recommended API
Prefer v3 (UTA), not v2.
Bitget is migrating Classic v2 → UTA v3. v2 still answers today and is spot-only (safer against the futures collision), but v3 is the documented long-term public ticker.
GET https://api.bitget.com/api/v3/market/tickersGET https://api.bitget.com/api/v2/spot/market/tickerscategory=SPOT(required) +symbol=USTCUSDT/LUNCUSDTsymbol=USTCUSDT/LUNCUSDT(no hyphen)lastPricelastPrcode == "00000"code=400172code=40034,data=nullParse: require
code=="00000", non-emptydata[0], finitelastPrice(X6). Map HTTP 429 → existingOracleError::RateLimited. Treat non-00000/ emptydataas parse/soft-fail, same as KuCoincode != 200000.No API key. Default
ORACLE_POLL_INTERVAL_MS=30000→ 2 symbols × 2 ticks/min ≈ 4 req/min, far under Bitget’s public cap.3. Rate limits vs current sources
gw-ratelimit-limit: 2000,remaining: 1998. Exceed → 429 + ~10s lockout.Retry-After.Bitget is not the rate-limit risk. CoinGecko already is — that is why it is alternate-tick +
RateLimiteddebug, not a hard outage.Authenticated Bitget UID tiers (60–250/s) and IP whitelists apply to API keys (withdraw whitelist is mandatory). Irrelevant for this unauthenticated ticker.
4. IP / geo / ToS
api.bitget.comfrom DE/US cloud ranges. Before any implement ticket: curl v3 SPOT tickers from the Coolify egress (not only from a laptop).Not a hard no-go on today’s evidence, but Germany-hosted prod + ToS prohibited list is a real ops footnote. KuCoin/MEXC public APIs also answered 200 from the same US probe.
5. Data quality vs KuCoin / MEXC / CoinGecko
24h USDT turnover (this snapshot)
Issue go-bar (“competitive with the weaker of KuCoin/MEXC”): met on this snapshot for both tickers. USTC is still thin (~$10k, same order as the issue’s own probe). MEXC is ~10× Bitget/KuCoin on USTC. Not a multi-day persistence check.
CoinGecko
/coins/{id}/tickersalready has a Bitget row for bothterrausdandterra-luna(USTC/USDT last 0.004872, LUNC/USDT last 4.744e-5). A fourth poll double-counts Bitget on even ticks if CoinGecko stays in the mean.CG also shows much larger books on Binance / LBank (USTC Binance ~$333k; LUNC LBank ~$1.7M). Out of scope here; better liquidity follow-ups than Bitget if the goal is volume.
Last vs book (3 samples, 10s apart)
KuCoin USTC last 0.00482 vs bid 0.00485 on sample 1 is the stale-print problem the ticket describes. Bitget last stayed inside a few bps of mid.
KuCoin LUNC last ~0.00004694 was ~107 bps below Bitget/MEXC (~0.00004744 / 0.00004746). Bitget vs MEXC LUNC: ~4 bps. Bitget is aligned with MEXC; KuCoin is the outlier on this window.
USTC top-of-book notional is thin on Bitget (best bid ~2.5k USTC / ~$12; best ask ~914 USTC). A single clip moves
lastPr. Same class of risk as KuCoin USTC, worse than MEXC.Mean impact if added today (sample 3 lasts)
On USTC, adding Bitget gives two thin books (KuCoin+Bitget) 2/3 weight vs MEXC. That fails the ticket’s own rule (“thin print must not dominate the mean”). On LUNC, the mean still drags toward the cheap KuCoin last; median would drop KuCoin and sit on Bitget/MEXC.
6. Failure modes (if ever implemented)
OracleError::RateLimited(reuse CoinGecko path).40034/400172/data=null→ parse/soft-fail, do not average.code != "00000"→ parse/soft-fail.lastPrice→ reject (X6); do notf64_to_bda NaN into0.dataarray (wrong category / delist) → fail closed.category=SPOTon v3.USTC-USDTon Bitget (40034).7. Ops / product
GET /api/v1/oracle/price/{ticker}sources[]is an open string list. Protocol UI maps it dynamically (ProtocolPagetable). Addingbitgetis not an API contract break.metadatacurrently names “KuCoin, MEXC, CoinGecko” inindexer/src/api/oracle.rs— docs-only string, update if a source is added.Aggregation recommendation
Do not extend
ok_prices.sum() / nwith Bitget.Written rule for a later implement ticket (venue-generic, not Bitget-specific):
average(it already embeds Bitget + many other venues).averageas the median of finite CEX lasts (or reject any print > ~50–100 bps from the median, then mean the rest).|last-mid|is large (KuCoin USTC sample 1).That rule is the actual quality fix. Bitget is optional insurance when KuCoin or MEXC 429s/dies — not a reason to overweight two ~$10k USTC books.
Go / no-go
category)sum/n)No-go / defer for implementation on current
oracle.rs.Follow-ups (not this ticket): (1) median / outlier-reject aggregation + tests; (2) optional Bitget v3 SPOT fetcher behind that rule; (3) Coolify egress probe to
api.bitget.com; (4) if the goal is liquidity, evaluate Binance/LBank before more thin CEXes.Items 1, 2, 4 approved for followup, but deferred as low priority
mentioned in issue #550
mentioned in issue #579
marked as related to #579