Protocol

Accounting and Reconciliation

Every Pair figure PairStreet displays is derived from ledger rows. The figures on a Pair Market page are a read model of that ledger, and the read model can be rebuilt from the ledger at any time.

Ledger first

PairStreet records each financial step of 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. as its own row, in the order it happened, and derives every aggregate from those rows. Nothing on a market page is typed in by hand or computed in the browser.

The ledger is the record. The read model is a view of it.

Ledger tables
Ledger tableWritten whenKey columns
pair_vault_fundingsSettlement asset is credited to a Pair Vaultsource, amount_raw, usd_value, tx_signature
pair_settlementsA settlement runs, whether it completes or is blockedsequence, status, route_mode, settlement_amount_raw, plan (the full Pair Router plan), error
pair_purchasesThe Pair Router’s acquisition executesinput_mint, input_amount_raw, input_usd, output_amount_raw, output_usd, execution_price_usd, tx_signature, status
reward_epochsA reward epoch is allocatedepoch_number, start_at, end_at, methodology, total_pool_raw, total_weight, allocation_root
reward_allocationsOne row per eligible wallet in an epochwallet, weight, amount_raw, status (claimable, claimed, expired), claim_id
reward_claimsA holder claimswallet, amount_raw, usd_value, tx_signature, status

The read model sits on pair_markets (Pair Value, Pair Asset balance, lifetime acquired, holder rewards lifetime, claimable and claimed, eligible holders, settlement count) and on pair_vaults (settlement balance, Pair Asset balance, total deposited, converted, allocated and claimed). The settlement engine updates both in the same database transaction that writes the ledger rows, so a reader never sees one without the other.

Ledger mode on every row

Every financial row carries a Ledger modeLedger modeEvery financial ledger row carries live or development. Production reads and writes live rows only.: live or dev_simulated. Production reads and writes live rows only. Two guards keep the modes apart at the source: a live Pair Market never settles through a simulated route (the Pair Router adds a blocker), and an execution flagged as simulated on a live plan is refused before anything is written.

Rebuilding the read model

recomputeFromLedger rebuilds a market’s Pair aggregates purely from ledger rows. It is the audit function: the test suite runs it against every seeded market and asserts that the stored read model matches it exactly.

src/server/pair/settlement.tsrecomputeFromLedger
// src/server/pair/settlement.ts (abridged)
export async function recomputeFromLedger(pairMarketId: string) {
  // acquired  = Σ pair_purchases.output_amount_raw   where status = "confirmed"
  // allocated = Σ reward_allocations.amount_raw
  // claimed   = Σ reward_claims.amount_raw           where status = "confirmed"
  return {
    acquiredRaw, allocatedRaw, claimedRaw,
    unallocatedRaw: acquiredRaw - allocatedRaw,     // pair_markets.pair_asset_balance_raw
    vaultPairAssetRaw: acquiredRaw - claimedRaw,    // what the Pair Vault still holds
    claimableUsd: allocatedUsd - claimedUsd,        // pair_markets.holder_rewards_claimable_usd
    pairValueUsd: unallocated units × reference price,
  };
}

Only confirmed purchases and confirmed claims count. A purchase that was planned, submitted or failed contributes nothing to acquired totals, and a pending claim contributes nothing to claimed totals.

The reconciliation chain

Each SettlementSettlementOne turn of a Pair Market's flywheel: the Pair Vault balance is routed, the Pair Asset acquired, accounting finalized and a reward epoch funded. can be traced end to end through six records. Each layer references the one above it by id, so a single settlement sequence number leads to every row it produced.

Reconciliation chain for one settlementARCHITECTURE
  1. Settlement asset inpair_vault_fundings

    Each credit to the Pair Vault, with its economic source and USD value, adds to the vault’s settlement balance.

  2. Executionpair_settlements

    Sequence, route mode, amount and the full Pair Router plan, persisted for audit. Blocked plans record status failed and the blockers in error.

  3. Pair Asset outpair_purchases

    Input amount and USD, output units and USD, execution price, transaction signature.

  4. Vaultpair_vaults

    Settlement balance returns to zero; the Pair Asset balance grows by the units acquired.

  5. Reward epochreward_epochs · reward_allocations

    Pool, total weight, Merkle root, and one allocation per eligible wallet.

  6. Claimsreward_claims

    Each claim links the allocations it paid through claim_id.

