|
| 1 | +# export-creator-liquidity |
| 2 | + |
| 3 | +Exports two CSVs that snapshot every Gnars DAO member's Zora-created coins and |
| 4 | +their pool liquidity, sourced from GeckoTerminal. The intended use is to |
| 5 | +prioritize creator outreach for the upcoming liquidity migration to the new |
| 6 | +Gnars token on Clanker. |
| 7 | + |
| 8 | +## Run |
| 9 | + |
| 10 | +```bash |
| 11 | +# from the main repo (where node_modules + .env.local live) |
| 12 | +pnpm tsx scripts/export-creator-liquidity.ts |
| 13 | +``` |
| 14 | + |
| 15 | +CSVs land in `output/`. Intermediate JSON caches land in `output/cache/`. To |
| 16 | +force a refresh of one stage, delete that stage's cache directory: |
| 17 | + |
| 18 | +```bash |
| 19 | +rm -rf output/cache/profile-coins # re-fetch Zora coins per member |
| 20 | +rm -rf output/cache/gecko-pools # re-fetch pool TVL |
| 21 | +rm -f output/cache/members.json # re-fetch DAO members |
| 22 | +rm -f output/cache/farcaster.json # re-fetch Farcaster profiles |
| 23 | +rm -f output/cache/ens.json # re-fetch ENS names |
| 24 | +``` |
| 25 | + |
| 26 | +Hitting `Ctrl+C` is safe — every stage persists progress, so a rerun resumes |
| 27 | +from where it stopped. |
| 28 | + |
| 29 | +## Required env (in `.env.local`) |
| 30 | + |
| 31 | +| Variable | Required? | Notes | |
| 32 | +| ------------------------------ | --------- | ---------------------------------------------------- | |
| 33 | +| `NEXT_PUBLIC_GOLDSKY_PROJECT_ID` | yes | Builder DAO subgraph | |
| 34 | +| `NEXT_PUBLIC_ZORA_API_KEY` | recommended | raises Zora API rate limits | |
| 35 | +| `ALCHEMY_API_KEY` | recommended | mainnet ENS reverse lookup | |
| 36 | +| `NEYNAR_API_KEY` | optional | Farcaster username, bio, follower count, Twitter via verified accounts | |
| 37 | +| `GECKOTERMINAL_API_KEY` | optional | paid tier; without it the run is throttled to ~30 req/min | |
| 38 | + |
| 39 | +Without an Alchemy or Neynar key, those columns are blank — the run still |
| 40 | +completes. |
| 41 | + |
| 42 | +## Stages |
| 43 | + |
| 44 | +1. **Members** — paginates `daotokenOwners` from the Builder DAO subgraph. |
| 45 | +2. **ProfileCoins** — `getProfileCoins` per member, paginates via `after` cursor up to 10 pages × 50 coins. |
| 46 | +3. **Pools** — GeckoTerminal `/networks/base/tokens/{coin}/pools` for each unique coin, picks the pool with the highest `reserve_in_usd`. |
| 47 | +4. **Creators** — Neynar `bulk-by-address` and viem mainnet `getEnsName` only for members who have ≥1 created coin. |
| 48 | +5. **CSVs** — aggregates per creator, writes both files with a timestamp suffix. |
| 49 | + |
| 50 | +## Output files |
| 51 | + |
| 52 | +### `creators-YYYYMMDD-HHMMSS.csv` |
| 53 | + |
| 54 | +One row per creator who has at least one created coin. Sorted by `total_tvl_usd` desc. |
| 55 | + |
| 56 | +| Column | Notes | |
| 57 | +| -------------------------- | -------------------------------------------------------------------- | |
| 58 | +| `creator_address` | DAO member address (lowercase) | |
| 59 | +| `creator_ens` | mainnet ENS name (if any) | |
| 60 | +| `creator_farcaster` | Neynar-resolved Farcaster username | |
| 61 | +| `creator_twitter` | Zora-linked Twitter > Farcaster verified accounts > coin metadata | |
| 62 | +| `creator_email` | best-effort regex on Farcaster bio + Zora handles (often blank) | |
| 63 | +| `creator_gnars_held` | NFT count from subgraph | |
| 64 | +| `creator_delegate` | who they delegated to (lowercase) | |
| 65 | +| `coins_count` | total created coins matched to this creator | |
| 66 | +| `coins_with_liquidity` | coins where pool TVL > $0 | |
| 67 | +| `total_tvl_usd` | sum of `reserve_in_usd` across all their pools | |
| 68 | +| `gnars_paired_tvl_usd` | sum of TVL where pool currency is the Gnars creator coin | |
| 69 | +| `top_coin_symbol` | symbol of the coin with the highest TVL | |
| 70 | +| `top_coin_tvl_usd` | TVL of that top coin | |
| 71 | +| `migration_priority_tier` | `high` (≥$10k), `med` (≥$1k), `low` (>$0), `none` | |
| 72 | + |
| 73 | +### `coins-YYYYMMDD-HHMMSS.csv` |
| 74 | + |
| 75 | +One row per created coin. Sorted by `is_gnars_paired` desc, then `pool_tvl_usd` desc. |
| 76 | + |
| 77 | +| Column | Notes | |
| 78 | +| --------------------------- | ---------------------------------------------------------------------- | |
| 79 | +| `creator_address` | | |
| 80 | +| `creator_ens` | | |
| 81 | +| `creator_farcaster` | | |
| 82 | +| `coin_address` | Zora content coin address (lowercase) | |
| 83 | +| `coin_name` | | |
| 84 | +| `coin_symbol` | | |
| 85 | +| `coin_created_at` | ISO timestamp from Zora | |
| 86 | +| `is_gnars_paired` | `true` if pool currency address = `0x0cf0c3b75d522290d7d12c74d7f1f0cc47ccb23b` | |
| 87 | +| `has_liquidity` | `true` if pool `reserve_in_usd` > 0 | |
| 88 | +| `pool_address` | Uni V4 / Uni V3 pool | |
| 89 | +| `pool_url` | GeckoTerminal explorer link | |
| 90 | +| `backing_currency_symbol` | from Zora `poolCurrencyToken.name` falling back to Gecko quote symbol | |
| 91 | +| `backing_currency_address` | | |
| 92 | +| `pool_tvl_usd` | sum of both sides of the pool, USD | |
| 93 | +| `base_reserve_usd` | one-side approx (≈ TVL/2; Gecko doesn't expose exact split) | |
| 94 | +| `quote_reserve_usd` | one-side approx (≈ TVL/2) | |
| 95 | +| `market_cap_usd` | from Gecko, falling back to Zora | |
| 96 | +| `volume_24h_usd` | | |
| 97 | +| `total_volume_usd` | lifetime, from Zora | |
| 98 | +| `price_usd` | Zora `tokenPrice.priceInUsdc` | |
| 99 | +| `holders_count` | Zora `uniqueHolders` | |
| 100 | +| `fee_bps` | pool fee in basis points | |
| 101 | +| `dex` | Gecko DEX id (e.g. `uniswap-v4`) | |
| 102 | +| `zora_url` | `https://zora.co/coin/base:{address}` | |
| 103 | + |
| 104 | +## Caveats |
| 105 | + |
| 106 | +- **Per-side reserves are approximations.** GeckoTerminal exposes the total |
| 107 | + pool USD reserve but not the split. The script reports `≈ TVL/2` for each |
| 108 | + side; this is accurate for balanced V3/V4 ranges but skewed for one-sided |
| 109 | + Doppler-style launches. For migration decisions, use `pool_tvl_usd` as the |
| 110 | + authoritative number. |
| 111 | +- **Creator filter.** The script only includes a coin if `coin.creatorAddress` |
| 112 | + matches the DAO member's address. Members who hold Gnars NFTs at one address |
| 113 | + but deploy Zora coins from a different wallet will be missed. If that |
| 114 | + becomes a gap, drop the `if (creatorAddr !== m.owner) continue;` check in |
| 115 | + `buildCoinRows` and let coins flow under the address Zora reports. |
| 116 | +- **Email column is best-effort.** Zora and Farcaster don't expose email |
| 117 | + directly; the script regex-scans bio text. Most rows will be blank. |
| 118 | +- **Free-tier GeckoTerminal is slow.** Expect ~3–5 minutes per 100 unique |
| 119 | + coins on the free tier. Set `GECKOTERMINAL_API_KEY` for a paid tier if you |
| 120 | + need it faster. |
0 commit comments