Shielded prediction pools and verifiable binary markets on Solana.
Live app · GitHub · Devnet program · Deployment record
Nortia is a prediction-market protocol with two complementary market designs:
- Private confidential-amount pools where an individual YES or NO side and exact wager are hidden behind a Poseidon commitment, verified by Noir proofs, shared across a 2-of-3 committee, and settled from TxLINE data.
- Public continuous markets with deterministic binary LMSR pricing, creator-funded liquidity, category-specific resolvers, and immutable settlement receipts.
Both designs escrow Circle devnet USDC in program-owned vaults. SOL is used only for Solana transaction fees and account rent.
Nortia is currently an unaudited devnet beta. Do not use it with real funds.
- Private sports positions with hidden YES or NO sides and hidden wager amounts from 1 USDC to a market-defined collateral ceiling
- Browser-generated Noir placement and redemption proofs compiled through Sunspot
- Onchain proof verification through dedicated Solana verifier programs
- Poseidon commitments, Merkle membership proofs, and market-scoped nullifiers
- Three-member Shamir committee with a two-member settlement quorum
- TxLINE score ingestion and permissionless settlement through
validate_stat_v2CPI - Deterministic binary LMSR markets with exact integer-only pricing
- Pyth, Switchboard, Stork, TxLINE, and bonded optimistic resolver paths
- User-created market metadata with immutable question, rule, label, and resolver hashes
- USDC escrow, bounded fees, winner claims, timeout recovery, and fee-free refunds
- Wallet-backed Next.js application, indexer, keeper, optional hosted prover, private relay, and committee services
| Private TxLINE pool | Public LMSR market | |
|---|---|---|
| Position | Hidden variable wager inside uniform market collateral | Variable YES or NO shares |
| Side visibility | Hidden from public chain observers | Public |
| Price | Pool payout known after aggregate settlement | Continuous LMSR quote |
| Liquidity | Aggregate hidden wager pool | Creator-funded worst-case subsidy |
| Resolution | TxLINE score proof and CPI | TxLINE, Pyth, Switchboard, Stork, or optimistic evidence |
| Claim | Zero-knowledge redemption proof | Wallet-owned position settlement |
| Fee | 1% of actual hidden wager liquidity | Probability-sensitive fill fee, bounded by market configuration |
| Failure path | Full public collateral refund | Invalid resolution or timeout settlement |
Private pools are for confidential participation. Public LMSR markets are for continuous price discovery across sports, crypto, economics, politics, science, governance, and other objective binary questions.
Within the onchain protocol, an individual private-pool side and exact wager are shielded. The transaction, order account, program events, and USDC transfer do not contain either plaintext value. Every order in one market transfers the same public collateral ceiling, so transfer size does not reveal the wager.
- The payer wallet and the market's uniform collateral transfer
- The market collateral ceiling, selectable from 1, 5, 10, 25, 50, 100, 250, 500, or 1,000 USDC
- The market, order index, and timing
- The Poseidon order commitment
- Three salted share commitments
- The placement proof and its public inputs
- Aggregate YES and NO counts and wager liquidity after batching
- The final outcome, pool accounting, and settlement evidence
- A claim nullifier hash and its recipient wallet
- The individual YES or NO side
- The individual exact wager amount
- The order secret and nullifier preimage
- The three random Shamir coefficients
- Each committee share bundle outside its assigned member service
- The direct link between an order commitment and a later winning claim
| Actor | Privacy property |
|---|---|
| Public chain observer | Cannot derive an individual side from the order transaction or link a proof-based claim to one order |
| One committee member | Holds one randomized bundle and cannot reconstruct the side or exact amount alone |
| Two colluding committee members | Can reconstruct individual sides and amounts because the current threshold is 2-of-3 |
| Browser prover | Generates the witness and proof locally without sending private inputs to Nortia services |
| Optional hosted prover | Sees the side, secret, nullifier, and Shamir witness when explicitly enabled as a fallback |
| Web routing layer | Routes member-specific encrypted envelopes and can observe request metadata, but cannot decrypt a committee share |
| Private relay | Receives public proof material and the selected recipient, and can observe request metadata and timing |
| Local browser storage | Holds an AES-GCM encrypted, wallet-bound recovery vault required for redemption |
Browser proving is the default. The hosted devnet prover is available only when NEXT_PUBLIC_NORTIA_PROOF_MODE=hosted is configured, deletes temporary proving workspaces, and remains a trusted witness boundary in that mode. Production privacy still requires independently operated committee members, an audited setup, an audited client, and stronger network-level metadata protection.
The current committee reconstructs aggregate counts and wager liquidity by design. The program accepts only batches with at least four orders and at least one order on each side. Ineligible markets remain open until anyone starts the full-value refund path, so their aggregates are never written onchain. Eligible aggregate counts and amounts can still narrow anonymity. Nortia therefore provides shielded onchain positions, not an unconditional anonymity claim against committee collusion or metadata analysis.
The browser generates a random nonzero secret, nullifier, three Shamir coefficients, and three nonzero salts with rejection-sampled Web Crypto entropy. It derives the payer hash and three member share bundles, then generates the proof locally. The placement circuit proves:
- The hidden side is binary.
- The hidden amount is at least 1 USDC and does not exceed the public market collateral ceiling.
- The public order commitment binds the market, collateral ceiling, hidden amount, side, secret, and nullifier.
- All three public share commitments bind consistent shares of the side, YES amount contribution, and total amount contribution.
- The proof is bound to the transaction payer.
The browser saves the recovery record into a wallet-signature-derived AES-GCM vault before asking the wallet to sign the placement transaction.
The Nortia program verifies the 388-byte Sunspot proof through the deployed placement-verifier program. A successful transaction creates the order PDA and moves the market's uniform public collateral ceiling into the vault. New private markets default to a 100 USDC ceiling, and creators can select another allowlisted ceiling.
The side, exact wager, secret, nullifier, coefficients, and raw share bundles are never written onchain.
The browser encrypts each share to its assigned committee member with RSA-OAEP and AES-256-GCM. The web route can route the three ciphertext envelopes but cannot decrypt them. Before accepting a decrypted share, every member verifies:
- The order PDA exists and belongs to Nortia.
- The market, order index, and commitment match.
- Its salted share matches the corresponding onchain share commitment.
- The supplied placement transaction succeeded and invoked the Nortia program.
Member state is encrypted with AES-256-GCM and persisted with owner-only file permissions. Share delivery is idempotent, and an interrupted delivery remains recoverable from the encrypted browser vault until all three members accept it.
After the lock time, two distinct committee members combine their ordered snapshots. The coordinator adds bundles before interpolation, reconstructs the aggregate YES count, YES wager liquidity, and total wager liquidity, derives the NO aggregates, builds the Poseidon commitment root, and submits the batch with two committee signatures.
The program rejects missing orders, fewer than four orders, one-sided batches, counts or amounts outside their provable bounds, invalid committee signers, early batches, and batches after the deadline. Rejected aggregates are not persisted or emitted. After the batch deadline, anyone can enter refunds.
The keeper selects a finalized TxLINE score record and requests the stat-validation payload. Nortia checks the exact fixture, final-period goal keys, timestamps, daily-root PDA, pinned TxLINE program, and CPI return origin before accepting the result.
The program stores the final score, aggregate counts and amounts, fee accounting, root account, sequence, and settlement-evidence hash.
Every position, including a losing position with unused collateral, proves that its hidden commitment:
- Belongs to the finalized 16-level commitment tree
- Binds the hidden side and amount used in deterministic settlement math
- Produces the submitted market-scoped nullifier
- Is bound to the selected recipient and exact total return
The claim PDA prevents replay without revealing which order was redeemed. The browser binds the proof to a fresh recipient address that must differ from the order wallet, then sends only the public proof material through the private relay. The relay creates the recipient token account when needed and submits the Solana transaction, so the original wallet is not the onchain redemption sender.
The encrypted recovery vault can be exported and imported as an authenticated wallet-bound backup. The same wallet must sign the vault-unlock message to decrypt it.
For a winner, the total return is unused collateral plus its proportional share of the net wager pool. For a loser, it is the unused collateral. If batching or resolution misses its deadline, anyone can open refunds. Every order receives its full public collateral and no protocol fee is charged.
A connected creator selects a category, binary question, exact rules, outcome labels, lock time, resolution policy, and one enabled resolver. The program stores immutable hashes and creates the market, metadata, oracle configuration, USDC vault, and liquidity-owner relationship.
The creator funds:
required subsidy = ceil(b * ln(2)) + rounding reserve
This bounds the binary market maker's worst-case loss before trading begins.
Nortia uses the binary LMSR cost function:
C(q_yes, q_no) = b * ln(exp(q_yes / b) + exp(q_no / b))
The onchain implementation uses deterministic fixed-point integer arithmetic. It does not use settlement-critical floating point. Every buy or sell includes:
- An exact share amount
- A transaction deadline
- A maximum buy cost or minimum sell return
- A bounded market-imbalance check
- A vault-solvency check
The same integer implementation exists in the client package and is tested against independent floating-point references and randomized solvency sequences.
Trading closes at the immutable lock timestamp. A keeper or any eligible caller submits resolver-specific evidence. The program normalizes that evidence into one outcome and writes an immutable resolution receipt.
A winning share pays one USDC. An invalid result pays half of the aggregate YES and NO shares, rounded down. The liquidity owner can withdraw only collateral above all unsettled trader liabilities.
The canonical private protocol charges 100 basis points, or 1%, once on a successfully resolved gross pool.
gross pool = aggregate YES wager + aggregate NO wager
protocol fee = floor(gross pool * 1%)
net pool = gross pool - protocol fee
unused collateral = market collateral ceiling - hidden wager
market payout = winner ? floor(hidden wager * net pool / winning-side wager) : 0
total return = unused collateral + market payout
Ten percent of the protocol fee rewards the resolving keeper. Ninety percent goes to the Nortia treasury. Proportional division dust is bounded by the number of winning positions and can be assigned only to the final valid settlement. Unrelated vault donations cannot inflate that settlement.
Refunds are fee-free. The protocol does not charge an order merely for being placed.
The canonical engine uses a 100-basis-point configured fill-fee parameter. Its effective fee is probability-sensitive and remains below that configured bound. Seventy percent of collected trading fees goes to the Nortia treasury and thirty percent remains with the market liquidity owner.
The program checks liability coverage after every value-moving instruction. Fees cannot be withdrawn from collateral needed to pay the largest possible outcome.
| Resolver | Intended markets | Current state | Onchain validation |
|---|---|---|---|
TxLINE validate_stat_v2 |
Sports results and deterministic props | Active on devnet | Fixture, final period, stat keys, daily root, program ID, CPI return source, and time window |
Pyth PriceUpdateV2 |
Crypto and economic price thresholds | Active and exercised on devnet | Receiver owner, verification level, feed ID, observation interval, publish lag, and confidence width |
| Pyth push oracle | Sponsored push-feed price markets | Enabled in program | Pinned push-oracle program and canonical feed identity |
| Switchboard canonical quote | Curated numeric facts outside sports and crypto | Built and tested | Quote program, devnet queue, canonical PDA, feed hash, sample count, and slot age |
| Stork price | Crypto and economic price thresholds | Built and tested, API access not configured on the live operator | Canonical feed PDA, owner, discriminator, asset ID, timestamp, and fixed exponent |
| Bonded optimistic | Politics, governance, launches, awards, and long-tail facts | Built and tested | Evidence hashes, equal opposing bonds, challenge window, committee dispute path, and timeout |
| UMA over Wormhole | Future long-tail arbitration | Disabled | Market creation fails closed |
| Chainlink report | Future report-based markets | Disabled | Market creation fails closed |
Unsupported resolvers cannot create an open market. A market cannot switch resolver, feed, rules, labels, fee split, or observation policy after creation.
Devnet defaults to credential-free public infrastructure:
ORACLE_PROVIDER_PROFILE=freeFree mode pins public Pyth Hermes and Switchboard Crossbar, removes accidentally supplied Pyth credentials, and paces Hermes requests. The Stork program adapter remains compiled, but offchain Stork fetching stays unavailable without an API token.
Managed provider access can be enabled without changing program code:
ORACLE_PROVIDER_PROFILE=managed
PYTH_API_KEY=replace-me
PYTH_HERMES_ORIGIN=https://managed-provider.example/hermes
SWITCHBOARD_CROSSBAR_ORIGIN=https://managed-provider.example/crossbar
STORK_REST_ORIGIN=https://managed-provider.example/stork
STORK_API_TOKEN=replace-meProvider selection never relaxes the onchain program, feed, queue, timestamp, confidence, sample, or canonical-account checks.
TxLINE is Nortia's primary sports data source. The integration is implemented in the TxLINE client, validation mapper, and onchain CPI adapter.
Endpoints used:
GET /scores/historical/{fixtureId}for final-record discovery and replayGET /scores/snapshot/{fixtureId}?asOf=...for current fixture stateGET /scores/updates/{epochDay}/{hourOfDay}/{interval}?fixtureId=...for bounded recovery scansGET /scores/streamfor live SSE ingestionGET /scores/stat-validation?fixtureId=...&seq=...&statKeys=1,2for the Merkle payload submitted to settlement CPI
Nortia accepts only a finalized game record with status 100, a positive sequence, and final period 100 when the record exposes a period field. Score validation separately requires both proven goal leaves to use the final period.
Integration notes:
- TxLINE's normalized score schema supports one adapter across the full tournament.
- Historical, snapshot, bounded-update, and SSE interfaces provide both live ingestion and recovery.
- The stat-validation endpoint maps directly to deterministic market predicates.
- Response casing is normalized across
Seqandseq. - Authenticated historical responses may arrive as SSE records even with an application-JSON accept header.
- A final historical record may omit its top-level period while the stat proof still binds both goal leaves to period
100.
flowchart LR
U[Wallet and Next.js app] --> BP[Browser Noir and Sunspot prover]
U -. optional fallback .-> HP[Hosted prover]
HP --> U
U --> N[Nortia Anchor program]
U --> E[Encrypted share router]
E --> C1[Committee member 1]
E --> C2[Committee member 2]
E --> C3[Committee member 3]
U --> L[Private redemption relay]
L --> N
C1 --> B[Threshold batch coordinator]
C2 --> B
C3 --> B
B --> N
T[TxLINE scores and proofs] --> K[Permissionless keeper]
O[Pyth, Switchboard, Stork, or evidence] --> K
K --> N
N --> Z[Sunspot verifier programs]
N --> V[USDC vaults]
N --> R[Resolution receipts]
I[Indexer] --> U
I --> N
| Path | Responsibility |
|---|---|
programs/nortia/ |
Anchor program, private pools, LMSR engine, resolvers, receipts, positions, and vault accounting |
circuits/ |
Noir placement and redemption circuits, browser prover wrapper, and generated verifier sources |
client/ |
Exact LMSR math, Poseidon commitments, Merkle trees, Shamir aggregation, oracle inputs, and portfolio math |
services/src/prover/ |
Optional authenticated hosted proof-generation fallback |
services/src/committee/ |
Three encrypted share services and the two-member batch coordinator |
services/src/relayer/ |
Authenticated redemption relay for fresh recipient addresses |
services/src/keeper/ |
Market locks, resolver settlement, optimistic finalization, and timeout recovery |
services/src/indexer/ |
Account snapshots, metadata verification, position state, and resolution receipts |
services/src/txline/ |
TxLINE REST, SSE, final-record selection, and validation-payload mapping |
services/src/pyth/ |
Timestamped Hermes retrieval and Pyth settlement composition |
services/src/switchboard/ |
Canonical quote definition and validation |
services/src/stork/ |
Authenticated price retrieval and canonical feed settlement |
web/ |
Next.js landing page and wallet-backed market application |
deployments/ |
Canonical devnet addresses, transactions, artifacts, and runtime configuration |
Root Cargo files and programs/ follow the standard Anchor workspace structure. JavaScript dependencies remain isolated under client/, services/, and web/.
The canonical record is deployments/devnet.json.
| Component | Solana devnet address |
|---|---|
| Nortia program | 4S2EvdGrbKJ9zazvB4gtR83crTrVJWqqwoVVvEQy8VE9 |
| ProgramData | 38zjkVVYSt6A2ZA5vN4grWH4kwY6GUGM4uzhaugNdZWS |
| Placement verifier | AJwBUyb2GP5MejGz22Tk88GX9FdVFVcJvFMUU9EtqLjH |
| Redemption verifier | 5LcBMGtMj2VAUFhQgRqPvJwRS9Z28Emv7rSWYcgBEPGZ |
| Protocol PDA | CJi67t1hHprwceArXdPyw6xLrN1Y3QbcvSC4R2SXoKZR |
| Market engine PDA | EWgvZgWZNc1m2yunKonZwavnPgY6n6T2BbwXFC6kdRpf |
| Circle devnet USDC | 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU |
| TxLINE program | 6pW64gN1s2uqjHkn1unFeEjAwJkPGHoppGvS715wyP2J |
| Private replay market | 44cD1kbvuheo5wSM4gxEZvAfitAXbC25f2u4Mzs48qix |
| Private replay vault | EqjB6nuMcvhtTw9Cngs2EgSNjthC6VrxFDthZnoYxtyM |
| Canonical Pyth market | Gwg5Q44JVakT3JdNtpJQPeSCCDas4VFJpeY2zmzXQ34h |
| Canonical Pyth vault | 3A3sQJ1V5g1tX5xy2P6zh72qXjPVH7T6Wupb74siEvGy |
The current Nortia binary was confirmed at slot 478034810 with transaction 5ATmai4oKW7dQJvXn5hP95YyEwJ2vfcgamZmL957e7Eeisy6JBnimypQSArLhBnmEyDDzVU7AwNUadpdqYq7SAtp.
The table records the currently live devnet state. The Nortia program, hardened verifier pair, protocol configuration, and empty replay market were rotated together. Browser-generated private placement and redemption proofs from this source tree are compatible with the live verifier programs.
The web application is deployed on Vercel. The optional prover, indexer, keeper, relay, and three committee members run as isolated services. Private service access is token-protected, bounded, and exposed through narrow no-store proxy routes. The current bootstrap deployment runs the committee members under one operator, so process isolation does not provide independence from operator collusion.
Requirements:
- Node.js 22 or newer
- Rust 1.89 or newer
- Solana CLI configured for devnet
- Anchor CLI 1.0.0
- Noir
1.0.0-beta.22 - Sunspot
v1.0.0
Install the three workspaces:
npm --prefix client install
npm --prefix services install
npm --prefix web installCreate local environment files:
cp services/.env.example services/.env
cp web/.env.example web/.env.localGenerate Nortia-only local keys without overwriting TxLINE credentials:
NORTIA_KEYPAIR_PATH=/absolute/path/to/devnet-keypair.json \
node scripts/bootstrap-local-env.mjsStart the web application:
npm --prefix web run devRun backend processes in separate terminals:
npm --prefix services run prover
npm --prefix services run committee
npm --prefix services run relayer
npm --prefix services run indexer
npm --prefix services run keeperEach committee member needs its own COMMITTEE_MEMBER_INDEX, port, state path, API token, and RSA encryption key file. The three COMMITTEE_API_TOKEN values must be distinct. Generate each encryption file once with:
npm --prefix services run committee:keys -- 1 /absolute/path/to/committee-1-encryption.jsonSet COMMITTEE_ENCRYPTION_KEY_PATH to the matching file and give each member a distinct 32-byte hex COMMITTEE_STATE_KEY to encrypt its state. Batch coordination additionally requires two committee signer keypairs and the three ordered member tokens in COMMITTEE_API_TOKENS. Vercel receives the same ordered list as NORTIA_COMMITTEE_API_TOKENS so its routing layer cannot use one member's credential against another.
The relay needs a dedicated funded devnet signer at NORTIA_RELAYER_KEYPAIR_PATH, a private RELAYER_API_TOKEN, and its own RELAYER_PORT. Create the signer with npm --prefix services run relayer:key -- /absolute/path/to/relayer.json. Configure NORTIA_RELAYER_URL and NORTIA_RELAYER_API_TOKEN only in the Vercel server environment. Keep NEXT_PUBLIC_NORTIA_PROOF_MODE unset for browser proving. Set it to hosted only when the explicit hosted fallback is required.
The keeper defaults to dry-run. Set KEEPER_DRY_RUN=false only for a deliberately funded devnet keeper.
cargo fmt --check
cargo clippy -p nortia --all-targets -- -D warnings
cargo test -p nortia
anchor build --ignore-keys
npm --prefix client test
npm --prefix client run typecheck
npm --prefix services test
npm --prefix services run typecheck
npm --prefix web run typecheck
npm --prefix web run buildCircuit checks:
cd circuits
nargo test --workspace
nargo compile --workspaceLatest verified results:
- 76 Rust tests pass.
- 48 client assertions pass.
- 56 service assertions pass.
- The Noir placement suite passes 5 tests across 8,024 compiled constraints.
- The Noir redemption suite passes 5 tests across 22,140 compiled constraints.
- Clippy passes with warnings denied.
- Anchor produces a valid SBF artifact.
- The optimized Next.js production build passes.
- A signed private-placement simulation verifies the proof and full order path in 215,237 of 300,000 requested compute units.
- A signed redemption-verifier simulation succeeds in 190,485 of 300,000 requested compute units.
- The browser WASM prover generates 388-byte placement and redemption proofs with 236-byte placement witnesses and 300-byte redemption witnesses, and both verify against the exact generated native verification keys.
- Browser ACIR, constraint-system, and proving-key artifacts match the generated circuit artifacts byte for byte.
Run cargo clean after Rust verification when local disk space is constrained.
Nortia is experimental and unaudited.
- The Solana program, integer LMSR implementation, oracle adapters, circuits, and generated verifier programs require independent review.
- Sunspot setup artifacts were generated with a development trusted setup. Mainnet requires an independent ceremony.
- The program remains upgradeable by the recorded devnet upgrade authority.
- Browser proving keeps private witnesses in the client by default. The explicitly enabled hosted fallback sees private witnesses.
- The current committee is a fixed 2-of-3 set running under one devnet operator. Two members, or that operator, can reconstruct individual sides and amounts despite encrypted transport and encrypted state.
- Eligible batches publish aggregate counts and wager liquidity, which can narrow anonymity even with the four-order and two-sided minimum.
- The routing layer and relay can observe IP addresses, timing, market selection, and recipient metadata. Nortia does not currently provide Tor routing or independent relay selection.
- Recovery secrets are encrypted in browser local storage and can be exported as a wallet-bound backup. Losing every copy can make a private position unclaimable.
- Public LMSR trades are intentionally transparent and must not be described as private.
- TxLINE settlement verifies the pinned onchain validation program and Merkle path, but still inherits the data-source trust assumptions of TxLINE.
- Keepers improve availability but hold no exclusive settlement authority. Nothing onchain executes automatically without a transaction.
- No mainnet deployment should occur before an audit, independent committee operation, production monitoring, setup review, privacy-set policy, and legal review.
Prediction-market operation must comply with applicable gambling, gaming, financial, consumer-protection, securities, privacy, and data laws.