use solana_sdk::pubkey::Pubkey;
use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Network {
Devnet,
Mainnet,
}
impl Default for Network {
fn default() -> Self {
Network::Mainnet
}
}
pub struct ProgramIds {
pub vault: Pubkey,
pub cred: Pubkey,
pub oxo: Pubkey,
pub vtp: Pubkey,
pub avp: Pubkey,
pub shopping: Pubkey,
}
impl ProgramIds {
pub fn for_network(network: Network) -> Self {
match network {
Network::Devnet => Self::devnet(),
Network::Mainnet => Self::mainnet(),
}
}
pub fn devnet() -> Self {
Self {
vault: Pubkey::from_str("9gKRCrUpHv9CRHwYMmm2zP5bN1bUKgyi7BxuS5jifX4x").unwrap(),
cred: Pubkey::from_str("JCzhFrDvipRr5CYtWoV3szpaJ3RP59gbAn4zCi5CZcv8").unwrap(),
oxo: Pubkey::from_str("AZZQdfcmejPjebNnneqTeVbkbVY8nyZuomuChbLqwhHj").unwrap(),
vtp: Pubkey::from_str("3mP7L31af6MV6FnqWG6E78JELNuizWDwBK3rC3g3WjSK").unwrap(),
avp: Pubkey::from_str("FE3ZJBqVcqP6ar2pnndMghgNb3pi4mrjhVoAS7x4BVCA").unwrap(),
shopping: Pubkey::from_str("FSqRkH7nkGHP3VpHwFE667PVAfLKfSGaPMgTrXpJZJoJ").unwrap(),
}
}
pub fn mainnet() -> Self {
Self {
vault: Pubkey::from_str("J8HhLeRv5iQaSyYQBXJoDwDKbw4V8uA84WN93YrVSWQT").unwrap(),
cred: Pubkey::from_str("HYQJwCJ5wH9o4sb9sVPyvSSeY9DtsznZGy2AfpiBaBaG").unwrap(),
oxo: Pubkey::from_str("AZZQdfcmejPjebNnneqTeVbkbVY8nyZuomuChbLqwhHj").unwrap(),
vtp: Pubkey::from_str("3mP7L31af6MV6FnqWG6E78JELNuizWDwBK3rC3g3WjSK").unwrap(),
avp: Pubkey::from_str("FE3ZJBqVcqP6ar2pnndMghgNb3pi4mrjhVoAS7x4BVCA").unwrap(),
shopping: Pubkey::from_str("HiewKEBy6YVn3Xi5xdhyrsfPr3KjKg6Jy8PXemyeteXJ").unwrap(),
}
}
}
pub struct TokenMints {
pub cred: Pubkey,
pub oxo: Pubkey,
pub usdc: Pubkey,
}
impl TokenMints {
pub fn for_network(network: Network) -> Self {
match network {
Network::Devnet => Self::devnet(),
Network::Mainnet => Self::mainnet(),
}
}
pub fn devnet() -> Self {
Self {
cred: Pubkey::from_str("DDfGosZop37FA97MqCrXA6fKMu8kTwduQ1JsdYJFoNBP").unwrap(),
oxo: Pubkey::from_str("A8UkSCWuFvgBjeqWzZVowyCyUgW3JcyPB8VscPycd9hd").unwrap(),
usdc: Pubkey::from_str("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU").unwrap(),
}
}
pub fn mainnet() -> Self {
Self {
cred: Pubkey::from_str("9GQMCAK3MpZF1hEbwqA9d4mRGtippGV9hyr8fxmz6eA").unwrap(),
oxo: Pubkey::from_str("A8UkSCWuFvgBjeqWzZVowyCyUgW3JcyPB8VscPycd9hd").unwrap(),
usdc: Pubkey::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v").unwrap(),
}
}
}
pub struct MainnetState {
pub cred_config: Pubkey,
pub shopping_state: Pubkey,
pub reserve_vault: Pubkey,
pub treasury: Pubkey,
pub staker_pool: Pubkey,
}
impl MainnetState {
pub fn get() -> Self {
Self {
cred_config: Pubkey::from_str("FzwRshrm2Pu8ygMPqHwbjb7yWBiQ2pQc3Lgk8XJRpvNM").unwrap(),
shopping_state: Pubkey::from_str("92nXnNG5gJFDEqXE8Lyrqr3nDZiaD2atBCoRSNY5JD3n").unwrap(),
reserve_vault: Pubkey::from_str("Ga2PRtFu3TRsTjN1QxdVpvtjVP7kb1rAkZse9MXWXjPh").unwrap(),
treasury: Pubkey::from_str("CTRnn7vC1EtL1YDLU3x3iidEcCWRmZupEXHKQfL17Fxa").unwrap(),
staker_pool: Pubkey::from_str("zEJf7Vy7ZDjvoMTBndAvkvxRpACUzCboQg2dP8H8R6k").unwrap(),
}
}
}
pub const CRED_DECIMALS: u8 = 6;
pub const OXO_DECIMALS: u8 = 6;
pub const LAMPORTS_PER_CRED: u64 = 1_000_000;
pub const LAMPORTS_PER_OXO: u64 = 1_000_000;
pub const USER_SHARE_BPS: u16 = 8000; pub const TREASURY_SHARE_BPS: u16 = 1400; pub const STAKER_SHARE_BPS: u16 = 600;
pub const STAKING_APY: [(u16, u16); 5] = [
(365, 1500), (180, 1200), (90, 800), (30, 500), (7, 300), ];
pub const STAKE_30D_APY_BPS: u16 = 800; pub const STAKE_90D_APY_BPS: u16 = 1200; pub const STAKE_180D_APY_BPS: u16 = 1600; pub const STAKE_365D_APY_BPS: u16 = 2000;
pub const MIN_STAKE_DAYS: u16 = 7;
pub const MAX_STAKE_DAYS: u16 = 730;
pub const EXTRACTION_FEE_BPS: u16 = 500;
pub const OXO_TOTAL_SUPPLY: u64 = 1_000_000_000_000_000; pub const MIN_LOCK_SECONDS: i64 = 15_552_000; pub const MAX_LOCK_SECONDS: i64 = 126_144_000; pub const GRADUATION_THRESHOLD: u64 = 25_000_000_000; pub const AGENT_CREATION_FEE: u64 = 500_000_000;
pub const TRANSFER_FEE_BPS: u16 = 10; pub const ESCROW_FEE_BPS: u16 = 25; pub const MAX_ARBITERS: u8 = 5;
pub const MAX_CONDITIONS: u8 = 10;
pub const MIN_SERVICE_AGENT_STAKE: u64 = 500_000_000; pub const MAX_CAPABILITIES: u8 = 20;
pub const MAX_METADATA_LEN: usize = 200;
pub const SESSION_KEY_TTL_HOURS: u32 = 24;
pub const SESSION_KEY_MAX_ACTIONS: u32 = 1000;
pub const CONTEXT_RELOAD_TARGET_MS: u32 = 100;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum CaptureType {
Shopping = 0,
Data = 1,
Presence = 2,
Attention = 3,
Referral = 4,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PermissionLevel {
None = 0,
Read = 1,
Capture = 2,
Guided = 3,
Autonomous = 4,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum EscrowStatus {
Active = 0,
Released = 1,
Cancelled = 2,
Disputed = 3,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum AgentType {
Personal = 0,
Service = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum AgentStatus {
Active = 0,
Suspended = 1,
Revoked = 2,
}
pub const DEVNET_RPC: &str = "https://api.devnet.solana.com";
pub const MAINNET_RPC: &str = "https://api.mainnet-beta.solana.com";
pub fn rpc_endpoint(network: Network) -> &'static str {
match network {
Network::Devnet => DEVNET_RPC,
Network::Mainnet => MAINNET_RPC,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn program_ids_are_valid() {
let devnet = ProgramIds::devnet();
let mainnet = ProgramIds::mainnet();
assert_ne!(devnet.vault, Pubkey::default());
assert_ne!(mainnet.vault, Pubkey::default());
}
#[test]
fn distribution_adds_to_100() {
assert_eq!(
USER_SHARE_BPS + TREASURY_SHARE_BPS + STAKER_SHARE_BPS,
10000
);
}
#[test]
fn staking_apy_increases_with_duration() {
for i in 1..STAKING_APY.len() {
assert!(STAKING_APY[i-1].1 > STAKING_APY[i].1);
}
}
}