> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/Ge0frey/nullgraph/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture

> Technical architecture, system design, and implementation details of the NullGraph protocol.

# 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

```
                +----------------------------+
                |   Researcher's Browser     |
                |   (React 19 + Vite 7.3)   |
                +-----+--------------+-------+
                      |              |
           Write (tx) |              | Read (RPC)
                      v              v
       +--------------+--------------+-----------+
       |        Solana Devnet Cluster             |
       |                                          |
       |  +------------------------------------+  |
       |  |  NullGraph Anchor Program          |  |
       |  |  (2u3DXQq...CgZK)                 |  |
       |  |                                    |  |
       |  |  Accounts:                         |  |
       |  |   - ProtocolState (singleton)      |  |
       |  |   - NullResult PDAs                |  |
       |  |   - NullBounty PDAs                |  |
       |  |   - BountySubmission PDAs          |  |
       |  |   - Vault Token Accounts           |  |
       |  +------------------------------------+  |
       |                                          |
       |  +------------------------------------+  |
       |  |  SPL Token Interface               |  |
       |  |  (BIO transfers via CPI)           |  |
       |  +------------------------------------+  |
       +------------------------------------------+
```

### Data flow

* **Writes** go through the Solana Wallet Adapter. The user signs transactions in Phantom, which are submitted to the Anchor program.
* **Reads** use `getProgramAccounts` RPC calls through the Anchor client. There is no backend server, database, or indexer.
* **BIO transfers** use CPI `transfer_checked` to the SPL Token Interface, which validates the mint and decimal precision.

<Note>
  No off-chain infrastructure means no single point of failure, no database migrations, and no hosting costs.
</Note>

## 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

```rust theme={null}
declare_id!("2u3DXQq9A6UgMryeVSWCNdYLy3Fjh391R5hcfWYkCgZK");
```

Deployed to **Solana Devnet** at `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.

| Field              | Type     | Description                        |
| ------------------ | -------- | ---------------------------------- |
| `authority`        | `Pubkey` | Protocol admin wallet              |
| `nka_counter`      | `u64`    | Auto-incrementing NKA counter      |
| `bounty_counter`   | `u64`    | Auto-incrementing bounty counter   |
| `fee_basis_points` | `u16`    | Fee on settlement (250 = 2.5%)     |
| `treasury`         | `Pubkey` | Treasury wallet for collected fees |
| `bump`             | `u8`     | PDA bump seed                      |

**PDA Seeds:** `["protocol_state"]`

<Info>
  The singleton pattern ensures a single source of truth for protocol configuration and prevents counter collision.
</Info>

#### 2. NullResult (One per NKA)

Stores all metadata for a Null Knowledge Asset.

| Field              | Type        | Description                             |
| ------------------ | ----------- | --------------------------------------- |
| `researcher`       | `Pubkey`    | Submitting wallet                       |
| `specimen_number`  | `u64`       | Sequential ID (NKA-0001, NKA-0002, ...) |
| `hypothesis`       | `[u8; 128]` | Hypothesis tested (UTF-8, zero-padded)  |
| `methodology`      | `[u8; 128]` | Methodology summary                     |
| `expected_outcome` | `[u8; 128]` | What was expected                       |
| `actual_outcome`   | `[u8; 128]` | What actually happened                  |
| `p_value`          | `u32`       | Fixed-point (8700 = 0.8700)             |
| `sample_size`      | `u32`       | Sample size n                           |
| `data_hash`        | `[u8; 32]`  | SHA-256 of attached data file           |
| `status`           | `u8`        | 0 = Pending, 1 = Verified, 2 = Disputed |
| `created_at`       | `i64`       | Unix timestamp                          |
| `bump`             | `u8`        | PDA bump seed                           |

**PDA Seeds:** `["null_result", researcher_pubkey, specimen_number_le_bytes]`

<Tip>
  The `data_hash` field stores a SHA-256 fingerprint of any attached data file. The file stays off-chain; the hash provides a tamper-proof link.
</Tip>

#### 3. NullBounty (One per bounty)

Represents a bounty posted by a BioDAO or researcher.

| Field                | Type        | Description                                      |
| -------------------- | ----------- | ------------------------------------------------ |
| `creator`            | `Pubkey`    | Bounty poster wallet                             |
| `bounty_number`      | `u64`       | Sequential ID (NB-0001, NB-0002, ...)            |
| `description`        | `[u8; 256]` | Description of null result needed                |
| `reward_amount`      | `u64`       | BIO reward in base units (6 decimals)            |
| `BIO_mint`           | `Pubkey`    | Token mint address (BIO)                         |
| `vault`              | `Pubkey`    | Vault token account PDA                          |
| `deadline`           | `i64`       | Deadline as Unix timestamp                       |
| `status`             | `u8`        | 0 = Open, 1 = Matched, 2 = Fulfilled, 3 = Closed |
| `matched_submission` | `Pubkey`    | BountySubmission PDA (zeroed if unmatched)       |
| `created_at`         | `i64`       | Unix timestamp                                   |
| `vault_bump`         | `u8`        | Vault PDA bump                                   |
| `bump`               | `u8`        | PDA bump seed                                    |

**PDA Seeds:** `["null_bounty", creator_pubkey, bounty_number_le_bytes]`\
**Vault Seeds:** `["bounty_vault", bounty_pda_key]`

<Warning>
  Bounty funds are escrowed immediately on creation. The vault token account uses the vault PDA itself as authority, ensuring funds can only be released via program CPI.
</Warning>

#### 4. BountySubmission (Links an NKA to a bounty)

Created when a researcher submits their NKA to a bounty.

| Field         | Type     | Description                             |
| ------------- | -------- | --------------------------------------- |
| `researcher`  | `Pubkey` | Claimant wallet                         |
| `null_result` | `Pubkey` | NullResult PDA key                      |
| `bounty`      | `Pubkey` | NullBounty PDA key                      |
| `status`      | `u8`     | 0 = Pending, 1 = Approved, 2 = Rejected |
| `created_at`  | `i64`    | Unix timestamp                          |
| `bump`        | `u8`     | PDA bump seed                           |

**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.

```rust theme={null}
pub fn initialize_protocol(
    ctx: Context<InitializeProtocol>,
    fee_basis_points: u16,
) -> Result<()>
```

**Emits:** `ProtocolInitialized { authority, fee_basis_points }`

#### `submit_null_result(...)`

Mints a new NKA. Increments `nka_counter`, creates the NullResult PDA, and records all fields.

```rust theme={null}
pub fn submit_null_result(
    ctx: Context<SubmitNullResult>,
    hypothesis: [u8; 128],
    methodology: [u8; 128],
    expected_outcome: [u8; 128],
    actual_outcome: [u8; 128],
    p_value: u32,
    sample_size: u32,
    data_hash: [u8; 32],
) -> Result<()>
```

**Emits:** `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.

