Skip to content
0xileriPublic

About

Rung — a capital-backed valuation market for PreStocks on Solana. Built for the Stocklana hackathon.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Rung

A capital-backed valuation market for PreStocks on Solana.


Private-market investors do not think in token prices. They think in valuations:

Would I own OpenAI at $1.2T? At $1.0T? At $900B?

Spot markets cannot answer that. They show where an asset trades now, not where capital is actually willing to buy, and they give an existing holder no way to set a floor.

Rung turns those two intentions into one fully collateralized agreement.

A valuation buyer locks USDC at the private-company valuation where they would genuinely own exposure. A PreStocks holder locks the matching tokens, pays a premium, and receives the right to exchange those tokens for that USDC at any point before expiry. If they never exercise, both sides take back their own collateral and the buyer keeps the premium.

Every open commitment joins a live Commitment Curve: how much real capital sits at each valuation level. Not a poll — capital that has already been escrowed.

The name is the mark. Every bar in the logo is a rung: a valuation someone has committed at, stepping out from the line where the asset currently trades.


For judges: two minutes

Live on devnet: https://rung.up.railway.app

The devnet book is on chain, not fixtures: about fifty floors across all eight PreStocks, escrowed in program vaults by five demo makers, some already partly taken. It is mock USDC, seeded by scripts/seed-depth.ts so the sweep and the curve have depth to show; every escrow is real and every figure is read from chain.

  1. Get tokens. Switch Phantom to devnet, connect, and press Get test tokens for devnet SOL, mock USDC and a mock of every PreStock.
  2. Take a band. On Protect for OpenAI, ask for $300 of protection. It is filled from several makers' floors, cheapest premium first, in one signature. Or take just a slice of any one floor: $10 is enough.
  3. Be the maker. On an asset page, Commit across a range spreads USDC over a span of valuations in one approval. Your book then shows what holders took, what the premium paid, your capital over time, and lets you withdraw what nobody took.
  4. Walk away. A claim past its deadline settles without either side coming back: settling is open to anyone, and a keeper does it for everyone every ten minutes.
  5. Share it. Every commitment has its own page, /c/<address>, whose link unfolds on X or Telegram into a card with its live numbers. It is also a Solana Action (/api/actions/take/<address>, mapped by /actions.json): the page draws the Blink itself, so a holder takes a slice in one click, and any Blink-aware wallet can use the same endpoint.

Worth checking:

  • Slices are exact. Every slice is its own claim with its own collateral, priced pro rata against what the maker escrowed, rounded so the vault is never short. A slice must be at least $10 and may not leave less than $10 behind unless it takes everything left, so everything on the curve stays takeable. A seeded random walk of takes, refusals, exercises, withdrawals and expiries checks that every vault balances to the unit after every step.
  • The fee cannot touch collateral. The protocol takes 1% of the premium on devnet. The program caps it at 5% and only ever takes it from the premium, so a compromised admin key cannot reach anyone's collateral.
  • A new kind of price data. Each asset's capital's median floor is the valuation with half the escrowed buying capital at or below it: weighted by money, not by opinions, and counting only capital a holder could still take. It is on every asset page and served as open JSON at /api/curve and /api/curve/<SYMBOL>.
  • No oracle in settlement. Exercise is the holder's contractual right, and expiry is permissionless, so no admin switch or absent counterparty can trap collateral.
  • The real mints, handled honestly. The 48.6% multiplier trap, the epoch-scheduled transfer fee, and the issuer's powers (permanent delegate, freeze, pause, transfer hook) are dealt with in code and disclosed on screen, not ignored.
  • Tested against the real thing. 37 program tests including that random walk, 33 checks against the real OpenAI and SpaceX mints on a mainnet fork, including slices and the fee, 106 SDK tests, and 12 checks against the live devnet program itself: create, take, exercise, withdraw and expire, plus the cap and self-match guardrails.
  • Guardrails on chain. A $1,000 cap per position, a transfer-hook guard, and no self-matching, all enforced by the program.

What is new since Stocklana. Rung began at the Stocklana hackathon, where one commitment could be taken by one holder, whole. The parts that make it a market came after: slices, the sweep, ladders, the maker's book and its capital charts, the protocol fee, and the keeper. Slices needed a new account layout, so this runs as its own program; the version submitted to Stocklana is tagged stocklana-submission, and its program is still deployed on devnet. The design of slices is in docs/fills.md.


What we found in the mints

