Developers

Events

Every state change PairStreet records about a Pair Market lands in one insert-only stream: activity_events. Each row names what happened, which objects it concerns, and the transaction that proves it.

The event row

An event is one row in activity_events. The type column is a Postgres enum; the identifier columns link the event to the token, the 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., the 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. and the wallet it concerns; usdValue and amountRaw carry the quantity; txSignature points at the Solana transaction when one exists; payload holds the type-specific fields.

src/server/db/schema.tsactivity_events
export const activityEvents = pgTable(
  "activity_events",
  {
    id: uuid("id").primaryKey().defaultRandom(),
    type: activityTypeEnum("type").notNull(),
    tokenId: uuid("token_id").references(() => tokens.id, { onDelete: "cascade" }),
    pairMarketId: uuid("pair_market_id").references(() => pairMarkets.id, { onDelete: "cascade" }),
    instrumentId: uuid("instrument_id").references(() => pairInstruments.id),
    wallet: text("wallet"),
    usdValue: numeric("usd_value", { precision: 24, scale: 6 }),
    amountRaw: numeric("amount_raw", { precision: 40, scale: 0 }),
    txSignature: text("tx_signature"),
    payload: jsonb("payload").$type<Record<string, unknown>>().notNull().default({}),
    ledgerMode: ledgerModeEnum("ledger_mode").notNull(),
    createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
  },
  (t) => [
    index("activity_created_idx").on(t.createdAt),
    index("activity_token_idx").on(t.tokenId, t.createdAt),
    index("activity_instrument_idx").on(t.instrumentId, t.createdAt),
    index("activity_wallet_idx").on(t.wallet, t.createdAt),
    index("activity_type_idx").on(t.type, t.createdAt),
  ],
);
  • ·Amounts are stored as integer base units in amountRaw; usdValue is a display figure computed at the time of the event.
  • ·Every row carries a Ledger modeLedger modeEvery financial ledger row carries live or development. Production reads and writes live rows only.. Production reads and writes live rows only.
  • ·The five indexes serve the feeds PairStreet renders: global, per token, per instrument, per wallet, and per type, each ordered by time.
  • ·Events are written inside the same database transaction as the state change they describe, so an event never exists without the state change it reports.

Event catalog

Twelve event types are defined. Three are emitted on Solana mainnet today. The settlement family is emitted by the settlement engine and goes live per Pair Instrument with its settlement route. The status column says which.

Event catalog
EventTriggerIdentifiersPayloadStatus
TOKEN_CREATEDLaunch confirmed on chain through POST /api/launch/confirm, in the transaction that registers the Pair Market.tokenIdpairMarketIdinstrumentIdwallet (creator)txSignature (launch){ symbol }Live
PAIR_SELECTEDSame database transaction, stamped 1 ms after TOKEN_CREATED. Records the Pair Instrument the creator chose.tokenIdpairMarketIdinstrumentIdwallet (creator){ instrument }Live
PAIR_REQUESTEDA counted vote on a Pair Request through POST /api/pair-requests. A repeat vote by the same voter is not counted and emits nothing.wallet (when connected){ title, count }Live
TOKEN_TRADEA trade on the coin’s market. Mainnet trades enter through the Pump program event stream.tokenIdpairMarketIdinstrumentIdwallettxSignature{ side, priceUsd }In integration
GRADUATEDThe bonding curve completes and the coin moves to PumpSwap. Mainnet graduation is already detected from the curve account and recorded on the market; the event joins the indexer stream.tokenIdpairMarketIdinstrumentId{ venue }In integration
PAIR_VAULT_FUNDEDSettlement asset credited to a Pair VaultPair VaultThe account that receives settlement value allocated to one Pair Market, holds it until conversion, and holds the acquired Pair Asset until it is allocated to holders., alongside its pair_vault_fundings row.tokenIdpairMarketIdinstrumentIdusdValue{ source }Activates per route
PAIR_ASSET_PURCHASEDThe settlement engine confirms an acquisition (runSettlement).tokenIdpairMarketIdinstrumentIdtxSignatureusdValue (output)amountRaw (units){ routeMode, priceUsd, quoteSource }Activates per route
PAIR_SETTLEMENTSame transaction, stamped 1 s after the purchase. Closes the settlement.tokenIdpairMarketIdinstrumentIdusdValue (input){ sequence }Activates per route
PAIR_REWARD_ALLOCATEDSame transaction, stamped 2 s after the purchase, when the epoch has eligible holders. The epoch is written finalized with its allocations.tokenIdpairMarketIdinstrumentIdusdValue (pool)amountRaw (pool){ epoch, holders, methodology, root }Activates per route
PAIR_REWARD_CLAIMEDA holder’s claim settles through POST /api/rewards/claim. Live claims run through the Merkle distributor.tokenIdpairMarketIdinstrumentIdwallettxSignatureusdValueamountRaw{ }Activates per route
PAIR_INSTRUMENT_ADDEDAn instrument enters the registry.instrumentId{ provider }Planned
PAIR_INSTRUMENT_PAUSEDAn instrument’s status moves to paused.instrumentId{ reason }Planned

