Skip to content

feat(jobs): price AUDIO from the on-chain AUDIO/USDC pool, not Birdeye - #1006

Merged
rickyrombo merged 1 commit into
mainfrom
mjp-audio-price-onchain
Aug 4, 2026
Merged

feat(jobs): price AUDIO from the on-chain AUDIO/USDC pool, not Birdeye#1006
rickyrombo merged 1 commit into
mainfrom
mjp-audio-price-onchain

Conversation

@rickyrombo

Copy link
Copy Markdown
Contributor

What

Rewrites AudioPriceJob to 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 existing meteora_damm_v2 decoder, and computes AUDIO/USD via the same price_from_sqrt_price SQL function the artist_coin_prices view uses.

Why

artist_coin_stats.price for 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 outside CoinStatsJob. Sourcing it on-chain removes that dependency and is independent of the coin-stats flip — safe to ship first.

Verification (on-chain)

  • Pool Ha6tnG7LrhsTyw4tyarQ59HxAKqpdbEc2yQZp9mrDM4h decoded live: tokenA = AUDIO, tokenB = USDC, so price_from_sqrt_price(sqrt, 8, 6) is the direct AUDIO/USD (no inversion).
  • The decode lands on $0.012142, matching DexScreener/Birdeye ($0.01214). The test asserts this against the real mainnet sqrt_price.

Notes

  • Env-gated: pool lives in SolanaConfig — mainnet pool on prod/stage; dev has none, so the job no-ops (poolAddr.IsZero()).
  • Nothing downstream changes: same anchor slot (artist_coin_stats.price for AUDIO), so the view, coin_dbc.go, and get_users.sql are untouched. The anchor value barely moves (~0.2% vs Birdeye).
  • USDC pinned at $1 (negligible depeg risk).
  • Combined with the eventual coin-stats flip (retiring CoinStatsJob), this leaves Birdeye fully removed from the pricing path.

Test

New audio_price_test.go: injects the real pool sqrt_price via 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

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
rickyrombo merged commit 5049a72 into main Aug 4, 2026
2 checks passed
@rickyrombo
rickyrombo deleted the mjp-audio-price-onchain branch August 4, 2026 02:20
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant