Skip to main content
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

authority
Pubkey
required
Protocol admin wallet with permission to modify global settings. Set during initialize_protocol.
nka_counter
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, …).
bounty_counter
u64
required
Auto-incrementing counter for bounty numbers. Starts at 0, incremented by create_bounty. Used to derive sequential IDs (NB-0001, NB-0002, …).
fee_basis_points
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%
treasury
Pubkey
required
Treasury wallet address receiving protocol fees. Validated by admin during initialization. Must have an associated USDC token account.
bump
u8
required
PDA bump seed for the ProtocolState account. Stored to avoid recomputation in subsequent instructions.

Code Reference

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

researcher
Pubkey
required
Wallet address of the researcher who submitted this NKA. Used for has_one constraint in submit_to_bounty.
specimen_number
u64
required
Sequential identifier assigned from protocol_state.nka_counter. Displayed as NKA-{specimen_number:04} in the frontend.
hypothesis
[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”
methodology
[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”
expected_outcome
[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)”
actual_outcome
[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)”
p_value
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
sample_size
u32
required
Number of subjects/observations in the experiment. Stored as raw integer.Example: 50, 1000, 250
data_hash
[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)
status
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.
created_at
i64
required
Unix timestamp of submission. Set via Clock::get()?.unix_timestamp in submit_null_result:62.
bump
u8
required
PDA bump seed for this NullResult account.

Code Reference

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

creator
Pubkey
required
Wallet address of the bounty creator. Used for has_one constraint in approve_bounty_submission and close_bounty.
bounty_number
u64
required
Sequential identifier assigned from protocol_state.bounty_counter. Displayed as NB-{bounty_number:04} in the frontend.
description
[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”
reward_amount
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
usdc_mint
Pubkey
required
USDC token mint address. Stored to validate transfers in approve_bounty_submission and close_bounty.
vault
Pubkey
required
Address of the PDA token account holding escrowed USDC. Authority is the vault itself.
deadline
i64
required
Unix timestamp deadline for bounty submissions. Currently not enforced by program but used for frontend filtering.
status
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)
matched_submission
Pubkey
required
Address of the BountySubmission PDA if status is Matched/Fulfilled. Set to Pubkey::default() (all zeros) when Open or Closed.
created_at
i64
required
Unix timestamp of bounty creation. Set via Clock::get()?.unix_timestamp in create_bounty:94.
vault_bump
u8
required
PDA bump seed for the vault token account. Used to derive signer seeds for CPI transfers.
bump
u8
required
PDA bump seed for this NullBounty account.

Code Reference

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

researcher
Pubkey
required
Wallet address of the researcher submitting their NKA. Copied from the linked NullResult account.
null_result
Pubkey
required
Address of the NullResult PDA being submitted. Must be owned by the submitting researcher.
bounty
Pubkey
required
Address of the NullBounty PDA this submission is for. Must have status Open when created.
status
u8
required
Submission approval status:
  • 0 = Pending (awaiting creator review)
  • 1 = Approved (accepted, payout executed)
  • 2 = Rejected (declined by creator - not currently used)
created_at
i64
required
Unix timestamp of submission. Set via Clock::get()?.unix_timestamp in submit_to_bounty:131.
bump
u8
required
PDA bump seed for this BountySubmission account.

Code Reference

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

AccountDiscriminatorDataTotal
ProtocolState88391 bytes
NullResult8601609 bytes
NullBounty8419427 bytes
BountySubmission8106114 bytes
All accounts use Anchor’s #[derive(InitSpace)] macro to automatically calculate the correct space allocation.

Build docs developers (and LLMs) love