Skip to main content

loop_agent_sdk/
constants.rs

1//! Loop Protocol Constants
2//! 
3//! Program IDs, token mints, and protocol constants.
4//! Shared between all Loop SDKs.
5
6use solana_sdk::pubkey::Pubkey;
7use std::str::FromStr;
8
9// ============================================================================
10// NETWORK CONFIGURATION
11// ============================================================================
12
13/// Network environment
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Network {
16    Devnet,
17    Mainnet,
18}
19
20impl Default for Network {
21    fn default() -> Self {
22        Network::Mainnet
23    }
24}
25
26// ============================================================================
27// PROGRAM IDS
28// ============================================================================
29
30/// Program IDs for each network
31pub struct ProgramIds {
32    pub vault: Pubkey,
33    pub cred: Pubkey,
34    pub oxo: Pubkey,
35    pub vtp: Pubkey,
36    pub avp: Pubkey,
37    pub shopping: Pubkey,
38}
39
40impl ProgramIds {
41    /// Get program IDs for the specified network
42    pub fn for_network(network: Network) -> Self {
43        match network {
44            Network::Devnet => Self::devnet(),
45            Network::Mainnet => Self::mainnet(),
46        }
47    }
48    
49    /// Devnet program IDs (March 16, 2026)
50    pub fn devnet() -> Self {
51        Self {
52            vault: Pubkey::from_str("9gKRCrUpHv9CRHwYMmm2zP5bN1bUKgyi7BxuS5jifX4x").unwrap(),
53            cred: Pubkey::from_str("JCzhFrDvipRr5CYtWoV3szpaJ3RP59gbAn4zCi5CZcv8").unwrap(),
54            oxo: Pubkey::from_str("AZZQdfcmejPjebNnneqTeVbkbVY8nyZuomuChbLqwhHj").unwrap(),
55            vtp: Pubkey::from_str("3mP7L31af6MV6FnqWG6E78JELNuizWDwBK3rC3g3WjSK").unwrap(),
56            avp: Pubkey::from_str("FE3ZJBqVcqP6ar2pnndMghgNb3pi4mrjhVoAS7x4BVCA").unwrap(),
57            shopping: Pubkey::from_str("FSqRkH7nkGHP3VpHwFE667PVAfLKfSGaPMgTrXpJZJoJ").unwrap(),
58        }
59    }
60    
61    /// Mainnet program IDs (March 18, 2026)
62    pub fn mainnet() -> Self {
63        Self {
64            vault: Pubkey::from_str("J8HhLeRv5iQaSyYQBXJoDwDKbw4V8uA84WN93YrVSWQT").unwrap(),
65            cred: Pubkey::from_str("HYQJwCJ5wH9o4sb9sVPyvSSeY9DtsznZGy2AfpiBaBaG").unwrap(),
66            // OXO not yet deployed to mainnet
67            oxo: Pubkey::from_str("AZZQdfcmejPjebNnneqTeVbkbVY8nyZuomuChbLqwhHj").unwrap(),
68            // VTP not yet deployed to mainnet
69            vtp: Pubkey::from_str("3mP7L31af6MV6FnqWG6E78JELNuizWDwBK3rC3g3WjSK").unwrap(),
70            // AVP not yet deployed to mainnet
71            avp: Pubkey::from_str("FE3ZJBqVcqP6ar2pnndMghgNb3pi4mrjhVoAS7x4BVCA").unwrap(),
72            shopping: Pubkey::from_str("HiewKEBy6YVn3Xi5xdhyrsfPr3KjKg6Jy8PXemyeteXJ").unwrap(),
73        }
74    }
75}
76
77// ============================================================================
78// TOKEN MINTS
79// ============================================================================
80
81/// Token mints for each network
82pub struct TokenMints {
83    pub cred: Pubkey,
84    pub oxo: Pubkey,
85    pub usdc: Pubkey,
86}
87
88impl TokenMints {
89    /// Get token mints for the specified network
90    pub fn for_network(network: Network) -> Self {
91        match network {
92            Network::Devnet => Self::devnet(),
93            Network::Mainnet => Self::mainnet(),
94        }
95    }
96    
97    /// Devnet token mints
98    pub fn devnet() -> Self {
99        Self {
100            cred: Pubkey::from_str("DDfGosZop37FA97MqCrXA6fKMu8kTwduQ1JsdYJFoNBP").unwrap(),
101            oxo: Pubkey::from_str("A8UkSCWuFvgBjeqWzZVowyCyUgW3JcyPB8VscPycd9hd").unwrap(),
102            usdc: Pubkey::from_str("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU").unwrap(),
103        }
104    }
105    
106    /// Mainnet token mints
107    pub fn mainnet() -> Self {
108        Self {
109            cred: Pubkey::from_str("9GQMCAK3MpZF1hEbwqA9d4mRGtippGV9hyr8fxmz6eA").unwrap(),
110            // OXO not yet deployed to mainnet
111            oxo: Pubkey::from_str("A8UkSCWuFvgBjeqWzZVowyCyUgW3JcyPB8VscPycd9hd").unwrap(),
112            usdc: Pubkey::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v").unwrap(),
113        }
114    }
115}
116
117// ============================================================================
118// STATE ACCOUNTS (Mainnet)
119// ============================================================================
120
121/// Mainnet state accounts
122pub struct MainnetState {
123    pub cred_config: Pubkey,
124    pub shopping_state: Pubkey,
125    pub reserve_vault: Pubkey,
126    pub treasury: Pubkey,
127    pub staker_pool: Pubkey,
128}
129
130impl MainnetState {
131    pub fn get() -> Self {
132        Self {
133            cred_config: Pubkey::from_str("FzwRshrm2Pu8ygMPqHwbjb7yWBiQ2pQc3Lgk8XJRpvNM").unwrap(),
134            shopping_state: Pubkey::from_str("92nXnNG5gJFDEqXE8Lyrqr3nDZiaD2atBCoRSNY5JD3n").unwrap(),
135            reserve_vault: Pubkey::from_str("Ga2PRtFu3TRsTjN1QxdVpvtjVP7kb1rAkZse9MXWXjPh").unwrap(),
136            treasury: Pubkey::from_str("CTRnn7vC1EtL1YDLU3x3iidEcCWRmZupEXHKQfL17Fxa").unwrap(),
137            staker_pool: Pubkey::from_str("zEJf7Vy7ZDjvoMTBndAvkvxRpACUzCboQg2dP8H8R6k").unwrap(),
138        }
139    }
140}
141
142// ============================================================================
143// PROTOCOL CONSTANTS
144// ============================================================================
145
146/// Token decimals
147pub const CRED_DECIMALS: u8 = 6;
148pub const OXO_DECIMALS: u8 = 6;
149pub const LAMPORTS_PER_CRED: u64 = 1_000_000;
150pub const LAMPORTS_PER_OXO: u64 = 1_000_000;
151
152/// Distribution policy (enforced by smart contract)
153pub const USER_SHARE_BPS: u16 = 8000;      // 80%
154pub const TREASURY_SHARE_BPS: u16 = 1400;  // 14%
155pub const STAKER_SHARE_BPS: u16 = 600;     // 6%
156
157/// Staking APY tiers (basis points, 100 = 1%)
158pub const STAKING_APY: [(u16, u16); 5] = [
159    (365, 1500),  // 15% APY for 365+ days
160    (180, 1200),  // 12% APY for 180-364 days
161    (90, 800),    // 8% APY for 90-179 days
162    (30, 500),    // 5% APY for 30-89 days
163    (7, 300),     // 3% APY for 7-29 days
164];
165
166/// Individual staking APY constants (basis points)
167pub const STAKE_30D_APY_BPS: u16 = 800;   // 8%
168pub const STAKE_90D_APY_BPS: u16 = 1200;  // 12%
169pub const STAKE_180D_APY_BPS: u16 = 1600; // 16%
170pub const STAKE_365D_APY_BPS: u16 = 2000; // 20%
171
172/// Staking duration limits
173pub const MIN_STAKE_DAYS: u16 = 7;
174pub const MAX_STAKE_DAYS: u16 = 730;  // 2 years
175
176/// Vault constants
177pub const EXTRACTION_FEE_BPS: u16 = 500;  // 5%
178
179/// OXO constants
180pub const OXO_TOTAL_SUPPLY: u64 = 1_000_000_000_000_000;  // 1B with 6 decimals
181pub const MIN_LOCK_SECONDS: i64 = 15_552_000;   // 6 months
182pub const MAX_LOCK_SECONDS: i64 = 126_144_000;  // 4 years
183pub const GRADUATION_THRESHOLD: u64 = 25_000_000_000;  // 25,000 OXO
184pub const AGENT_CREATION_FEE: u64 = 500_000_000;  // 500 OXO
185
186/// VTP constants
187pub const TRANSFER_FEE_BPS: u16 = 10;   // 0.1%
188pub const ESCROW_FEE_BPS: u16 = 25;     // 0.25%
189pub const MAX_ARBITERS: u8 = 5;
190pub const MAX_CONDITIONS: u8 = 10;
191
192/// AVP constants
193pub const MIN_SERVICE_AGENT_STAKE: u64 = 500_000_000;  // 500 OXO
194pub const MAX_CAPABILITIES: u8 = 20;
195pub const MAX_METADATA_LEN: usize = 200;
196
197/// Session key defaults
198pub const SESSION_KEY_TTL_HOURS: u32 = 24;
199pub const SESSION_KEY_MAX_ACTIONS: u32 = 1000;
200
201/// State reload target
202pub const CONTEXT_RELOAD_TARGET_MS: u32 = 100;
203
204// ============================================================================
205// ENUMS (matching TypeScript SDK)
206// ============================================================================
207
208/// Type of value capture
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
210#[repr(u8)]
211pub enum CaptureType {
212    Shopping = 0,
213    Data = 1,
214    Presence = 2,
215    Attention = 3,
216    Referral = 4,
217}
218
219/// Agent permission levels for vault access
220#[derive(Debug, Clone, Copy, PartialEq, Eq)]
221#[repr(u8)]
222pub enum PermissionLevel {
223    None = 0,
224    Read = 1,
225    Capture = 2,
226    Guided = 3,
227    Autonomous = 4,
228}
229
230/// Escrow status
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
232#[repr(u8)]
233pub enum EscrowStatus {
234    Active = 0,
235    Released = 1,
236    Cancelled = 2,
237    Disputed = 3,
238}
239
240/// Agent type
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242#[repr(u8)]
243pub enum AgentType {
244    Personal = 0,
245    Service = 1,
246}
247
248/// Agent status
249#[derive(Debug, Clone, Copy, PartialEq, Eq)]
250#[repr(u8)]
251pub enum AgentStatus {
252    Active = 0,
253    Suspended = 1,
254    Revoked = 2,
255}
256
257// ============================================================================
258// RPC ENDPOINTS
259// ============================================================================
260
261/// Default RPC endpoints
262pub const DEVNET_RPC: &str = "https://api.devnet.solana.com";
263pub const MAINNET_RPC: &str = "https://api.mainnet-beta.solana.com";
264
265/// Get RPC endpoint for network
266pub fn rpc_endpoint(network: Network) -> &'static str {
267    match network {
268        Network::Devnet => DEVNET_RPC,
269        Network::Mainnet => MAINNET_RPC,
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    use super::*;
276    
277    #[test]
278    fn program_ids_are_valid() {
279        let devnet = ProgramIds::devnet();
280        let mainnet = ProgramIds::mainnet();
281        
282        // All should be valid pubkeys
283        assert_ne!(devnet.vault, Pubkey::default());
284        assert_ne!(mainnet.vault, Pubkey::default());
285    }
286    
287    #[test]
288    fn distribution_adds_to_100() {
289        assert_eq!(
290            USER_SHARE_BPS + TREASURY_SHARE_BPS + STAKER_SHARE_BPS,
291            10000
292        );
293    }
294    
295    #[test]
296    fn staking_apy_increases_with_duration() {
297        for i in 1..STAKING_APY.len() {
298            assert!(STAKING_APY[i-1].1 > STAKING_APY[i].1);
299        }
300    }
301}