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

# Account Structures

> Complete reference for all NullGraph program data accounts

The NullGraph program uses four primary account types to manage protocol state, null results, bounties, and submissions. All accounts are Program Derived Addresses (PDAs) owned by the program.

## ProtocolState

Global singleton storing protocol configuration and auto-incrementing counters.

**Location**: `lib.rs:472-481`

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

**Space**: `8 + ProtocolState::INIT_SPACE` (8-byte discriminator + 83 bytes)

### Fields

<ParamField path="authority" type="Pubkey" required>
  Protocol admin wallet with permission to modify global settings. Set during `initialize_protocol`.
</ParamField>

<ParamField path="nka_counter" type="u64" required>
  Auto-incrementing counter for NKA specimen numbers. Starts at 0, incremented by `submit_null_result`. Used to derive sequential IDs (NKA-0001, NKA-0002, ...).
</ParamField>

<ParamField path="bounty_counter" type="u64" required>
  Auto-incrementing counter for bounty numbers. Starts at 0, incremented by `create_bounty`. Used to derive sequential IDs (NB-0001, NB-0002, ...).
</ParamField>

<ParamField path="fee_basis_points" type="u16" required>
  Protocol fee rate in basis points (1/100th of a percent). Default: 250 (2.5%). Used in `approve_bounty_submission` to calculate treasury fee.

  **Example**: `250 = 2.5%`, `100 = 1%`, `1000 = 10%`
</ParamField>

<ParamField path="treasury" type="Pubkey" required>
  Treasury wallet address receiving protocol fees. Validated by admin during initialization. Must have an associated USDC token account.
</ParamField>

<ParamField path="bump" type="u8" required>
  PDA bump seed for the ProtocolState account. Stored to avoid recomputation in subsequent instructions.
</ParamField>

### Code Reference

```rust theme={null}
#[account]
#[derive(InitSpace)]
pub struct ProtocolState {
    pub authority: Pubkey,        // 32
    pub nka_counter: u64,         // 8
    pub bounty_counter: u64,      // 8
    pub fee_basis_points: u16,    // 2
    pub treasury: Pubkey,         // 32
    pub bump: u8,                 // 1
}
```

***

## NullResult

Stores complete scientific metadata for a single Null Knowledge Asset (NKA).

**Location**: `lib.rs:483-498`

**PDA Seeds**: `["null_result", researcher.key().as_ref(), &(nka_counter + 1).to_le_bytes()]`

**Space**: `8 + NullResult::INIT_SPACE` (8-byte discriminator + 601 bytes)

### Fields

<ParamField path="researcher" type="Pubkey" required>
  Wallet address of the researcher who submitted this NKA. Used for `has_one` constraint in `submit_to_bounty`.
</ParamField>

<ParamField path="specimen_number" type="u64" required>
  Sequential identifier assigned from `protocol_state.nka_counter`. Displayed as `NKA-{specimen_number:04}` in the frontend.
</ParamField>

<ParamField path="hypothesis" type="[u8; 128]" required>
  UTF-8 encoded hypothesis tested in the experiment. Fixed-size buffer, zero-padded. Maximum 128 bytes.

  **Example**: "Increasing dopamine levels improves working memory in rats"
</ParamField>

<ParamField path="methodology" type="[u8; 128]" required>
  UTF-8 encoded summary of experimental methodology. Fixed-size buffer, zero-padded. Maximum 128 bytes.

  **Example**: "Double-blind RCT with 50 subjects, t-test analysis"
</ParamField>

<ParamField path="expected_outcome" type="[u8; 128]" required>
  UTF-8 encoded description of what was expected. Fixed-size buffer, zero-padded. Maximum 128 bytes.

  **Example**: "Significant improvement in test scores (p \< 0.05)"
</ParamField>

<ParamField path="actual_outcome" type="[u8; 128]" required>
  UTF-8 encoded description of what actually occurred. Fixed-size buffer, zero-padded. Maximum 128 bytes.

  **Example**: "No significant difference observed (p = 0.42)"