Planned for the two instrument events means the enum values exist and the activity feed renders them; emission from the catalog sync that upserts the registry is the remaining step. The Pair Requests chapter covers the demand events in context.

Event names across the documentation

The architecture chapters describe the lifecycle with descriptive event names. Each maps to a stored type, or to a ledger row where the stored type is planned.

Lifecycle event names mapped to stored types
Lifecycle nameRecorded asStatus
PAIR_MARKET_CREATEDPAIR_SELECTED, written in the transaction that inserts the pair_markets rowLive
SETTLEMENT_OPENEDA pair_settlements row and its status; a dedicated event type is plannedPlanned
PAIR_SETTLEMENT_FINALIZEDPAIR_SETTLEMENTActivates per route
REWARD_EPOCH_FUNDEDPAIR_REWARD_ALLOCATED; the epoch is funded and allocated in one stepActivates per route
REWARD_ALLOCATEDPAIR_REWARD_ALLOCATEDActivates per route
REWARD_CLAIMEDPAIR_REWARD_CLAIMEDActivates per route
PAIR_PAUSEDThe pair_markets.status value paused; a dedicated event type is plannedPlanned
INSTRUMENT_STATUS_CHANGEDPAIR_INSTRUMENT_PAUSED covers the move to paused; a general status-change event is plannedPlanned

A settlement in the stream

One settlement produces three events, written in a single database transaction in a fixed order: the purchase, the settlement, and the reward allocation. A reader of the stream sees them together or not at all.

Event stream for one settlementEXAMPLE SETTLEMENT
  1. 12:02:14PAIR_ASSET_PURCHASEDusdValue $14,218.00 · Tokyo Residential553.01 units · priceUsd 25.7102 · routeMode ONCHAIN_SWAP
  2. 12:02:18PAIR_SETTLEMENTsequence 184$TOKYO × Tokyo Residential
  3. 12:03:02PAIR_REWARD_ALLOCATEDepoch 184 · 7,291 unitsmethodology time_weighted_balance · root <allocation-root>
Example values. The engine stamps the three rows at t, t + 1 s and t + 2 s; the times shown are illustrative. The epoch pool is the configured release share of the market’s undistributed Pair Asset, which includes units acquired in earlier settlements, so it can exceed one settlement’s purchase.

Figure summary: Example stream. 12:02:14 PAIR_ASSET_PURCHASED, 14,218 dollars of Tokyo Residential, 553.01 units at 25.7102 dollars per unit, route ONCHAIN_SWAP. 12:02:18 PAIR_SETTLEMENT, sequence 184. 12:03:02 PAIR_REWARD_ALLOCATED, epoch 184, 7,291 units, time-weighted balance, with an allocation root.

Tokyo Residential here is a Pair Instrument representing the target financial exposure configured for the Pair Market. The purchase row carries the units acquired and their value at the execution price ($14,218.00 ÷ 553.01 ≈ $25.7102 per unit); the settlement row carries the sequence that ties all three rows to pair_settlements; the allocation row carries the epoch number and the Merkle root holders verify their claims against. See Accounting for the full chain.

Reading the stream

GET /api/activity returns events newest first, with the token and instrument joined in. Production returns live rows only.

Activity query parameters
ParameterMeaning
groupall, pair, trades, launches, rewards or instruments. Each group is a fixed set of event types.
tokenMint address. Restricts to one token’s events.
instrumentInstrument slug. Restricts to one Pair Instrument.
walletWallet address. Restricts to events that name the wallet.
beforeISO timestamp cursor. Pass the previous page’s nextCursor.
limit1 to 100, default 30.
GET /api/activityresponse shape
GET /api/activity?group=pair&token=<mint>&limit=30

200 OK
cache-control: no-store

{
  "items": [
    {
      "id": "<uuid>",
      "type": "PAIR_SELECTED",
      "createdAt": "<iso-8601>",
      "wallet": "<creator-wallet>",
      "usdValue": null,
      "amountRaw": null,
      "txSignature": null,
      "ledgerMode": "live",
      "payload": { "instrument": "tokyo-residential" },
      "token": { "mint": "<mint>", "symbol": "TOKYO", "name": "<name>", "imageUrl": "<url>" },
      "instrument": { "slug": "tokyo-residential", "name": "Tokyo Residential", "shortName": "Tokyo Residential", "icon": "<icon>", "country": "JP" }
    }
  ],
  "nextCursor": "<iso-8601 of the last item, or null>"
}

Groups

Activity groups
GroupTypes
launchesTOKEN_CREATED, PAIR_SELECTED, GRADUATED
pairPAIR_VAULT_FUNDED, PAIR_ASSET_PURCHASED, PAIR_SETTLEMENT, PAIR_REWARD_ALLOCATED, PAIR_REWARD_CLAIMED
rewardsPAIR_REWARD_ALLOCATED, PAIR_REWARD_CLAIMED
tradesTOKEN_TRADE
instrumentsPAIR_REQUESTED, PAIR_INSTRUMENT_ADDED, PAIR_INSTRUMENT_PAUSED