- VAULT77 Community: Join on X
- Telegram (Community): @BurnWalletBroadcast
Join VAULT77 🔐77 and become part of the operator network.
TADPOLE is part of the VAULT77 🔐77 toolchain — a research and trading side project designed to explore the use of OpenAI’s APIs and SolanaTracker data to improve memecoin trading strategies. The main goals are: Small Node.js proof-of-concept for decoding Solana pool state accounts and deriving spot prices for memecoin liquidity pools. The repo is intentionally lightweight and script-first while we iterate on a more structured app.
- Decode pool state accounts using Anchor-compatible IDLs in
idl/. - Calculate spot prices from vault balances or bonding curve state (e.g., pumpfun).
- Move toward a small app that uses:
@solana/kitfor RPC clients and Solana interactionswinstonfor structured logging@solana-tracker/data-apifor SolanaTracker data access
- A SolanaTracker.io account (used for data API lookups).
- A RPC to connect to the Solana blockchain. SolanaTracker.io is the best one.
- Node.js 24 LTS and npm.
- Install dependencies:
npm install- Copy
.env.sampleto.envand fill in the variables:
cp .env.sample .envThe .env file is ignored by git. The scripts read RPC_URL, SOLANATRACKER_DATA_API_KEY, VAULT_ENCODING, and MIN_QUOTE_SOL from the environment.
VAULT_ENCODING controls how vault balances are decoded:
raw(default): subscribe with base64 and decode SPL token accounts locallyparsed: subscribe withjsonParsedand extract the parsed token amount
MIN_QUOTE_SOL sets the minimum quote reserve (default 35) required for state labeling; smaller pools are marked THIN_OR_STALE.
We use dotenvx to load (and optionally encrypt) env vars. Run scripts via the npm commands (they wrap dotenvx run --) or call dotenvx directly.
To encrypt .env, use dotenvx and keep .env.keys private (it is gitignored).
npm run app -- <TOKEN_MINT>(fetch pools via Data API, pick one, then choose snapshot or live pricing)npm run inspect -- <ACCOUNT_PUBKEY>npm run decode:pool -- <POOL_STATE_PUBKEY> <MARKET> [idlPath]npm run price:pool -- <POOL_STATE_PUBKEY> <TOKEN_MINT> <MARKET> [quoteMint] [idlPath]
Live mode prints a two-line display that refreshes in place:
- Price line (unchanged from previous behavior): timestamped spot price per base token, with slot when available and a delta vs the previous tick.
- PoolTick summary line:
STATE <name> <dir> | depth <quoteReserveUi> SOL | flow10 <net10> | flow60 <net60> | stale <seconds>s
Field meanings:
STATE <name>: state label from the pool state machine (THIN_OR_STALE, IDLE, IMPULSE, CONFIRMING, TRENDING, FAILED, or UNKNOWN).dir: arrow for direction (↑ for UP, ↓ for DOWN) or blank.depth: quote reserve in SOL (pool liquidity depth).flow10: net quote reserve change over the last 10s window.flow60: net quote reserve change over the last 60s window.stale: seconds since the last coherent snapshot for the pool.
Snapshot mode prints a single price line (no live refresh). It represents the latest on-chain snapshot for the selected pool and uses the same spot price format as live mode.
npm testruns Jest with coverage (60% global threshold overlib/, excludinglib/logger.js).
idl/contains the Anchor-compatible IDLs used for decoding.- IDLs are expected to be named after the pool
market(e.g.,idl/raydium-cpmm.json). - Decoding is driven by
idl/manifest.json, which maps eachmarketto:- the IDL file,
- the primary account name,
- and the vault/mint field names needed to derive price.
- Supported markets (current):
raydium-cpmm,meteora-dlmm,meteora-dyn-v2,meteora-curve,pump-amm,pumpfun-amm,pumpfun,raydium-launchlab. - RPC clients are built with
@solana/kit(HTTP + WSS) and log tologs/app.log. RPC_URLis used for both HTTP and WebSocket connections (include the API key in the URL).- Log level is controlled by
NODE_ENV(e.g.,developmentfor verbose logs). - Pool ticks: whenever a price line is printed (snapshot or live), the app also emits a normalized PoolTick JSON object to
logs/poolTicks.log(one line per tick). This includes spot price, reserves, staleness, rolling metrics (quote flow + depth score), and a state label.
PoolTick JSON lines written to logs/poolTicks.log follow this schema:
| Field | Type | Description |
|---|---|---|
| tsMs | int | Unix timestamp in milliseconds. |
| slot | int|null | Solana slot when the tick was captured (null when unavailable). |
| market | string | Market identifier (matches idl/manifest.json). |
| poolId | string | Pool account address. |
| baseMint | string | Base token mint. |
| quoteMint | string | Quote token mint. |
| priceQuotePerBase | number | Spot price in quote units per base. |
| baseReserveUi | number|null | Base reserve in UI units. |
| quoteReserveUi | number|null | Quote reserve in UI units. |
| reserveSource | string | Source of reserves (vault/bonding curve). |
| stalenessMs | int | Time since last coherent snapshot for the pool. |
| metrics | object | Rolling flow metrics and depth score. |
| metrics.netQuoteFlow_10s | number|null | Net quote reserve change over 10s window. |
| metrics.netQuoteFlow_60s | number|null | Net quote reserve change over 60s window. |
| metrics.netQuoteFlow_300s | number|null | Net quote reserve change over 300s window. |
| metrics.flowVol_10s | number|null | Total absolute flow over 10s window. |
| metrics.flowVol_60s | number|null | Total absolute flow over 60s window. |
| metrics.depthScore | number|null | Log-based depth score for quote reserve. |
| state | object | State label from the pool state machine. |
| state.name | string | One of: THIN_OR_STALE, IDLE, IMPULSE, CONFIRMING, TRENDING, FAILED. |
| state.dir | string|null | Direction of the move: UP, DOWN, or null. |
| state.reasons | string[] | Short reasons explaining the state decision. |
- Drop the IDL in
idl/<market>.json(use the Data APImarketstring). - Add an entry to
idl/manifest.jsonwithaccountName,vaultFields, andmintFields. - If a market stores quote vault but not quote mint in pool state, set
"quoteMintFromPool": truein the manifest entry. - Run
npm run app -- <TOKEN_MINT>and select a pool for that market to validate.
- VAULT77 Community: Join on X
- Telegram (Community): @BurnWalletBroadcast
Join VAULT77 🔐77 and become part of the operator network.