</ParamField>

<ParamField path="p_value" type="u32" required>
  Statistical p-value stored as fixed-point integer (multiply by 10000). To convert: `p_value / 10000.0`.

  **Example**: `4200` = 0.42, `500` = 0.05, `8700` = 0.87
</ParamField>

<ParamField path="sample_size" type="u32" required>
  Number of subjects/observations in the experiment. Stored as raw integer.

  **Example**: `50`, `1000`, `250`
</ParamField>

<ParamField path="data_hash" type="[u8; 32]" required>
  SHA-256 hash of the underlying dataset file. Provides tamper-proof link between on-chain record and off-chain data. Can be all zeros if no file attached.

  **Computation**: `SHA256(raw_data_file_bytes)`
</ParamField>

<ParamField path="status" type="u8" required>
  Verification status of the null result:

  * `0` = Pending (default)
  * `1` = Verified (community validated)
  * `2` = Disputed (challenged by peers)

  Currently set to 0 on creation. Future versions may implement community verification.
</ParamField>

<ParamField path="created_at" type="i64" required>
  Unix timestamp of submission. Set via `Clock::get()?.unix_timestamp` in `submit_null_result:62`.
</ParamField>

<ParamField path="bump" type="u8" required>
  PDA bump seed for this NullResult account.
</ParamField>

### Code Reference

```rust theme={null}
#[account]
#[derive(InitSpace)]
pub struct NullResult {
    pub researcher: Pubkey,       // 32
    pub specimen_number: u64,     // 8
    pub hypothesis: [u8; 128],    // 128
    pub methodology: [u8; 128],   // 128
    pub expected_outcome: [u8; 128], // 128
    pub actual_outcome: [u8; 128],   // 128
    pub p_value: u32,             // 4
    pub sample_size: u32,         // 4
    pub data_hash: [u8; 32],      // 32
    pub status: u8,               // 1
    pub created_at: i64,          // 8
    pub bump: u8,                 // 1
}
```

***

## NullBounty

Bounty posted by a creator seeking a specific null result. Escrows USDC in a PDA-controlled vault.

**Location**: `lib.rs:500-515`

**PDA Seeds**: `["null_bounty", creator.key().as_ref(), &(bounty_counter + 1).to_le_bytes()]`

**Vault Seeds**: `["bounty_vault", bounty.key().as_ref()]`

**Space**: `8 + NullBounty::INIT_SPACE` (8-byte discriminator + 419 bytes)

### Fields

<ParamField path="creator" type="Pubkey" required>
  Wallet address of the bounty creator. Used for `has_one` constraint in `approve_bounty_submission` and `close_bounty`.
</ParamField>

<ParamField path="bounty_number" type="u64" required>
  Sequential identifier assigned from `protocol_state.bounty_counter`. Displayed as `NB-{bounty_number:04}` in the frontend.
</ParamField>

<ParamField path="description" type="[u8; 256]" required>
  UTF-8 encoded description of the desired null result. Fixed-size buffer, zero-padded. Maximum 256 bytes.

  **Example**: "Negative result for CRISPR-based treatment of Huntington's disease in mouse models"
</ParamField>

<ParamField path="reward_amount" type="u64" required>
  USDC reward in base units (6 decimals). Must be > 0 (validated in `create_bounty:78`). Escrowed in vault on creation.

  **Example**: `1000000` = 1 USDC, `2500000` = 2.5 USDC
</ParamField>

<ParamField path="usdc_mint" type="Pubkey" required>
  USDC token mint address. Stored to validate transfers in `approve_bounty_submission` and `close_bounty`.
</ParamField>

<ParamField path="vault" type="Pubkey" required>
  Address of the PDA token account holding escrowed USDC. Authority is the vault itself.
</ParamField>

<ParamField path="deadline" type="i64" required>
  Unix timestamp deadline for bounty submissions. Currently not enforced by program but used for frontend filtering.