Figure summary: Six layers: settlement asset in (pair_vault_fundings and the vault settlement balance), execution (pair_settlements with the router plan), Pair Asset out (pair_purchases with input, output and execution price), vault (pair_vaults Pair Asset balance), reward epoch (reward_epochs and reward_allocations with the Merkle root), claims (reward_claims linked from allocations).

Reconciling one settlement

The example below follows settlement 184 of a Pair Market paired with Tokyo Residential. Tokyo Residential is a 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. representing the target financial exposure configured for the Pair Market; the figures are an example, chosen to show how each line ties to the next.

Settlement 184: reconciliation tableEXAMPLE SETTLEMENT
Settlement 184 reconciliation
LineValueLedger source
Settlement ID184pair_settlements.sequence
Settlement received$14,220.00pair_settlements.settlement_amount_usd = pair_purchases.input_usd
Execution$14,186.21pair_purchases.output_usd
Fees (execution cost)$33.79input_usd − output_usd
Pair Asset received551.77 unitspair_purchases.output_amount_raw
Allocated551.77 unitsΣ reward_allocations.amount_raw for the epoch
Claimed341.09 unitsΣ allocations with status claimed
Outstanding210.68 unitsΣ allocations with status claimable

Figure summary: Settlement 184. Settlement received 14,220.00 dollars. Execution value 14,186.21 dollars. Execution cost 33.79 dollars. Pair Asset received 551.77 units. Allocated 551.77 units. Claimed 341.09 units. Outstanding 210.68 units.

Three identities tie the table together. The settlement side balances in dollars, the asset side in units.

$14,220.00 = $14,186.21 + $33.79
Settlement received equals execution value plus execution cost.
551.77 received = 551.77 allocated + 0.00 unallocated
Pair Asset received equals the epoch pool. In this example the epoch releases the full acquisition (epochReleaseBps 10,000 over a prior unallocated balance of zero), so the unallocated remainder is zero.
551.77 allocated = 341.09 claimed + 210.68 outstanding
Allocated equals claimed plus outstanding.

The implied execution price is $14,186.21 ÷ 551.77 ≈ $25.7104 per unit, and the execution cost is 33.79 ÷ 14,220.00 ≈ 0.24% of the settlement. The ledger stores input and output; the cost is the difference between them, so it can never drift from the two figures it is derived from.

Settlement 184: accounting waterfallEXAMPLE SETTLEMENT
$14,220.00
Settlement received
−$33.79
Execution cost
$14,186.21
Pair Asset acquired
−$8,769.55
Claimed
$5,416.66
Outstanding
Claimed and outstanding are valued at the settlement’s execution price: 341.09 × 25.7104 ≈ $8,769.55 and 210.68 × 25.7104 ≈ $5,416.66, which sum to $14,186.21.

Figure summary: Settlement received 14,220.00 dollars, minus execution cost 33.79, gives Pair Asset acquired at 14,186.21 dollars. Minus claimed 8,769.55 dollars gives outstanding 5,416.66 dollars.

Invariants

Three invariants are enforced in code and covered by tests. They are the guarantees the ledger makes today.

Σ allocations = pool
For every reward epoch, the allocations sum to total_pool_raw exactly. Shares are computed in integer base units: each wallet receives the floor of its share, and the leftover units go to the largest fractional remainders, ties broken by wallet address. No unit is created or lost to rounding.
Aggregates = ledger
The read model on pair_markets equals the result of recomputeFromLedger: acquired, allocated, claimed, unallocated and claimable. The integration suite asserts this for every market.
Claims pay once
A claim moves each allocation from claimable to claimed inside one database transaction, conditional on the allocation still being claimable. If any allocation changed underneath, the whole claim aborts. Allocations are unique per epoch and wallet.

Two further identities follow directly from the read model’s definitions:

unallocated = acquired − allocated
Unallocated Pair Asset, held for future epochs.
vault balance = acquired − claimed
Pair Asset the vault still holds.

A blocked settlement writes its pair_settlements row with status failed and leaves the settlement balance in the vault, so value received but not yet converted stays visible as the vault’s settlement balance until the next attempt. See Failure Modes.

Fee accounting on mainnet

The fee flows that run on Solana mainnet today are verifiable from chain without trusting PairStreet’s database. Two streams reach the PairStreet treasuryPairStreet treasuryThe protocol wallet that receives the protocol share and Pair Vault reserve of creator fees plus the 0.5% trading fee..