```rust theme={null}
pub fn create_bounty(
    ctx: Context<CreateBounty>,
    description: [u8; 256],
    reward_amount: u64,
    deadline: i64,
) -> Result<()>
```

**Validation:** Requires `reward_amount > 0`\
**Emits:** `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.

```rust theme={null}
pub fn submit_to_bounty(
    ctx: Context<SubmitToBounty>
) -> Result<()>
```

**Security:** Only the NKA's owner can call this (enforced via `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.

```rust theme={null}
pub fn approve_bounty_submission(
    ctx: Context<ApproveBountySubmission>
) -> Result<()>
```

**Fee calculation:**

```rust theme={null}
let fee = reward_amount
    .checked_mul(fee_basis_points as u64)
    .unwrap()
    .checked_div(10000)
    .unwrap();
let payout = reward_amount.checked_sub(fee).unwrap();
```

**Emits:** `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.

```rust theme={null}
pub fn close_bounty(
    ctx: Context<CloseBounty>
) -> Result<()>
```

**Emits:** `BountyClosed { bounty_number, creator, refunded_amount }`

### Error codes

```rust theme={null}
#[error_code]
pub enum NullGraphError {
    #[msg("Bounty is not in the expected status for this operation")]
    InvalidBountyStatus,
    #[msg("Submission is not in the expected status")]
    InvalidSubmissionStatus,
    #[msg("The submission PDA does not match the bounty's matched_submission")]
    SubmissionMismatch,
    #[msg("The bounty deadline has passed")]
    BountyExpired,
    #[msg("Reward amount must be greater than zero")]
    InvalidRewardAmount,
    #[msg("Arithmetic overflow during fee calculation")]
    FeeOverflow,
}
```

## 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

```
ConnectionProvider (Solana RPC)
  -> WalletProvider (Phantom, auto-connect)
    -> WalletModalProvider
      -> ProgramProvider (Anchor Program instance)
        -> ToastProvider
          -> BrowserRouter
```

The `ProgramProvider` exposes a single Anchor `Program` instance constructed from the compiled IDL:

```typescript theme={null}
import { Program, AnchorProvider } from '@coral-xyz/anchor';
import { Nullgraph } from './lib/nullgraph_types';
import idl from './lib/nullgraph.json';