</ParamField>

<ParamField path="status" type="u8" required>
  Bounty lifecycle status:

  * `0` = Open (accepting submissions)
  * `1` = Matched (submission linked, pending approval)
  * `2` = Fulfilled (submission approved, payout complete)
  * `3` = Closed (refunded, no longer active)
</ParamField>

<ParamField path="matched_submission" type="Pubkey" required>
  Address of the BountySubmission PDA if status is Matched/Fulfilled. Set to `Pubkey::default()` (all zeros) when Open or Closed.
</ParamField>

<ParamField path="created_at" type="i64" required>
  Unix timestamp of bounty creation. Set via `Clock::get()?.unix_timestamp` in `create_bounty:94`.
</ParamField>

<ParamField path="vault_bump" type="u8" required>
  PDA bump seed for the vault token account. Used to derive signer seeds for CPI transfers.
</ParamField>

<ParamField path="bump" type="u8" required>
  PDA bump seed for this NullBounty account.
</ParamField>

### Code Reference

```rust theme={null}
#[account]
#[derive(InitSpace)]
pub struct NullBounty {
    pub creator: Pubkey,          // 32
    pub bounty_number: u64,       // 8
    pub description: [u8; 256],   // 256
    pub reward_amount: u64,       // 8
    pub usdc_mint: Pubkey,        // 32
    pub vault: Pubkey,            // 32
    pub deadline: i64,            // 8
    pub status: u8,               // 1
    pub matched_submission: Pubkey, // 32
    pub created_at: i64,          // 8
    pub vault_bump: u8,           // 1
    pub bump: u8,                 // 1
}
```

***

## BountySubmission

Junction account linking a researcher's NullResult to a specific NullBounty.

**Location**: `lib.rs:517-526`

**PDA Seeds**: `["bounty_submission", bounty.key().as_ref(), null_result.key().as_ref()]`

**Space**: `8 + BountySubmission::INIT_SPACE` (8-byte discriminator + 106 bytes)

### Fields

<ParamField path="researcher" type="Pubkey" required>
  Wallet address of the researcher submitting their NKA. Copied from the linked NullResult account.
</ParamField>

<ParamField path="null_result" type="Pubkey" required>
  Address of the NullResult PDA being submitted. Must be owned by the submitting researcher.
</ParamField>

<ParamField path="bounty" type="Pubkey" required>
  Address of the NullBounty PDA this submission is for. Must have status Open when created.
</ParamField>

<ParamField path="status" type="u8" required>
  Submission approval status:

  * `0` = Pending (awaiting creator review)
  * `1` = Approved (accepted, payout executed)
  * `2` = Rejected (declined by creator - not currently used)
</ParamField>

<ParamField path="created_at" type="i64" required>
  Unix timestamp of submission. Set via `Clock::get()?.unix_timestamp` in `submit_to_bounty:131`.
</ParamField>

<ParamField path="bump" type="u8" required>
  PDA bump seed for this BountySubmission account.
</ParamField>

### Code Reference

```rust theme={null}
#[account]
#[derive(InitSpace)]
pub struct BountySubmission {
    pub researcher: Pubkey,       // 32
    pub null_result: Pubkey,      // 32
    pub bounty: Pubkey,           // 32
    pub status: u8,               // 1
    pub created_at: i64,          // 8
    pub bump: u8,                 // 1
}
```

***

## Account Size Summary

| Account          | Discriminator | Data | Total     |
| ---------------- | ------------- | ---- | --------- |
| ProtocolState    | 8             | 83   | 91 bytes  |
| NullResult       | 8             | 601  | 609 bytes |
| NullBounty       | 8             | 419  | 427 bytes |
| BountySubmission | 8             | 106  | 114 bytes |

<Info>
  All accounts use Anchor's `#[derive(InitSpace)]` macro to automatically calculate the correct space allocation.
</Info>