Creator fees

Every Pair Market launches with its own Pump fee-sharing config, one per mint. The Creator-fee splitCreator-fee splitEvery PairStreet launch locks its Pump creator fees on-chain: 50% creator, 25% PairStreet protocol, 25% Pair Vault reserve. Pump allows the split to be set once. sets two shareholders: the creator at 5,000 bps and the PairStreet treasury at 5,000 bps. Pump allows the split to be configured once; afterwards the config is locked (adminRevoked = true) and no account, PairStreet included, can change it.

PairStreet accounts for the treasury’s half as 25% protocol revenue and 25% Pair Vault reservePair Vault reserveThe 25% share of a Pair Market's creator fees earmarked for its Pair Vault. Held by the PairStreet treasury until the market's settlement route activates.. Because each mint has its own sharing config, every distribution identifies the Pair Market it came from, and the reserve is attributable per market. Until a market’s instrument route and vault custody activate, its reserve accrues in the treasury.

Trading fees

Trades built by PairStreet carry a 0.5% fee on the SOL side, transferred to the treasury by a System Program instruction in the same transaction as the trade. On a buy it is taken from the SOL in; on a sell, from the SOL proceeds. Pump’s own protocol and creator fees are separate and embedded in Pump’s curve math. If the treasury account is below Solana’s rent-exempt minimum, a fee too small to fund it is skipped so the trade never fails because of the fee.

  1. Step 01ADDRESS
    Open the treasury

    The treasury wallet is 5XAwPtXHkA2F68tEj5ZAZePJfJWH4tzGkeQiqK5ggfRg. Its full history is public on any Solana explorer.

  2. Step 02GET /api/markets/[mint]/fees
    Read a market’s sharing config

    The endpoint reads the config from chain and returns its address, shareholders, and status: locked, pending or mismatch. The same account can be read directly from the Pump fees program.

  3. Step 03distribute_creator_fees
    Check a distribution

    Anyone can trigger the permissionless distribution from the market page. The transaction pays the accrued fees out to both shareholders; the treasury’s share appears as an inflow from that mint’s config.

  4. Step 04SYSTEM TRANSFER
    Check a trade

    Any PairStreet-built trade lists a transfer to the treasury equal to 0.5% of its SOL side, rounded down to the lamport.

Public proof page

The ledger is designed to be checked against chain by anyone. The planned /proof page puts each ledger figure for a Pair Market next to the onchain reading it should equal, so a reader can confirm the match without an account and without trusting the database.

/proof: what each panel comparesPLANNED
Planned proof page panels
PanelLedger figureOnchain checkStatus
Pair MarketRegistry row: mint, bonding curve, instrumentMint and bonding-curve accounts exist; launch signature matchesPlanned
Fee splitconfiguration.feeSplitSharing config shareholders and adminRevoked flagPlanned
Vaultpair_vaults balances and custody modelVault token accounts for the settlement asset and the Pair AssetPlanned
Settlement transactionspair_settlements and pair_purchasesEach purchase signature, its input and output amountsPlanned
Pair Asset balanceacquired − claimedVault Pair Asset token balancePlanned
Reward epochsallocation_root and allocationsMerkle root recomputed in the browser; proof verified for any walletPlanned
Claimsreward_claimsClaim transactions against the distributorPlanned

Figure summary: Planned proof page panels. Pair Market: registry row versus mint, bonding curve and sharing config onchain. Fee split: recorded status versus the onchain shareholders and admin revoked flag. Vault: ledger balances versus the vault's token accounts. Settlement transactions: pair_purchases rows versus their transaction signatures. Pair Asset balance: acquired minus claimed versus the vault token balance. Reward epochs: allocation root versus a recomputed Merkle root and per-wallet proofs. Claims: reward_claims versus claim transactions.

  • ·Every check reads chain directly from the reader’s browser through a public RPC, so the page proves the match rather than asserting it.
  • ·Merkle leaves are SHA-256(wallet, amountRaw) in a sorted-pair tree, the same construction the reward engine already uses, so any wallet’s allocation can be verified against the epoch root. See Holder Rewards.
  • ·Panels for vaults, settlements, epochs and claims populate per market as its settlement route activates. The fee-split and treasury panels can be checked against chain today using the steps above.