Account Structures
Every piece of state in Sandbox GHI is stored in one of three account types. All are owned by the sandbox_ghi program.
MiningPool
Global singleton. Stores protocol-level state including the current halving epoch and total emissions.
PDA seeds: ["mining_pool"]
#[account]
pub struct MiningPool {
pub authority: Pubkey, // program upgrade authority
pub ghi_mint: Pubkey, // $GHI SPL token mint
pub ghi_vault: Pubkey, // token account holding mining rewards
pub total_staked: u32, // total NFTs currently staked
pub epoch_start: i64, // unix timestamp of contract deployment
pub total_distributed: u64, // cumulative $GHI distributed (u64, 6 decimals)
pub bump: u8,
}
// Discriminator: 8 bytes
// Total size: 8 + 32 + 32 + 32 + 4 + 8 + 8 + 1 = 125 bytesHalving epoch is derived at read time:
let year = (Clock::get()?.unix_timestamp - pool.epoch_start) / (86_400 * 365);StakeAccount
Created per staked NFT. Tracks when the NFT was staked and when rewards were last claimed.
PDA seeds: ["stake", nft_mint.key(), owner.key()]
#[account]
pub struct StakeAccount {
pub owner: Pubkey, // wallet that staked the NFT
pub nft_mint: Pubkey, // mint address of the staked Agent NFT
pub tier: Tier, // Common | Rare | Legendary
pub staked_at: i64, // unix timestamp of initial stake
pub last_claimed: i64, // unix timestamp of last claim (= staked_at at creation)
pub bump: u8,
}
// Total size: 8 + 32 + 32 + 1 + 8 + 8 + 1 = 90 bytes
#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)]
pub enum Tier {
Common, // base_rate = 32 $GHI/day
Rare, // base_rate = 80 $GHI/day
Legendary, // base_rate = 320 $GHI/day
}Reward Calculation
Rewards are calculated lazily — never stored, always computed on-chain at claim/unstake time:
pub fn calculate_rewards(stake: &StakeAccount, pool: &MiningPool, now: i64) -> u64 {
let elapsed_seconds = now - stake.last_claimed;
let elapsed_days = elapsed_seconds as u64 / 86_400;
// halving: year 0 = full rate, year 1 = half, year 2 = quarter...
let year = (now - pool.epoch_start) / (86_400 * 365);
let base_rate = match stake.tier {
Tier::Common => 32_000_000, // 32 $GHI (6 decimals)
Tier::Rare => 80_000_000, // 80 $GHI
Tier::Legendary => 320_000_000, // 320 $GHI
};
let current_rate = base_rate >> year; // bitshift = halving
elapsed_days * current_rate
}$GHI uses 6 decimal places — consistent with most Solana SPL tokens. All amounts in the program are in raw units (e.g., 32_000_000 = 32 $GHI).
AgentAccount
Created when a user deploys an Agent Forge agent. Stores agent configuration, $GHI budget, and reputation.
PDA seeds: ["agent", nft_mint.key(), owner.key()]
#[account]
pub struct AgentAccount {
pub owner: Pubkey, // NFT owner / agent controller
pub nft_mint: Pubkey, // Agent NFT that backs this agent
pub task_type: TaskType, // Monitor | Trader | Optimizer | Custom
pub ghi_budget: u64, // remaining $GHI budget for task execution
pub reputation: u64, // cumulative reputation score
pub tasks_completed: u32, // total tasks completed
pub deployed_at: i64, // unix timestamp of deployment
pub is_active: bool, // false when paused or closed
pub bump: u8,
}
// Total size: 8 + 32 + 32 + 1 + 8 + 8 + 4 + 8 + 1 + 1 = 103 bytes
#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub enum TaskType {
Monitor, // watches on-chain conditions, triggers actions
Trader, // executes trades by configurable strategy
Optimizer, // manages LP positions / portfolio rebalancing
Custom, // user-defined task logic
}Reputation Formula
reputation += task_weight * completion_bonus;
// task_weight: set per task type by protocol (1–10)
// completion_bonus: 100 for on-time, 50 for late, 0 for failedToken Accounts (SPL)
Sandbox GHI uses standard SPL token accounts — no custom token logic:
| Account | Description |
|---|---|
| GHI Vault | PDA-owned ATA holding all mining reserve |
| User ATA | Associated Token Account created at first claim |
| Agent ATA | Agent's own ATA — can hold and spend $GHI autonomously |