Skip to content

Releases: capcaprail/CapRail

v0.2.0 — a secondary market that checks the buyer before the match

Choose a tag to compare

@capcaprail capcaprail released this 03 Oct 15:24

What's new

A secondary market that checks the buyer before the match — a holder offers shares at a fixed price, only wallets the token would admit see the offer, and shares and payment move in one transaction with the platform fee shown before signing.

Details

  • Offers from the investor cabinet: fixed price per share, no escrow — the shares stay in the seller's wallet, delegated to the offer; cancel at any time.
  • The storefront shows an offer only to wallets that would pass admission for that token right now; everyone else sees nothing.
  • Accept the whole offer or part of it: shares through the transfer hook, payment to the seller, the fee to the platform — all or nothing. The fee in the form is the fee the program charges (100 bps on devnet, demo stablecoin dUSD).
  • A buyer revoked after the offer was posted is refused by the network before any balance moves.
  • An offer the seller's account no longer backs is marked stale, with the reason and what can still be taken.
  • The company panel lists every offer of its tokens and shows each trade in the journal with its price, payment and fee — live.
  • Hosted on the owner's accounts: the panel on GitHub Pages (a landing page at the root, the app under /app/), the api with the indexer on Render, the index in Supabase.

Measured on devnet: 0 one-sided trades of 100 (20 broken on purpose), 0 matches for 200 non-admitted buyers (program tests); through the hosting, cap table p95 0.74 s and refusal in the journal p95 0.80 s.

Not yet: vesting (v0.3.0), right of first refusal (v0.4.0), the compliance report (v0.5.0). Admission is set inside the product, with no external KYC.

Files changed

  • programs/caprail — platform config, create_offer, cancel_offer, accept_offer
  • packages/chain, packages/indexer, apps/worker — offer builders, market events, the stale sweep
  • apps/api — company book, storefront, /me, trades in the journal, live offer events
  • apps/web — investor cabinet, storefront, the company's book; apps/landing — the site's landing page
  • render.yaml, .github/workflows/pages.yml — the hosting

Migration

⚠️ Database migrations 0003–0005 (pnpm --filter @caprail/db db:migrate) — already applied to the production database. Devnet programs were upgraded on 2026-10-03; the platform is initialised.

v0.1.0 — the rule is enforced by the network

Choose a tag to compare

@capcaprail capcaprail released this 17 Sep 13:00

First public release: milestone M1. A private company issues an equity token on Solana whose transfer restrictions are enforced by the token itself — a Token-2022 mint with a transfer hook — and a panel whose cap table and journal are read from the chain.

What's in

  • Two programs on devnet — caprail (state and actions: company, token, policy, registry, distribution) at As8C4JwSGHd7HPvh5KD1FhhLsQphQ8veSdhipiSRWs7g, and caprail-hook (the rule; reads only) at 6EMZVfUkf2wrtwfnESLghWfdWyzDu71uJTJ7dCKG3YEi. The deployed bytecode is verified byte for byte against the build.
  • Admission enforced on every transfer. A transfer to a wallet that is not admitted, whose admission expired or was revoked is refused by the network — from the panel, from a script, from any wallet — with the reason in the logs (NotAccredited, AccreditationExpired). Balances stay unchanged.
  • Roles separated in the program: admin (company, token, policy, distribution) and compliance officer (admission statuses) are two keys; the platform holds none.
  • Company panel: create company → issue token → set policy → registry → distribute; live cap table and transfer journal over an event stream; a refused simulation is recorded in the journal next to on-chain refusals.
  • Indexer: worker → Postgres (row-level security) → API → SSE.
  • CLI demo (pnpm demo:us1) that tells the whole story and measures it; the exit code is the verdict.
  • CI: Rust (fmt, clippy, SBPFv0 build, tests under mollusk, lock pinned), Node gate, secrets and trace sweep over the whole history.
  • Panel on GitHub Pages: https://capcaprail.github.io/CapRail/ (sign-in needs the API host — see below).

Measured on devnet

Budget Measured
Transfers to a non-admitted wallet refused 100 of 100 100 of 100
Transfers to an admitted wallet passed ≥ 99 of 100 100 of 100
Cap table updated after confirmation ≤ 5 s p95 1.45 s
Refusal in the journal with the right reason ≤ 5 s p95 1.14 s
End-to-end demo ≤ 3 min 18.7 s
Cost of a compliant transfer to the sender ≤ 0.001 SOL 0.000005 SOL
Status change applies to the next transfer ≤ 10 s ≤ 20 slots (≈ 4 s)

Not in this release

  • Secondary market and offers (v0.2), vesting (v0.3), right of first refusal (v0.4), compliance report (v0.5). The investor cabinet and market screens are prototypes on mock data and say so.
  • Admission status is set by a person inside the product; there is no external KYC. The release proves the rule is enforced by the network, not where the status came from.
  • The API and the worker are not hosted yet; the Pages panel connects once they are (v0.2).
  • A wallet that does not attach the transfer hook's extra accounts fails before the rule runs — a property of the wallet, not of the token.

Running it

See the README: Node 22, pnpm 9.15, a Postgres (Supabase) and a devnet RPC endpoint — nothing to build on chain.

✅ No migration needed for a fresh database (pnpm --filter @caprail/db db:migrate creates the schema).