Rung is built on the actual OpenAI PreStock (PreweJ…Q3rpgF), and three of its properties break the obvious implementation. Each was verified against mainnet before a line of the program was written, and each is pinned by a test.

1. The multiplier that is a 48.6% bug. The mint carries a ScaledUiAmount extension with two values — multiplier (1) and newMultiplier (1.4861347) — and the effective timestamp for the second one has already passed. So the live multiplier is the one that is not in the obvious field. PreStocks quotes prices in scaled units, proven by reconciling the API's reported supply against raw supply × multiplier to nine decimal places. Read the wrong field and every position over-escrows by 48.6%.

$100 at a $1.00T target  →  83,365,949 raw units     ✅ multiplier-correct
                            123,893,030 raw units    ❌ the naive answer

2. A transfer fee on an epoch schedule. transferFeeConfig holds two slots and steps from 50 bps to 100 bps at epoch 1039. A vault therefore never receives what was sent, and the rate can change between quoting and signing.

Two halves to the answer. On chain, the program measures the vault's balance delta and escrows what actually arrived, then requires it to clear the maker's floor — equality is unachievable under a fee, a floor is both achievable and sufficient. Off chain, the client sizes transfers against the worse of the two slots rather than the live one, so a transaction still clears if the rate steps up before it is signed. A test asserts that sizing against the active slot fails once the fee moves, so the reasoning cannot be quietly simplified away later.

3. The collateral is not trustless, and we say so. The issuer holds permanentDelegate, freezeAuthority and a pause switch over the mint — they can move tokens out of a program-controlled vault, freeze it, or halt settlement entirely. No program can defend against that, because it is a property of the token. The UI derives this disclosure from live chain state rather than hardcoding it, so the claim tightens automatically if the issuer ever gives a power up. See docs/limitations.md.


How it works

PreStocks API  ──►  valuation context, mark price, implied valuation
                    (never authorizes a transfer)

Solana program ──►  lock · match (whole or in slices) · exercise · expire
                    PDA vaults hold both legs, one claim per taker

Chain state    ──►  the Commitment Curve, rebuilt by anyone

No oracle sits in the settlement path. The holder bought a contractual right, not a bet on a price feed, so the program never asks what anything is worth. That removes stale marks, thin prints and feed manipulation from settlement entirely.

Valuations are metadata; raw amounts are the contract. A target valuation is converted to a fixed strike once, at creation, and then frozen. A later change to the mark, the share count or the multiplier cannot rewrite an agreement already in force.

Settlement cannot be blocked. exercise_fill and expire_fill read neither the pause flag nor the market flags, and expiry is permissionless — so no admin switch and no absent counterparty can trap collateral that is owed back.

Layout

Path
programs/rung/ Anchor program — 12 instructions, PDA vaults
packages/sdk/ Token-2022 math, valuation→strike, Commitment Curve
apps/web/ Next.js app — Commitment Curve, commit and ladder, protect and sweep, positions, the maker's book
docs/limitations.md What this does not do, stated plainly
scripts/ Chain preflight, WSL toolchain, deploy

Running it

npm install
npm run test:sdk                      # 106 tests, no chain needed
bash scripts/wsl/test-local.sh        # 37 tests against a local validator, one of them a random walk
bash scripts/wsl/fork-test.sh         # every instruction against the REAL mints, on a mainnet fork
node scripts/devnet-smoke.ts          # 12 checks: every trading instruction and two guardrails, on the live devnet program
node scripts/verify-chain.ts          # re-check the mint against live mainnet

The fork test is the one that matters before touching mainnet. It loads the live OpenAI and SpaceX mint accounts (every extension intact), mainnet's Token-2022 program and feature set, and warps past the epoch where the 1% fee took effect, then runs create, accept, exercise, cancel and expire with quantities computed by the SDK exactly as the web app computes them. Nothing is sent to mainnet.

verify-chain.ts is worth running before any demo: the transfer fee is on an epoch schedule, so yesterday's numbers are not evidence.

The keeper

node scripts/keeper.ts --once --dry-run   # what is past its deadline, sending nothing
node scripts/keeper.ts --once             # settle it: one pass, for a cron job
node scripts/keeper.ts --watch 60         # or loop, backing off if the RPC does

Settling a claim after its deadline is permissionless, so nobody depends on a counterparty coming back — and nobody depends on the keeper either. It just saves people the trouble: the maker's USDC and the holder's tokens come home without either of them lifting a finger. It sends one instruction, expire_fill, which the program refuses before the deadline and which pays each side only what that claim records, so a keeper with a bug can waste its own fees and nothing else. It reads the cluster's clock rather than its own, settles the longest-waiting collateral first, and batches settlements by measured transaction size; if a batch fails because something changed under it, the claims are retried one at a time.