const program = new Program<Nullgraph>(
  idl as Nullgraph,
  provider
);
```

### Routes

| Route                  | Page             | Description                                   |
| ---------------------- | ---------------- | --------------------------------------------- |
| `/`                    | Landing          | Hero, problem/solution, how-it-works timeline |
| `/dashboard`           | Dashboard        | Protocol stats and full NKA registry grid     |
| `/submit`              | Submit           | Four-step guided form                         |
| `/market`              | Market           | Bounty card grid with create-bounty modal     |
| `/market/:bountyId`    | BountyDetail     | Single bounty view with submissions           |
| `/nka/:specimenNumber` | NullResultDetail | Full NKA metadata with Explorer links         |

### Data hooks

All data fetching uses custom React hooks that wrap `program.account.<type>.all()`:

* `useProtocolState()` - Fetches the singleton ProtocolState
* `useNullResults()` - Fetches all NullResult accounts
* `useBounties()` - Fetches all NullBounty accounts
* `useBountySubmissions(filters)` - Fetches BountySubmission accounts (filterable by bounty or researcher)

All hooks expose `{ data, loading, error, refetch }`.

<Info>
  No indexer required -- the frontend fetches all accounts on page load. For production scale, consider using a Solana indexer like Helius or GenesysGo.
</Info>

### Transaction hooks

Transaction hooks derive PDAs, build instructions, send transactions, and show toast notifications:

* `useSubmitNullResult()` - Mints a new NKA
* `useCreateBounty()` - Creates a bounty with escrowed BIO
* `useSubmitToBounty()` - Links an NKA to a bounty
* `useApproveBountySubmission()` - Approves a submission and pays out
* `useCloseBounty()` - Closes a bounty and refunds escrow

Example from `useSubmitNullResult`:

```typescript theme={null}
const [protocolStatePda] = PublicKey.findProgramAddressSync(
  [Buffer.from(SEEDS.PROTOCOL_STATE)],
  program.programId
);

const [nullResultPda] = PublicKey.findProgramAddressSync(
  [
    Buffer.from(SEEDS.NULL_RESULT),
    wallet.publicKey.toBuffer(),
    specimenNumberBuffer,
  ],
  program.programId
);

await program.methods
  .submitNullResult(
    hypothesisBytes,
    methodologyBytes,
    expectedOutcomeBytes,
    actualOutcomeBytes,
    pValueFixed,
    sampleSize,
    dataHashBytes
  )
  .accounts({
    researcher: wallet.publicKey,
    protocolState: protocolStatePda,
    nullResult: nullResultPda,
    systemProgram: SystemProgram.programId,
  })
  .rpc();
```

## Design system

Dark-mode **cyberpunk aesthetic** defined as Tailwind v4 `@theme` tokens:

* **Background:** `#060810` with a 40px CSS grid overlay
* **Surfaces:** `#0d1017` (surface), `#161c2a` (raised), `#232840` borders
* **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_one` constraints** ensure only the correct wallets can perform actions
* **Status guards** validate current status before every state mutation
* **Replay protection** -- PDA `init` constraints 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

<Warning>
  Never use `transfer` for SPL tokens -- always use `transfer_checked` to prevent decimal/mint confusion attacks.
</Warning>

### 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

| Technology | Version      | Purpose                 |
| ---------- | ------------ | ----------------------- |
| Solana     | Devnet       | Settlement layer        |
| Anchor     | 0.31.1       | Program framework       |
| anchor-spl | 0.31.1       | SPL Token Interface CPI |
| Rust       | 2021 edition | Program language        |

### Frontend

| Technology   | Version | Purpose                 |
| ------------ | ------- | ----------------------- |
| React        | 19.2    | UI framework            |
| TypeScript   | 5.9     | Type safety             |
| Vite         | 7.3     | Build tool + dev server |
| Tailwind CSS | 4.2     | Utility-first styling   |
| React Router | 7.13    | Client-side routing     |
| Lucide React | 0.575   | Icons                   |

### Solana client libraries

| Library                           | Purpose                                         |
| --------------------------------- | ----------------------------------------------- |
| `@coral-xyz/anchor` (0.32.1)      | Anchor client for IDL-based program interaction |
| `@solana/web3.js`                 | JSON-RPC client and transaction building        |
| `@solana/wallet-adapter-react`    | React hooks for wallet connection               |
| `@solana/wallet-adapter-react-ui` | Wallet connection modal and button              |
| `@solana/wallet-adapter-wallets`  | Phantom wallet adapter                          |
| `@solana/spl-token`               | SPL Token client utilities                      |

## What's next?

Now that you understand the architecture, explore:

* [Program reference](/api-reference/program-overview) - Complete on-chain program documentation
* [Frontend integration](/guides/frontend-integration) - Build a frontend for NullGraph
* [Security best practices](/guides/security) - Anchor security patterns used in NullGraph
