Data Architecture
PairStreet runs across three zones. Value lives onchain. The Pair Registry, the ledger and the indexer live offchain. Prices, quotes and issuer facts arrive from external sources. This chapter maps how they connect.
Three zones
A Pair MarketPair MarketThe canonical pairing of an internet asset with a financial Pair: TOKEN × PAIR INSTRUMENT, plus the Pair Vault, configuration and accounting that connect them. is assembled from facts held in different places. The token, its bonding curve, its fee-sharing config and every wallet balance are Solana accounts. The relationship between the token and its Pair InstrumentPair InstrumentThe financial exposure a Pair Market is paired with, such as Tokyo Residential or Swiss Government Debt. A catalog object with a provider, an eligibility policy and a status., the instrument catalog, the ledger and the demand data are PairStreet records in Postgres. Market statistics, SOL pricing, instrument quotes and issuer documentation come from external services.
The three zones carry three kinds of traffic. Value movement happens only onchain: SOL, tokens and Pair AssetsPair AssetThe transferable onchain asset the implementation acquires and distributes for a Pair Instrument. Holders receive the Pair Asset, not the reference market itself. move between accounts through signed transactions. Data flows from chain and from external sources into the offchain store. Metadata, such as issuer documents and eligibility terms, describes instruments without moving anything.
Figure summary: Three columns. Onchain: Solana, Pump program, Pump fees program, PumpSwap AMM, wallets, Pair Assets as SPL tokens, claim transactions. Offchain and indexed: Pair Registry, Instrument Registry, provider metadata, indexer, Postgres ledger, reward engine, analytics, search, eligibility metadata. External: financial providers and issuers, Jupiter for quotes and SOL price, DexScreener, GeckoTerminal, issuer documentation. Solid lines are value movement, dashed lines are data, dotted lines are metadata.
Value paths inside the onchain zone
Every value movement is a Solana transaction that a wallet signs. PairStreet builds the transaction; the wallet authorizes it; the programs below execute it.
| From | To | What moves | Program |
|---|---|---|---|
| Wallet | Pump bonding curve | SOL for tokens (buy) or tokens for SOL (sell) | Pump program |
| Wallet | PairStreet treasury | 0.5% of the SOL side of a PairStreet-built trade, in the same transaction | System Program transfer |
| Pump bonding curve | Fee-sharing config | Creator fees accrued on the coin’s trades | Pump program, Pump fees program |
| Fee-sharing config | Creator wallet and treasury | Accrued creator fees, 50% / 50%, via distribute_creator_fees | Pump fees program |
| Bonding curve | PumpSwap pool | Liquidity at graduation | Pump program, PumpSwap AMM |
| Pair Vault | Holder wallet | Pair Asset claims against a reward epoch’s Merkle root | Activates per route |
What each zone holds
Onchain
PairStreet composes Pump’s programs on Solana mainnet: the Pump program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P for creation, bonding-curve trading and graduation; the Pump fees program pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ for the per-mint fee-sharing config; and the PumpSwap AMM pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA where graduated coins trade. PairStreet deploys no program of its own today. See Pump and Solana for the division of responsibilities.
Pair Assets are issuer tokens on Solana. Five instruments in the catalog reference SPL assets with live onchain routes (USDY, CETES, GILTS, XAUt0, PAXG). Pair Vault custody and claim transactions are the settlement half of the onchain zone; they activate per Pair Instrument route.
Offchain and indexed
- Pair Registry
- The
pair_marketstable: one row per Pair Market binding a token to its Pair Instrument, with status, Ledger modeLedger modeEvery financial ledger row carries live or development. Production reads and writes live rows only., configuration (fee split, reward methodology, cadence, exclusions) and read-model aggregates. - Instrument Registry
- The
pair_instrumentsandpair_providerstables, upserted idempotently from the catalog at build time. 44 instruments today, each with category, provider, mint (where one exists), status, verification and reference price. - Provider metadata
- Issuer, provider reference, transfer restrictions, price source and documentation URL per instrument.
- Indexer
- Ingestion sources with checkpoints. Every external fact enters through a source, never through per-wallet polling from the browser.
- Ledger (Postgres)
- Insert-oriented financial tables:
pair_vault_fundings,pair_settlements,pair_purchases,reward_epochs,reward_allocations,reward_claims. See Accounting. - Reward engine
- Pure functions that turn holder segments into weights, allocations and a Merkle root. Tested for exact pool allocation and proof verification.
- Analytics
- Aggregations of Pair Markets by instrument, category and country behind
/analyticsand What Is Crypto Buying? - Search
- Instrument search terms (names, countries, synonyms) and Pair Market lookup, plus Pair Requests for instruments not yet listed.
- Eligibility metadata
- Rules in
instrument_eligibility: allow or block countries and regions, KYC required, accredited only. Evaluated by the Eligibility EngineEligibility EngineEvaluates a viewer's region and verification state against a Pair Instrument's rules and returns allow or restrict before any Pair interaction..
External
External sources supply data and metadata only. None of them holds PairStreet value. Issuers such as Ondo Finance, Etherfuse, Tether Gold and Paxos publish the SPL assets that five instruments reference; PairStreet references their tokens and documentation and has no commercial arrangement with them. Jupiter supplies the SOL/USD price and the quotes for SPL instrument routes. DexScreener and GeckoTerminal supply market statistics and candles.
How a market page is assembled
A Pair Market page reads from the database first and refreshes from chain second. The database row is the read model; the refresh keeps it current. Rendering never waits on a slow external service for longer than a fixed bound.
- Step 01GET /token/[mint]Validate and read the read model
The mint is checked against the base58 address pattern.
getMarketByMintjoinstokens,token_markets,pair_marketsandpair_instruments, filtered to visible ledger modes. Production readsliverows only. - Step 02refreshLiveMarketRefresh from chain, with a bound
For a live market the page races a chain refresh against a 2.5 second timer. The refresh reads the Pump bonding-curve account, DexScreener and the SOL price in parallel, then patches
token_markets. If the timer wins, the page renders the stored row and later polls bring the fresh values. - Step 03eligibilityForRe-read and evaluate eligibility
The page re-reads the market, then evaluates the viewer’s eligibility for the instrument from the edge country header and the instrument’s rules, and loads the creator profile. These run in parallel.
- Step 04GET /api/markets/[mint]Client polling and after-response refresh
The market API responds from the read model with
cache-control: no-storeand schedules a refresh after the response is sent (Next.jsafter). List endpoints refresh up to six live markets whose data is older than 30 seconds, in the background.
Live data sources
Each source is cached in process with a short time to live. A failed fetch never blanks a value: the last good value stays in place until the next successful read.
| Source | Supplies | Cache / throttle | On failure |
|---|---|---|---|
| Pump bonding-curve account (Pump SDK over Solana RPC) | Price, reserves, market cap, liquidity, bonding progress, graduation flag | Per-mint refresh window, 10 to 30 s | Stored values remain |
| Pump fees program (sharing config) | Shareholders, admin revoked flag, fee-split status | Read at launch confirmation and on the fees endpoint | Last recorded status remains |
| DexScreener | 24h volume, 24h change, transaction count, pool address, price and market cap after graduation | 20 s | Previous columns kept |
| GeckoTerminal | OHLCV candles for the market’s pool (1m, 5m, 15m, 1h, 4h) | 45 s | Empty series; chart waits |
| Jupiter price API v3 | SOL/USD | 60 s | Last good price |
| Jupiter quote API | USDC to instrument routes, output amount, price impact, hops | 30 s | Route reports provider_unavailable with a blocker |
| Jupiter token API | Canonical asset check for SPL instruments (verified tag, token program) | 10 min | Catalog verification level used |
| Solana RPC | Launch transaction confirmation, treasury balance for the rent guard | Per request | Client retries confirmation |
| Edge geo header | Viewer country (x-vercel-ip-country) | Per request | Unknown country under geographic rules resolves to UNKNOWN_REGION in production |
Figure summary: Pump bonding-curve account: price, reserves, market cap, liquidity, bonding progress, graduation, refreshed per mint every 10 to 30 seconds. Pump fees program: fee-sharing config and split status. DexScreener: 24 hour volume, change, transactions, pool address and post-graduation price, cached 20 seconds. GeckoTerminal: OHLCV candles, cached 45 seconds. Jupiter price API: SOL/USD, cached 60 seconds. Jupiter quote API: instrument routes, cached 30 seconds. Jupiter token API: canonical asset verification, cached 10 minutes. Solana RPC: launch confirmation. Edge geo header: viewer country.
Quotes and token checks use a small in-process cache that is reserved for remote reads. Economic state is never cached this way: balances, settlements and allocations are always read from the ledger.
Indexing
Every external fact PairStreet shows enters through an indexer source. A source declares a name and an interval and processes everything after its cursor. Runs are idempotent, and each source keeps one checkpoint row with its last run time, status and last error.
// src/server/indexer/index.ts
export interface IndexerSource {
name: string;
intervalMs: number;
/** Process everything after `cursor`; return the new cursor. Must be idempotent. */
run(cursor: string | null): Promise<string | null>;
}
// One checkpoint row per ingestion source.
// indexer_cursors: source (pk), cursor, last_run_at, status ("idle" | "ok" | "error"), errorOn Vercel, background timers do not persist between invocations, so sources run as throttled, request-driven ticks scheduled after a response has been sent.
| Ingestion | Feeds | Status |
|---|---|---|
| Market-level chain refresh | Price, reserves, market cap, liquidity, progress and graduation per live market | Live |
| Fee-split reads | Per-mint fee-sharing config recorded on the Pair Market | Live |
| Pump program event stream (Helius webhook or Geyser) | Trades and holder balances decoded from Pump program events (decodeTradeEventBc, decodeCompleteEventBc), feeding live reward weights | In integration |
| Pair Vault events | Vault deposits, acquisitions and claims for custody-enabled markets | Activates per route |
Holder-level indexing is the input to reward epochs. The reward engine weights each wallet by balance held over time, so it needs every balance change with its block time, which the Pump event stream supplies.
Stack
- Application
- Next.js 16 with the App Router and React 19. Pages are server components; the API lives in route handlers under /api. The docs are served from the same application at docs.pairstreet.xyz.
- Database
- Postgres on Neon, accessed through Drizzle ORM with a small connection pool per instance. Image uploads and token metadata JSON are stored in Postgres so every serverless instance serves the same files.
- Hosting
- Vercel. Server secrets are encrypted environment variables; the edge supplies the viewer country.
- Solana
- @solana/web3.js for versioned transactions and RPC, @pump-fun/pump-sdk v2.0.0 for Pump instructions and curve math, the Solana wallet adapter for signing in the browser.
- ·Every write endpoint validates its body with a schema and rejects bodies above 64 KB.
- ·Every error returns the same envelope. See API.
- ·Every ledger row carries its ledger mode, and production queries filter to live rows. See Accounting.