--rpc, --keypair and --limit (settlements per cycle, default 50) are flags, or KEEPER_RPC_URL and KEEPER_KEYPAIR in the environment. The keypair pays fees, plus rent if a recipient no longer has a token account to receive into.

On devnet it runs as a Railway cron job: one pass every ten minutes, paid for by a wallet of its own that holds devnet SOL and nothing else. Railway applies the root railway.json to every service built from this repository, so on this branch it only picks the builder; each service carries its own commands (the site: npm run build, npm start, health check on /). The keeper service's settings are listed below. A redeploy from the dashboard builds with Railpack instead, which would read the root Cargo.toml as a Rust project and ship an image without Node, so railpack.json pins its provider to Node and either builder produces a working image.

Setting Value
Source this repository, branch main (verified October 1, 2026)
Build command a no-op: the keeper runs as TypeScript directly on Node 24
Start command node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON scripts/keeper.ts --once
Cron schedule */10 * * * *
Restart policy never: the next pass is ten minutes away anyway
Watch paths scripts/keeper.ts, packages/sdk/**, package.json, package-lock.json, railway.json, railpack.json
Variables KEEPER_RPC_URL, NPM_CONFIG_PRODUCTION=false, KEEPER_SECRET_KEY

A host has no key file to point at, so KEEPER_SECRET_KEY carries the key itself, as the keypair file's JSON array. It was set from stdin and appears nowhere in this repository.

Claims last a month, so on an ordinary book the keeper has nothing to do for weeks. To watch it work, scripts/keeper-demo.ts puts one on devnet that is due in minutes:

node scripts/keeper-demo.ts <maker.json> <taker.json> --watch

The maker commits $20 at $1.00T on OPENAI and the taker takes all of it, with the deadline two minutes before a keeper pass, so the maker's book shows the claim waiting to settle before the keeper settles it. --watch then reports the settling transaction and who paid for it: the keeper's wallet, not either party's.

A book with depth

node scripts/seed-depth.ts --dry-run   # the plan and its rent, sending nothing
node scripts/seed-depth.ts             # lay it down; a re-run only fills gaps

One maker's floors on one market prove the mechanism but cannot show a sweep crossing different makers' floors or a curve whose concentration note means anything. seed-depth.ts puts floors on every listed market's curve bands, shared by five makers (the deployer and four demo wallets), sized to peak a little below the market, with premium falling as floors get deeper and terms of 21 to 60 days; a few have a slice already taken by one of two demo holders. Strikes and slices are quoted from live PreStocks marks exactly as the app quotes them. On devnet it placed 45 commitments ($8,140) and 9 slices for about 0.25 SOL of rent.

Open data

curl https://rung.up.railway.app/api/curve          # every listed asset's signal
curl https://rung.up.railway.app/api/curve/OPENAI   # one asset: its signal and every band

The same chain read and SDK arithmetic as the pages, in whole USD, with CORS open and a 30-second cache. Per band: capital committed, open commitments, distinct wallets, the largest wallet's share, the average premium and the median days to expiry.

A local stack

Devnet is shared, so changes to the program are exercised here first:

bash scripts/wsl/local-stack-up.sh <tester-address>   # validator, program, market, floors, a funded tester
NEXT_PUBLIC_CLUSTER=localnet bash scripts/wsl/dev.sh  # the web app, against it
node scripts/local-book-scenario.ts maker.json taker.json --expiring 5
bash scripts/wsl/local-stack.sh --stop

local-book-scenario.ts puts one maker's book into every state the app has to show — untouched, partly taken, partly withdrawn, expired and waiting — which is also what the keeper is tested against.

Status

Live: https://rung.up.railway.app

  • Program: 37/37 tests — full lifecycle, partial fills, both settlement paths, every refusal in the state machine, and the launch guardrails below. One of them is a seeded random walk of takes, refused sizes, exercises, withdrawals and expiries that checks every vault is exactly balanced after every step (FUZZ_SEED=<n> replays a run)
  • Mainnet fork: 33/33 checks against the real OpenAI and SpaceX mints, including partial fills and the protocol fee
  • SDK: 106/106 tests, pinned against live mainnet values
  • Every trading instruction has a UI: commit (one floor or a ladder), take a slice or sweep a band, exercise, withdraw, and settle expired claims one at a time or all at once. The one without a screen yet is close_fill, which lets a holder reclaim a settled claim's rent.

Launch guardrails

Enforced by the program, not just the interface:

  • $1,000 cap per position. Unaudited code should not be able to lose more than that on one position. Raising it takes a program upgrade, not an admin toggle.
  • Transfer-hook guard. Every PreStocks mint carries an empty transfer-hook slot the issuer can fill at any time. Rung does not pass hook accounts, so it refuses to list, open or match against a mint with a hook set, rather than take collateral it could not settle.
  • No self-matching. A maker cannot take their own commitment, so a "matched" position always has two parties.

Deployed on devnet

Program BEEraLqHNJ8y8yTsXpDZsRAjwB9uKc3Awj1YBLZUKu2r
Config EnW1LkKkaq1PdcKpHsSWs3BxvBnLPR2DEerc7XGnxtFa (fee 1% of premium, minimum slice $10)
Mock USDC CRUjjjByxTpUfeAhR377RTdpmravXXgSX6eTk93XxBov
Keeper 2HwFCWL5g7H4z8vyhrC9DXzmDbqFKAs7aDyHEiDMiSwk, a wallet that only pays settlement fees

The Stocklana submission (tag stocklana-submission) runs its own program, 6kqka5…SwpM, over the same mock mints, still deployed and left exactly as it was.

Every PreStock is listed, each against its own mock (full addresses in devnet.json):

Market Market account Mock mint Multiplier Fee
OPENAI CpbNPF…Zrx8 3Q43N1…VwR6 1.4861347 0.5%
SPACEX 2tkjQP…W4Gy 3NS9XJ…gfZD 5 1%
ANDURIL 82QCqt…6dCv BJyL2P…Yfjf 1 1%
ANTHROPIC BibrhX…K9f3 GzN2PP…QUn1 1 1%
FIGUREAI 89hm19…E9HX 8TCxon…3SGg 1 1%
KALSHI B19iok…ivtW DqX88Z…LwkG 1 1%
NEURALINK 2iw7r7…JBww BZC1uG…sxqd 1 1%
POLYMARKET 3ZcoV7…hYTw 4PLS8f…RtQK 1 1%

PreStocks exist only on mainnet, so each devnet market escrows a mock. Each carries the extensions that change the program's arithmetic: 9 decimals, a transfer fee and its real counterpart's scaled-amount multiplier (1.4861347 for OpenAI, 5 for SpaceX, 1 for the rest). Every mock but OpenAI's was built from its live mint by scripts/add-devnet-market.ts, so those also have the real 1% fee and an empty transfer-hook slot; the older OpenAI mock charges 0.5%. Neither has the pause switch or confidential-transfer extensions, which is why the fork test above exists. Mocks are labelled on every screen they appear on, and valuation data is live from the real PreStocks API throughout.

Mainnet readiness

Rung runs on devnet for judging, where anyone can try both sides for free. Mainnet has been rehearsed, but it is deliberately not live: the code is unaudited, and trying it there would need real PreStocks and USDC.

What has been verified against mainnet itself:

  • The real mints. scripts/wsl/fork-test.sh runs every instruction against the live OpenAI and SpaceX mint accounts on a local fork (33/33 checks), covering the extensions the devnet mock lacks and SpaceX's 5x multiplier.
  • Listing. node scripts/setup-mainnet.ts --dry-run reads all 8 live PreStocks mints and confirms each one can be listed against real USDC.
  • Deploying. scripts/wsl/deploy-program.sh has been rehearsed on devnet, which caught two failures before they could cost real SOL.
  • The interface. On a real mint, the app refuses to quote without live mint data, shows wallet-scaled quantities, and enforces the cap.

Launching takes about 2.1 SOL for the current 404 KB build, plus fees. That is the program account's storage deposit, which comes back if the program is ever closed. The upload buffer does not add to it: the loader empties the buffer back to the payer before charging for the program account, as the fresh devnet deploy of this build showed. Then:

bash scripts/wsl/deploy-program.sh "$MAINNET_RPC_URL"
node scripts/setup-mainnet.ts

This is unaudited hackathon software.


Begun at the Stocklana hackathon, PreStocks track, and built on for Colosseum's Crypto World's Fair. PreStocks-only by design — no other pre-IPO token issuer is integrated.

About

Rung — a capital-backed valuation market for PreStocks on Solana. Built for the Stocklana hackathon.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages