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 table | Written when | Key columns |
|---|---|---|
pair_vault_fundings | Settlement asset is credited to a Pair Vault | source, amount_raw, usd_value, tx_signature |
pair_settlements | A settlement runs, whether it completes or is blocked | sequence, status, route_mode, settlement_amount_raw, plan (the full Pair Router plan), error |
pair_purchases | The Pair Router’s acquisition executes | input_mint, input_amount_raw, input_usd, output_amount_raw, output_usd, execution_price_usd, tx_signature, status |
reward_epochs | A reward epoch is allocated | epoch_number, start_at, end_at, methodology, total_pool_raw, total_weight, allocation_root |
reward_allocations | One row per eligible wallet in an epoch | wallet, weight, amount_raw, status (claimable, claimed, expired), claim_id |
reward_claims | A holder claims | wallet, 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.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.
- 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.
- 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.
- Pair Asset outpair_purchases
Input amount and USD, output units and USD, execution price, transaction signature.
- Vaultpair_vaults
Settlement balance returns to zero; the Pair Asset balance grows by the units acquired.
- Reward epochreward_epochs · reward_allocations
Pool, total weight, Merkle root, and one allocation per eligible wallet.
- 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.
| Line | Value | Ledger source |
|---|---|---|
| Settlement ID | 184 | pair_settlements.sequence |
| Settlement received | $14,220.00 | pair_settlements.settlement_amount_usd = pair_purchases.input_usd |
| Execution | $14,186.21 | pair_purchases.output_usd |
| Fees (execution cost) | $33.79 | input_usd − output_usd |
| Pair Asset received | 551.77 units | pair_purchases.output_amount_raw |
| Allocated | 551.77 units | Σ reward_allocations.amount_raw for the epoch |
| Claimed | 341.09 units | Σ allocations with status claimed |
| Outstanding | 210.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.
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.
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_rawexactly. 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_marketsequals the result ofrecomputeFromLedger: acquired, allocated, claimed, unallocated and claimable. The integration suite asserts this for every market. - Claims pay once
- A claim moves each allocation from
claimabletoclaimedinside 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:
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.
- Step 01ADDRESSOpen the treasury
The treasury wallet is
5XAwPtXHkA2F68tEj5ZAZePJfJWH4tzGkeQiqK5ggfRg. Its full history is public on any Solana explorer. - Step 02GET /api/markets/[mint]/feesRead a market’s sharing config
The endpoint reads the config from chain and returns its address, shareholders, and status:
locked,pendingormismatch. The same account can be read directly from the Pump fees program. - Step 03distribute_creator_feesCheck 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.
- Step 04SYSTEM TRANSFERCheck 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.
| Panel | Ledger figure | Onchain check | Status |
|---|---|---|---|
| Pair Market | Registry row: mint, bonding curve, instrument | Mint and bonding-curve accounts exist; launch signature matches | Planned |
| Fee split | configuration.feeSplit | Sharing config shareholders and adminRevoked flag | Planned |
| Vault | pair_vaults balances and custody model | Vault token accounts for the settlement asset and the Pair Asset | Planned |
| Settlement transactions | pair_settlements and pair_purchases | Each purchase signature, its input and output amounts | Planned |
| Pair Asset balance | acquired − claimed | Vault Pair Asset token balance | Planned |
| Reward epochs | allocation_root and allocations | Merkle root recomputed in the browser; proof verified for any wallet | Planned |
| Claims | reward_claims | Claim transactions against the distributor | Planned |
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.