BIO Protocol Integration
NullGraph is natively built for Bio Protocol , aligning with its mission to create decentralized infrastructure for scientific research and data.
All NullGraph bounties are denominated in BIO tokens , directly integrating with Bio Protocol’s token economy and creating economic incentives for publishing negative results.
Why Bio Protocol?
Bio Protocol and NullGraph share a common vision:
Decentralized Science Both protocols aim to remove centralized gatekeepers from scientific publishing and funding.
Solana Settlement Built on the same Layer 1 blockchain, enabling seamless composability and shared wallet infrastructure.
Token Alignment BIO tokens power NullGraph’s bounty economy, creating direct economic utility for Bio Protocol’s ecosystem.
Data Integrity Both use cryptographic proofs (hashes, signatures) to ensure research data provenance and tamper-resistance.
BIO Token in NullGraph
Bounty Denomination
All bounty rewards are paid in BIO tokens :
pub struct NullBounty {
pub reward_amount : u64 , // BIO reward in base units (6 decimals)
pub usdc_mint : Pubkey , // Token mint address (BIO)
pub vault : Pubkey , // Vault holding escrowed BIO
// ...
}
While the field is named usdc_mint in the current implementation, it refers to the BIO token mint on Solana. This naming will be updated in future versions.
Token Economics
BIO flows through the protocol in three paths:
Action BIO Flow Fee Create Bounty Creator → Vault 0% Approve Submission Vault → Researcher (97.5%) Vault → Treasury (2.5%) 2.5% Close Bounty Vault → Creator 0%
SPL Token Interface
NullGraph uses Anchor’s SPL Token Interface for all BIO token operations:
use anchor_spl :: token_interface :: {
Mint , TokenAccount , TokenInterface ,
transfer_checked, TransferChecked ,
};
Why Token Interface?
Token-2022 Ready Compatible with both SPL Token (legacy) and Token-2022 (new standard) without code changes.
Type Safety InterfaceAccount<TokenAccount> enforces correct account types at compile time.
Future-Proof Automatically supports new token features (transfer fees, confidential transfers, etc.).
Best Practice Recommended by Solana Foundation for all new token programs.
Transfer Example
All BIO transfers use transfer_checked:
transfer_checked (
CpiContext :: new (
ctx . accounts . token_program . to_account_info (),
TransferChecked {
from : ctx . accounts . creator_usdc_ata . to_account_info (),
mint : ctx . accounts . usdc_mint . to_account_info (),
to : ctx . accounts . vault . to_account_info (),
authority : ctx . accounts . creator . to_account_info (),
},
),
reward_amount , // Amount in base units
decimals , // Must match mint (6 for BIO)
) ? ;
Why transfer_checked?
Validates mint — ensures tokens are from the correct BIO mint
Validates decimals — prevents precision errors (e.g., treating 6-decimal tokens as 9-decimal)
Safer than transfer — explicit validation at instruction level
Using transfer without _checked is considered unsafe in production token programs. Always use transfer_checked.
BIO Token Decimals
BIO tokens use 6 decimals (same as USDC):
1 BIO = 1_000_000 base units
0.5 BIO = 500_000 base units
100 BIO = 100_000_000 base units
Frontend Conversion
const BIO_DECIMALS = 6 ;
// Human-readable to base units
const bioToBaseUnits = ( bio : number ) : number => {
return Math . floor ( bio * Math . pow ( 10 , BIO_DECIMALS ));
};
// Base units to human-readable
const baseUnitsToBio = ( raw : number ) : number => {
return raw / Math . pow ( 10 , BIO_DECIMALS );
};
// Examples
bioToBaseUnits ( 10.5 ) // 10_500_000
baseUnitsToBio ( 2_500_000 ) // 2.5
On-Chain Validation
The program reads decimals directly from the mint account:
let decimals = ctx . accounts . usdc_mint . decimals; // 6 for BIO
transfer_checked (
// ...
reward_amount ,
decimals , // Must match mint's decimals
) ? ;
If decimals doesn’t match the mint, the transaction fails — preventing loss of funds.
Associated Token Accounts (ATAs)
What are ATAs?
ATAs are deterministic token accounts derived from a wallet address and token mint:
import { getAssociatedTokenAddressSync } from '@solana/spl-token' ;
import { TOKEN_2022_PROGRAM_ID } from '@solana/spl-token' ;
const BIO_MINT = new PublicKey ( 'BioTokenMintAddress...' );
const walletAddress = new PublicKey ( 'UserWalletAddress...' );
const ata = getAssociatedTokenAddressSync (
BIO_MINT ,
walletAddress ,
false , // allowOwnerOffCurve
TOKEN_2022_PROGRAM_ID // Use Token-2022 program
);
console . log ( 'User BIO ATA:' , ata . toBase58 ());
ATA Benefits
Predictable Address Same wallet + mint always produces the same ATA address.
One Account per Token Users have exactly one ATA for each token mint they hold.
Easy Lookup No need to track custom token accounts — derive on-demand.
Standard Universal across all Solana dApps and wallets.
Creating ATAs
Anchor’s #[account(init, ...)] can automatically create ATAs:
#[account(
init,
payer = creator,
token :: mint = usdc_mint,
token :: authority = vault,
seeds = [ b"bounty_vault" , bounty . key() . as_ref()],
bump ,
)]
pub vault : InterfaceAccount <' info , TokenAccount >,
For user ATAs, use the associated_token constraint:
// Frontend: Ensure user has BIO ATA before transaction
const ensureAta = async (
connection : Connection ,
payer : Keypair ,
owner : PublicKey ,
mint : PublicKey
) => {
const ata = getAssociatedTokenAddressSync (
mint ,
owner ,
false ,
TOKEN_2022_PROGRAM_ID
);
const account = await connection . getAccountInfo ( ata );
if ( ! account ) {
// Create ATA if it doesn't exist
const tx = new Transaction (). add (
createAssociatedTokenAccountInstruction (
payer . publicKey ,
ata ,
owner ,
mint ,
TOKEN_2022_PROGRAM_ID
)
);
await sendAndConfirmTransaction ( connection , tx , [ payer ]);
console . log ( 'Created ATA:' , ata . toBase58 ());
}
return ata ;
};
Vault Escrow Mechanism
Bounties lock BIO in PDA-controlled vault accounts :
seeds = [ b"bounty_vault" , bounty_pda_key ]
Vault Authority
The vault is its own authority (PDA signer):
#[account(
init,
payer = creator,
token :: mint = usdc_mint,
token :: authority = vault, // Vault signs for itself
seeds = [ b"bounty_vault" , bounty . key() . as_ref()],
bump ,
)]
pub vault : InterfaceAccount <' info , TokenAccount >,
PDA Signer Seeds
Only the program can sign for the vault using signer seeds :
let bounty_key = bounty . key ();
let vault_seeds : & [ & [ u8 ]] = & [
b"bounty_vault" ,
bounty_key . as_ref (),
& [ bounty . vault_bump],
];
transfer_checked (
CpiContext :: new_with_signer (
ctx . accounts . token_program . to_account_info (),
TransferChecked { /* ... */ },
& [ vault_seeds ], // PDA signer
),
amount ,
decimals ,
) ? ;
Security: No Private Keys The vault has no private key . Only the program, using the correct PDA seeds, can authorize transfers. This eliminates custodial risk.
Cross-Program Invocation (CPI)
All token operations use CPI to the SPL Token Interface program:
transfer_checked (
CpiContext :: new_with_signer (
ctx . accounts . token_program . to_account_info (), // SPL Token Interface
TransferChecked {
from : vault ,
mint : bio_mint ,
to : researcher_ata ,
authority : vault , // PDA signer
},
& [ vault_seeds ],
),
payout_amount ,
6 , // BIO decimals
) ? ;
CPI Flow Diagram
┌─────────────────────┐
│ NullGraph Program │
│ (approve_bounty) │
└──────────┬──────────┘
│ CPI
v
┌─────────────────────┐
│ SPL Token Interface │
│ (transfer_checked) │
└──────────┬──────────┘
│
v
┌─────────────┐ ┌──────────────┐
│ Vault ATA │ --> │ Researcher │
│ (escrowed) │ │ ATA │
└─────────────┘ └──────────────┘
CPI allows programs to call other programs atomically — all operations succeed or all fail together.
Bio Protocol Alignment
NullGraph advances Bio Protocol’s core mission:
1. Shared Settlement Layer
Both protocols use Solana for:
Fast finality (~400ms)
Low transaction costs (~$0.00025)
High throughput (65,000 TPS)
Composable program architecture
2. BIO Token Utility
NullGraph creates real utility for BIO tokens:
Bounty Payments BioDAOs spend BIO to acquire valuable null results
Researcher Incentives Researchers earn BIO for publishing negative data
Protocol Fees Treasury accumulates BIO for ecosystem development
Liquidity Demand More bounties = more BIO demand = stronger token economics
3. Data Provenance
NullGraph uses SHA-256 hashing (same as Bio Protocol) for data integrity:
pub data_hash : [ u8 ; 32 ], // SHA-256 of dataset
This provides the same chain-of-custody guarantees Bio Protocol uses for research data provenance:
✅ Tamper-proof — changing one byte invalidates the hash
✅ Verifiable — anyone can recompute and verify
✅ Permanent — stored on-chain forever
✅ Privacy-preserving — hash reveals nothing about data contents
4. BioDAO Integration
BioDAOs can use NullGraph to:
Fund Targeted Research
Post bounties for specific null results needed for their research agenda
Avoid Redundancy
Browse existing NKAs before funding new experiments
Build Knowledge Graphs
Link NKAs to create comprehensive negative result databases
Community Verification
Use the status field (Pending/Verified/Disputed) for peer review
Future Integrations
Decentralized Verification Community-driven NKA validation network with BIO token incentives
Knowledge Graph Indexing On-chain graph linking NKAs by hypothesis, methodology, and outcomes
BioDAO Governance DAO-controlled treasury and protocol parameters
Cross-Chain Bridge Integrate with Bio Protocol’s Ethereum contracts via Wormhole
Code Example: BIO Transfer
Complete example of transferring BIO from vault to researcher:
use anchor_spl :: token_interface :: {
transfer_checked, TransferChecked ,
};
pub fn payout_researcher (
vault : & InterfaceAccount < TokenAccount >,
researcher_ata : & InterfaceAccount < TokenAccount >,
mint : & InterfaceAccount < Mint >,
token_program : & Interface < TokenInterface >,
vault_seeds : & [ & [ u8 ]],
amount : u64 ,
) -> Result <()> {
let decimals = mint . decimals; // 6 for BIO
transfer_checked (
CpiContext :: new_with_signer (
token_program . to_account_info (),
TransferChecked {
from : vault . to_account_info (),
mint : mint . to_account_info (),
to : researcher_ata . to_account_info (),
authority : vault . to_account_info (), // Vault is authority
},
& [ vault_seeds ], // PDA signer
),
amount ,
decimals ,
) ? ;
msg! ( "Transferred {} BIO to researcher" , amount as f64 / 1_000_000.0 );
Ok (())
}
Querying BIO Balances
Check user’s BIO balance:
const getBioBalance = async (
connection : Connection ,
walletAddress : PublicKey ,
bioMint : PublicKey
) => {
const ata = getAssociatedTokenAddressSync (
bioMint ,
walletAddress ,
false ,
TOKEN_2022_PROGRAM_ID
);
try {
const balance = await connection . getTokenAccountBalance ( ata );
return parseFloat ( balance . value . uiAmount || '0' );
} catch ( e ) {
// ATA doesn't exist yet
return 0 ;
}
};
// Usage
const balance = await getBioBalance (
connection ,
wallet . publicKey ,
BIO_MINT
);
console . log ( `You have ${ balance } BIO` );
Next Steps
Null Knowledge Assets Learn how NKAs store scientific data on-chain
Bounty Marketplace Explore how to create and fulfill BIO-denominated bounties