feat(jobs): price AUDIO from the on-chain AUDIO/USDC pool, not Birdeye - #1006
Merged
Conversation
Rewrites AudioPriceJob to derive the AUDIO USD anchor (artist_coin_stats.price for the AUDIO mint) from the Meteora DAMM v2 AUDIO/USDC pool instead of Birdeye's /defi/price. It reads the pool account over RPC, decodes it, and computes AUDIO/USD via the same price_from_sqrt_price SQL function the artist_coin_prices view uses (base=8 AUDIO, quote=6 USDC, USDC ≈ $1). The pool (Ha6tnG7...) and its token ordering (tokenA=AUDIO, tokenB=USDC) were verified on-chain: the decode lands on $0.012142, matching DexScreener/Birdeye. Pool address is env-gated in SolanaConfig (prod/stage = mainnet pool; dev has none, so the job no-ops). Nothing downstream changes — the anchor slot is the same; it's just sourced on-chain now. Removes AudioPriceJob's Birdeye dependency. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
rickyrombo
added a commit
that referenced
this pull request
Aug 4, 2026
…1007) ## What Phase-2 cutover of the coin-stats shadow rollout: retires the Birdeye-backed `CoinStatsJob`, makes the on-chain job the source of truth, and **removes Birdeye from the service entirely**. Follows #1006 (AUDIO anchor on-chain), which was the last Birdeye call outside `CoinStatsJob`. ## Mechanism (why consumers don't change) `CoinStatsOnchainJob` already read the AUDIO anchor from `artist_coin_stats` and iterated `artist_coins`. The cutover just **repoints its upsert from `artist_coin_stats_onchain` → `artist_coin_stats`**. Every consumer — the `artist_coin_prices` view, `v1_coins`, `v1_coin_insights`, `get_users`, `coin_dbc` — keeps reading `artist_coin_stats` unchanged. (Renaming tables was rejected: Postgres views bind to table OID, so a rename would leave the view pointing at the old data.) The AUDIO row bootstrap holds: AUDIO is one of the 57 `artist_coins`, so the on-chain job upserts its row (price kept via `COALESCE`, never clobbered), and `AudioPriceJob` maintains its price from the pool. ## Birdeye removal - Delete the `birdeye/` client package, the **dead** server `BirdeyeClient` interface + field + unused `mock_birdeye_client.go`, and the `BirdeyeToken` config field. - Delete `jobs/coin_stats.go` and its indexer scheduling. - Drop the now-unused `artist_coin_stats_onchain` shadow table and `artist_coin_stats_comparison` view (**migration 0235**). - De-Birdeye the `artist_coin_prices` view comment, swagger `coin_insights` description, and stale test/model comments. (Applied migrations 0229–0231 keep their historical comments.) ## Behavior — market-cap change (verified against live prod data) Rendered stats — price, market cap, liquidity, holder, total supply, 24h change — are all on-chain-derived. The ~40 unrendered Birdeye-only columns remain in the table (NULL on new rows; consumers `COALESCE(...,0)`). Market cap moves **−8.5% in aggregate ($4.64M → $4.25M)**, and it is a **stale-price** effect on dormant coins — **not** market aggregation and **not** a supply basis. Evidence from the live `/v1/coins` payload: - **No missing markets.** Birdeye's own `numberMarkets` = 1 for **53 of 57** coins. Only AUDIO (12) and KITT/UWU/JAY (2) have more, and those match within ~2%. - **Not supply.** `totalSupply == circulatingSupply` for every coin. - **Stale last-trade price.** The 34 coins whose Birdeye market cap exceeds on-chain by >1.3× have **0 trades in 24h and average 87 days since their last trade**; Birdeye holds their last-trade quote while the on-chain value tracks the current pool. Coins that actually trade agree within **0.3%** (AUDIO within 0.2%; corroborated by DexScreener, which reads the live pool). - **Concentration.** 100% of the aggregate drop is dormant coins; the active-coin bucket is −0.3%. Every liquid coin a user would look at is effectively unchanged — dormant coins simply reprice from a months-old quote to their current pool price. ## Schema dump `sql/01_schema.sql` hand-edited to remove only the shadow table + comparison view (the committed dump was stale on unrelated merged migrations; a full regen would have pulled in `new_chain_queue` etc.). Diff is 1 comment reword + 113 deletions. Verified by reloading the dump into a fresh DB. ## Test `go build ./...`, `go vet ./...`, `go test ./jobs/` (coin-stats + audio-price) and `go test ./api/` (Coin/Wallet/Users) all pass against the regenerated DB. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.
What
Rewrites
AudioPriceJobto derive the AUDIO USD anchor from the Meteora DAMM v2 AUDIO/USDC pool instead of Birdeye's/defi/price. It reads the pool account over RPC, decodes it with the existingmeteora_damm_v2decoder, and computes AUDIO/USD via the sameprice_from_sqrt_priceSQL function theartist_coin_pricesview uses.Why
artist_coin_stats.pricefor the AUDIO mint is the anchor every coin's USD price is derived from (coin/AUDIO × AUDIO/USD). It was the last Birdeye call in the pricing path outsideCoinStatsJob. Sourcing it on-chain removes that dependency and is independent of the coin-stats flip — safe to ship first.Verification (on-chain)
Ha6tnG7LrhsTyw4tyarQ59HxAKqpdbEc2yQZp9mrDM4hdecoded live:tokenA = AUDIO,tokenB = USDC, soprice_from_sqrt_price(sqrt, 8, 6)is the direct AUDIO/USD (no inversion).sqrt_price.Notes
SolanaConfig— mainnet pool on prod/stage; dev has none, so the job no-ops (poolAddr.IsZero()).artist_coin_stats.pricefor AUDIO), so the view,coin_dbc.go, andget_users.sqlare untouched. The anchor value barely moves (~0.2% vs Birdeye).CoinStatsJob), this leaves Birdeye fully removed from the pricing path.Test
New
audio_price_test.go: injects the real poolsqrt_pricevia a fake fetcher and asserts the stored price ≈ $0.012142; plus a dev/no-pool no-op case.go test ./jobs/, build, and vet pass.🤖 Generated with Claude Code