Architecture
NullGraph is a fully on-chain protocol built on Solana with zero backend infrastructure. All state lives in Program Derived Addresses (PDAs), all logic executes in an Anchor program, and all reads happen via JSON-RPC.System overview
Data flow
- Writes go through the Solana Wallet Adapter. The user signs transactions in Phantom, which are submitted to the Anchor program.
- Reads use
getProgramAccountsRPC calls through the Anchor client. There is no backend server, database, or indexer. - BIO transfers use CPI
transfer_checkedto the SPL Token Interface, which validates the mint and decimal precision.
No off-chain infrastructure means no single point of failure, no database migrations, and no hosting costs.
On-chain program
The Anchor program lives in a single file (programs/nullgraph/src/lib.rs, ~593 lines). It uses Anchor 0.31.1 with anchor-spl for SPL Token Interface CPI and associated token accounts.
Program ID
2u3DXQq9A6UgMryeVSWCNdYLy3Fjh391R5hcfWYkCgZK.
Account architecture
NullGraph uses four main account types, all implemented as Program Derived Addresses:1. ProtocolState (Singleton)
Global configuration storing auto-incrementing counters, fee rate, and treasury address.
PDA Seeds:
["protocol_state"]
The singleton pattern ensures a single source of truth for protocol configuration and prevents counter collision.
2. NullResult (One per NKA)
Stores all metadata for a Null Knowledge Asset.
PDA Seeds:
["null_result", researcher_pubkey, specimen_number_le_bytes]
3. NullBounty (One per bounty)
Represents a bounty posted by a BioDAO or researcher.
PDA Seeds:
["null_bounty", creator_pubkey, bounty_number_le_bytes]Vault Seeds:
["bounty_vault", bounty_pda_key]
4. BountySubmission (Links an NKA to a bounty)
Created when a researcher submits their NKA to a bounty.
PDA Seeds:
["bounty_submission", bounty_pda_key, null_result_pda_key]
Instructions
The program exposes six instructions:initialize_protocol(fee_basis_points)
One-time setup. Creates the ProtocolState singleton with counters at zero and the given fee rate.
ProtocolInitialized { authority, fee_basis_points }
submit_null_result(...)
Mints a new NKA. Increments nka_counter, creates the NullResult PDA, and records all fields.
NullResultSubmitted { specimen_number, researcher }
create_bounty(description, reward_amount, deadline)
Creates a NullBounty PDA + vault, transfers BIO from the creator’s token account to the vault.
reward_amount > 0Emits:
BountyCreated { bounty_number, creator, reward_amount, deadline }
submit_to_bounty()
Links a researcher’s NKA to an open bounty. Creates a BountySubmission PDA and transitions the bounty to Matched.
has_one constraint)Emits:
BountySubmissionCreated { bounty_number, specimen_number, researcher }
approve_bounty_submission()
Bounty creator approves the submission. Transfers reward - fee BIO to the researcher and fee BIO to the treasury via vault CPI.
BountyFulfilled { bounty_number, specimen_number, researcher, payout, fee }
close_bounty()
Bounty creator reclaims escrowed BIO from any Open or Matched bounty. Vault balance returns to creator, bounty set to Closed.
BountyClosed { bounty_number, creator, refunded_amount }
Error codes
Frontend architecture
A React 19 SPA built with Vite 7.3 and Tailwind CSS v4. Connects directly to Solana devnet with no backend server.Provider stack
ProgramProvider exposes a single Anchor Program instance constructed from the compiled IDL:
Routes
Data hooks
All data fetching uses custom React hooks that wrapprogram.account.<type>.all():
useProtocolState()- Fetches the singleton ProtocolStateuseNullResults()- Fetches all NullResult accountsuseBounties()- Fetches all NullBounty accountsuseBountySubmissions(filters)- Fetches BountySubmission accounts (filterable by bounty or researcher)
{ data, loading, error, refetch }.
No indexer required — the frontend fetches all accounts on page load. For production scale, consider using a Solana indexer like Helius or GenesysGo.
Transaction hooks
Transaction hooks derive PDAs, build instructions, send transactions, and show toast notifications:useSubmitNullResult()- Mints a new NKAuseCreateBounty()- Creates a bounty with escrowed BIOuseSubmitToBounty()- Links an NKA to a bountyuseApproveBountySubmission()- Approves a submission and pays outuseCloseBounty()- Closes a bounty and refunds escrow
useSubmitNullResult:
Design system
Dark-mode cyberpunk aesthetic defined as Tailwind v4@theme tokens:
- Background:
#060810with a 40px CSS grid overlay - Surfaces:
#0d1017(surface),#161c2a(raised),#232840borders - Accents: Neon cyan (
#5ec4de), magenta (#c8836a), lime (#62b862) - Typography: Cabinet Grotesk (display), Satoshi (body), Space Grotesk (mono/data)
- Effects: CRT scanline animation, SVG noise overlay, glass card hover lifts with colored glow shadows
Security model
On-chain security
- Signer checks on every write instruction via Anchor struct-level enforcement
- PDA ownership — all data accounts are program-owned PDAs
has_oneconstraints ensure only the correct wallets can perform actions- Status guards validate current status before every state mutation
- Replay protection — PDA
initconstraints prevent duplicate accounts - Vault authority — vault token accounts use the vault PDA itself as authority
- Safe arithmetic — all fee/payout math uses
checked_mul,checked_div,checked_sub - Transfer validation — all BIO transfers use
transfer_checked, validating mint and decimal precision
Frontend security
- No private keys handled. All signing delegated to Phantom.
- Client-side file hashing via Web Crypto API (
crypto.subtle.digest). - Input bounds match on-chain field sizes (128/256 chars via
maxLength).
Tech stack summary
On-chain
Frontend
Solana client libraries
What’s next?
Now that you understand the architecture, explore:- Program reference - Complete on-chain program documentation
- Frontend integration - Build a frontend for NullGraph
- Security best practices - Anchor security patterns used in NullGraph