Oracle3
Open-source trading engine and MCP server for prediction markets. Fee-aware no-arbitrage trading across related contracts, live on Kalshi, Polymarket and Solana or on paper.
One deposit, two risks. A loss hits junior first; cover against the pool's loss is settled on chain. Devnet demo.
FinanceTypeScript · RustGitHub @trancheyieldOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Structured credit on Solana: senior/junior tranches over a simulated lending pool, with an on-chain loss waterfall and operator-recorded loss events. Devnet demo.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Structured credit on Solana: a deposit into a lending pool is sliced into a senior
tranche with a capped, predictable yield and a junior tranche that takes the residual
yield and absorbs losses first. A separate loss-protection market will let anyone buy or
sell cover against a loss event in a specific pool, settled automatically on chain.
The lending pool in this repository is a simulation: yield accrues at a configured rate on a
scaled model clock, and loss events are recorded by the pool operator. Integration with
external lending protocols (Kamino, MarginFi, …) is out of scope.Read the full README ↗Structured credit on Solana: a deposit into a lending pool is sliced into a senior tranche with a capped, predictable yield and a junior tranche that takes the residual yield and absorbs losses first. A separate loss-protection market lets anyone buy or sell cover against a loss event in a specific pool, settled automatically on chain.
The lending pool in this repository is a simulation: yield accrues at a configured rate on a scaled model clock, and loss events are recorded by the pool operator. Integration with external lending protocols (Kamino, MarginFi, …) is out of scope.
Status — v0.2.0: pool, tranches, yield, loss waterfall and the protection market are
live on devnet with a web app and an operator sheet. The "what if" cabinet (v0.3.0) is
next; its screen in the web app still runs on mock data.
assets / supply, 1:1 while the tranche is empty).yield_rate. A performance fee goes to the operator's
treasury, senior gets at most senior_rate on its assets, junior takes everything left
(including rounding remainders).L hits junior first; senior loses only L − junior once junior is wiped out.
Every loss is written as an immutable LossEvent account (ts, model time, bps, amount,
junior/senior split, assets before) — the loss history is read from chain, no server.min_junior_bps of pool assets
(the junior floor); the first deposit into an empty pool must therefore be junior.assets == senior_assets + junior_assets == vault.amount.
Yield is minted into the vault, losses are burned from it.accrue. Time is a model clock:
model_time += elapsed × time_scale. The demo pool runs at 43 200×, so one minute on
chain is 30 model days — enough to watch NAV move during a demo.All amounts are integers in micro-units (u64 on chain, bigint in TypeScript); products go
through u128; everything is checked arithmetic.
| Site | https://trancheyield.github.io/Wash-App/ — landing page (GitHub Pages, built from main) |
| Web app | https://trancheyield.github.io/Wash-App/app/ |
| Program | 2Yq39tVgTH5e8be8YdssyhvM6339f2WG6QweNmxGpBbf (SBPFv0, upgradeable) |
| Pool 0 | yield 8 %/yr · senior 5 %/yr · junior floor 20 % · fee 10 % · clock 43 200× |
| Pool 0 cover | premium 2 %/yr of notional · trigger 1 % loss · fee 10 % of premium |
| Faucet | 1 000 WUSD per request from the pool page (cap per call in Config) |
fixtures/params.json is the single source of the demo parameters for demo:init, the
program tests and the design brief.
programs/washapp Anchor 1.2 program: init_config, faucet, create_pool, accrue, deposit,
redeem, record_loss, init_protection, provide_protection,
withdraw_protection, buy_protection, settle_protection,
expire_protection; pure waterfall and cover math in src/math.rs
packages/chain Codama-generated client on @solana/kit, PDAs, account readers,
instruction builders (each with a round-trip decode test)
packages/shared TypeScript mirror of the waterfall math, micro-unit formatting, Zod schemas
apps/web React + Vite SPA: pool page, deposit/redeem, loss sheet with operator
panel, protection market (live on devnet); position screen (mock data, M3)
apps/landing static landing page served at the site root (no build)
tools/demo devnet init and the scripted end-to-end scenario
fixtures/ evidence generated by the Rust tests and the scenario (see below)
scripts/ WSL build/deploy wrappers and the trace sweep run in CI
packageManager is pinned).1.97.1 (rust-toolchain.toml), Agave / cargo-build-sbf 3.1.10,
Anchor CLI 1.2.0 — only needed to emit the IDL. On Windows the on-chain toolchain runs in WSL.Copy .env.example to .env. SOLANA_RPC_URL and WASH_KEYS_DIR are read by the demo tools
and the deploy script; VITE_* by the web app. The operator, deployer and program keypairs live
in WASH_KEYS_DIR, outside the repository.
pnpm install
pnpm gate # biome + tsc + vitest across the workspace
pnpm dev # web on :5173
pnpm codama # IDL → packages/chain/src/generated (after wsl-build.sh idl)
pnpm demo:init # devnet: config, demo mint, treasury and pool 0 (idempotent)
pnpm demo:scenario # devnet: fresh buyer and seller → deposits → cover sold and bought → 60 s →
# loss → settle → expire → redeem → withdraw
On-chain, from PowerShell into WSL:
wsl.exe -e bash <repo>/scripts/wsl-build.sh gate # fmt-check, clippy, build-sbf, cargo test
wsl.exe -e bash <repo>/scripts/wsl-build.sh idl # anchor build for the IDL only
wsl.exe -e bash <repo>/scripts/wsl-build.sh fixtures # regenerate fixtures/*.json from the tests
wsl.exe -e bash <repo>/scripts/wsl-deploy.sh deploy # devnet deploy or upgrade, then verify
wsl.exe -e bash <repo>/scripts/wsl-deploy.sh verify # bytecode on chain == local .so, e_flags 0x0
The network artifact is built with cargo-build-sbf (SBPFv0 — Agave 3.1.10 on devnet does not
execute v3 bytecode). anchor build is used only to emit the IDL and overwrites the .so with a
v3 build, so build-sbf always follows it.
The site is static: there is no server, every number on it is read from chain in the browser.
.github/workflows/pages.yml runs on every push to main (or by hand from Actions → Pages →
Run workflow) and publishes one artifact of two folders:
https://<owner>.github.io/<repo>/ apps/landing — static landing page, copied as is
https://<owner>.github.io/<repo>/app/ apps/web — the app, built with base `/<repo>/app/`
apps/landing has no build and no dependencies (index.html, styles.css, assets/); every
figure on it comes from this README and fixtures/demo-run.json. Its og:image is an absolute
URL to the project site — change it together with the domain.
One-time setup in the repository:
Settings → Pages → Source: GitHub Actions (not "Deploy from a branch" — the branch root
has no index.html).
Optionally, Settings → Secrets and variables → Actions → Variables:
| Variable | Unset means |
|---|---|
VITE_SOLANA_RPC_URL | public https://api.devnet.solana.com |
VITE_WASH_DEFAULT_POOL | pool 0 |
VITE_SOLANA_CHAIN | solana:devnet (wallet-standard chain id) |
These are variables, not secrets: every VITE_* value is readable in the published
bundle. An RPC URL with a key is public once it is here. Helius Free (October 2026) cannot
lock a key to a domain, so use a key from a separate account that serves nothing else, and
test any provider's origin lock with curl -H "Origin: https://example.com" before relying
on it. Re-run the workflow after changing a variable.
Notes:
pnpm build: it reads the repo-root .env and would bake its RPC key
into the bundle. CI has no .env./Wash-App) comes from actions/configure-pages and the app is built under
<site path>/app/, so a custom domain needs no code change in the app. Set the domain in Settings → Pages only — a CNAME file in the repository is
ignored when Pages deploys from a workflow.index.html into every route of the default pool
(/app/pool/<id>/…, /app/me, /app/operator), so a direct link or a reload answers 200.
Any other path — another pool id, or a link from before the app moved under /app/ — renders
the app through the site's root 404.html with status 404; the app moves an old link below
/app/ before the router reads it.Run from PowerShell into WSL, with SOLANA_RPC_URL and WASH_KEYS_DIR set in .env:
wsl.exe -e bash <repo>/scripts/wsl-build.sh build-sbf # SBPFv0 artifact in target/deploy
wsl.exe -e bash <repo>/scripts/wsl-deploy.sh deploy # first deploy or upgrade, then verify
wsl.exe -e bash <repo>/scripts/wsl-deploy.sh status # program account and deployer balance
pnpm demo:init # Config, mint, treasury, pool 0, its cover market
washapp-keypair.json) fixes the address and must match declare_id!;
the deployer keypair is the upgrade authority. Both live in WASH_KEYS_DIR; the deploy
script creates the deployer if it is missing.deploy refuses to start until the deployer can pay: a first deploy needs rent for
ProgramData (.so × 1.5, room for upgrades) plus a buffer of the .so size; an upgrade needs
only the buffer. The buffer is refunded after the write. The script prints the exact amounts.deploy ends with verify: the bytecode on chain must equal target/deploy/washapp.so byte
for byte and be SBPFv0.wsl-build.sh idl, then build-sbf, then pnpm codama) and
push — Pages rebuilds from main.declare_id! (bytecode) and in the generated client (pnpm codama), which the site and the
tools both use. Set the new keypair, update declare_id! and WASH_PROGRAM_ID in .env,
then idl → build-sbf → pnpm codama → deploy, and push.Program tests run the real .so under mollusk-svm in cargo test: unit tests on
math.rs with proptest invariants, one test file per instruction, a compute-unit gate
(every instruction < 200 000 CU; current peak 40 713 for record_loss), and 100 seeded random
sequences of deposit/redeem/accrue/loss that check the vault invariant and the loss split byte
for byte after every step.
fixtures/ holds what the Rust side generated so the TypeScript side can be checked against
the program rather than against itself:
waterfall.json — 152 cases from math.rs; packages/shared must reproduce them exactly.pda.json — 30 PDA derivations at the edges of every numeric seed.accounts/m0.json — raw account bytes from the SVM before and after a 15 % loss; the readers
decode them in vitest.demo-run.json — the last demo:scenario run on devnet: per-step timings (signature →
confirmed → change visible in RPC), the loss waterfall checked against the on-chain event, and
the protection checks — premiums against the mirror, the buyer's balance after settle
(+notional × loss), the expired contract's released reservation, the seller's result.Guard tests fail cargo test when a fixture is stale, and vitest fails when a fixture breaks a
budget (scenario ≤ 180 s, each step ≤ 10 s) or records a failed check. The last run: 97.4 s end
to end over 16 transactions, slowest step 4.2 s; settle paid 150.00 on a 1,000 WUSD notional for a
15 % loss, to the unit.
CI (.github/workflows/ci.yml) runs the Rust gate on the SBPFv0 artifact, the Node gate, and a
sweep for keys and machine-specific traces over the whole history.
v0.1.0 — pool, tranches, yield, loss waterfall; pool page, deposit/redeem, operator loss
sheet; devnet scenario.v0.2.0 — protection market: sellers fund a cover pool, buyers pay a premium for cover
against a loss above a trigger, settlement pays out from the cover pool when a loss is
recorded; landing page at the site root. This release.v0.3.0 — position cabinet with a "what if the pool loses X %" forecast that must match the
program to one micro-unit.Licensed under the Apache License, Version 2.0.