Skip to main content

loop_agent_sdk/
lib.rs

1//! # Loop Agent SDK
2//! 
3//! Trustless agent infrastructure for the Loop Protocol.
4//! 
5//! ## Architecture
6//! 
7//! ```text
8//! ┌─────────────────────────────────────────────────────────┐
9//! │                    LOOP AGENT SDK                       │
10//! ├─────────────────┬─────────────────┬─────────────────────┤
11//! │     STATE       │     ACTION      │     PERCEPTION      │
12//! │                 │                 │                     │
13//! │ context_fetch   │ capture_value   │ on_transaction      │
14//! │ context_push    │ stake_cred      │ on_location_hit     │
15//! │ agent_memory    │ claim_yield     │ on_proof_submitted  │
16//! │                 │ optimize_yield  │ on_position_unlock  │
17//! └─────────────────┴─────────────────┴─────────────────────┘
18//! ```
19//! 
20//! ## Security Model
21//! 
22//! - **Intent-Based Execution**: Agents request actions, smart contracts execute
23//! - **Session Keys**: Scoped permissions with TTL (read/capture/stake)
24//! - **Trustless Distribution**: 80/14/6 split enforced by contract, not agent
25//! - **No Key Access**: Agents never hold private keys or move funds directly
26//! 
27//! ## MCP Compliance
28//! 
29//! This SDK is designed to be MCP (Model Context Protocol) compliant.
30//! See `mcp/loop-vault-manifest.json` for the tool/resource definitions
31//! that allow any MCP-compatible AI to interact with Loop vaults.
32//! 
33//! ## Quick Start
34//! 
35//! ```rust,ignore
36//! use loop_agent_sdk::{
37//!     action::{VaultAction, SessionKey, ZkProof},
38//!     perception::{PerceptionEvent, PerceptionHandler},
39//!     state::{StateStore, UserContext},
40//! };
41//! 
42//! // In your Lambda handler:
43//! async fn handler(event: PerceptionEvent) -> Result<(), Error> {
44//!     // 1. Load user context (<100ms)
45//!     let ctx = state_store.context_fetch(&event.user_pubkey())?;
46//!     
47//!     // 2. Get session key for this user
48//!     let session = get_session_key(&ctx.pubkey).await?;
49//!     
50//!     // 3. Execute action based on event
51//!     match event {
52//!         PerceptionEvent::TransactionDetected(tx) => {
53//!             let proof = build_proof_from_pos(&tx)?;
54//!             let result = vault.capture_value(&session, tx.merchant_id, proof)?;
55//!             
56//!             // 4. Update context
57//!             state_store.push_event(&ctx.pubkey, ContextEvent {
58//!                 event_type: "capture".into(),
59//!                 amount: Some(result.cred_minted),
60//!                 ..
61//!             })?;
62//!         }
63//!         // ... handle other events
64//!     }
65//!     
66//!     Ok(())
67//! }
68//! ```
69
70pub mod action;
71pub mod constants;
72#[cfg(feature = "dynamodb")]
73pub mod dynamo;
74pub mod gateway;
75#[cfg(feature = "notifications")]
76pub mod notifications;
77pub mod perception;
78#[cfg(feature = "privacy")]
79pub mod privacy;
80pub mod reputation_engine;
81pub mod state;
82#[cfg(feature = "supabase")]
83pub mod supabase;
84pub mod vault_action;
85#[cfg(feature = "webhook")]
86pub mod webhook;
87
88// Re-export lambda_runtime for binaries
89#[cfg(feature = "lambda")]
90pub use aws_lambda_runtime as lambda_runtime;
91
92// Re-exports for convenience
93pub use action::{
94    VaultAction, SessionKey, PermissionScope, ZkProof, ProofType,
95    CaptureResult, StakingPosition, YieldRecommendation, ActionError,
96    DISTRIBUTION, YIELD_RATES_BPS,
97};
98
99pub use perception::{
100    PerceptionEvent, PerceptionHandler, PerceptionError,
101    TransactionEvent, LocationEvent, ProofEvent, UnlockEvent,
102    Processor, GeoEventType,
103};
104
105pub use state::{
106    StateStore, StateError, UserContext, VaultState,
107    UserPreferences, ContextEvent, init_user_context,
108};
109
110pub use vault_action::SolanaVaultAction;
111
112#[cfg(feature = "dynamodb")]
113pub use dynamo::{
114    DynamoStateStore, DynamoConfig, CardMapping, PendingCapture, SessionKeyData,
115    create_table,
116};
117
118#[cfg(feature = "privacy")]
119pub use privacy::{
120    PrivacyLayer, PrivacyConfig, LoopFingerprint, PrivacyError,
121    generate_pepper, create_pepper_secret,
122};
123
124#[cfg(feature = "webhook")]
125pub use webhook::{
126    WebhookHandler, WebhookSecrets, FidelSecrets, WebhookContext,
127    WebhookSource, WebhookError, NormalizedTransaction, TransactionType,
128    verify_fidel_signature, verify_square_signature, verify_stripe_signature,
129    parse_fidel_webhook, parse_square_webhook,
130};
131
132#[cfg(feature = "notifications")]
133pub use notifications::{
134    NotificationService, NotificationConfig, NotificationError,
135    PushNotification, NotificationData, NotificationPriority, NotificationResult,
136};
137
138#[cfg(feature = "supabase")]
139pub use supabase::{
140    SupabaseClient, SupabaseConfig, SupabaseAttestation, SupabaseReputationScore,
141};
142
143pub use constants::{
144    Network, ProgramIds, TokenMints, MainnetState,
145    CaptureType, PermissionLevel, EscrowStatus, AgentType, AgentStatus,
146    CRED_DECIMALS, OXO_DECIMALS, LAMPORTS_PER_CRED, LAMPORTS_PER_OXO,
147    USER_SHARE_BPS, TREASURY_SHARE_BPS, STAKER_SHARE_BPS,
148    STAKING_APY, MIN_STAKE_DAYS, MAX_STAKE_DAYS,
149    rpc_endpoint, DEVNET_RPC, MAINNET_RPC,
150    STAKE_30D_APY_BPS, STAKE_90D_APY_BPS, STAKE_180D_APY_BPS, STAKE_365D_APY_BPS,
151    SESSION_KEY_TTL_HOURS, SESSION_KEY_MAX_ACTIONS, CONTEXT_RELOAD_TARGET_MS,
152};
153
154// Reputation Engine for 22-Layer Stack
155pub use reputation_engine::{
156    ReputationEngine, TrustScore, TrustTier, CaptureLayer, LayerGroup,
157    Attestation, AttestationRecord, AttestationMetadata, VaultAttestation, VaultAction as VaultAttestAction,
158    ReputationDimension, ReputationResponse, ReputationCapabilities, LayerActivity,
159    // VPA (Verified Professional Attestation) - industry agnostic
160    ProfessionalAttestation, CredentialCategory, DifficultyTier, VerificationLevel,
161    SWARM_COORDINATOR_THRESHOLD, COLLATERAL_THRESHOLD, MAX_SCORE,
162};
163
164// A2A Gateway with reputation-based access control
165pub use gateway::{
166    Gateway, GatewayError, A2ARequest, A2AResponse, SwarmAction, PermissionCheck,
167};
168
169/// SDK version
170pub const VERSION: &str = env!("CARGO_PKG_VERSION");
171
172/// Utility functions
173pub mod utils {
174    use super::constants::LAMPORTS_PER_CRED;
175    
176    /// Convert lamports to Cred (human readable)
177    pub fn lamports_to_cred(lamports: u64) -> f64 {
178        lamports as f64 / LAMPORTS_PER_CRED as f64
179    }
180    
181    /// Convert Cred to lamports
182    pub fn cred_to_lamports(cred: f64) -> u64 {
183        (cred * LAMPORTS_PER_CRED as f64) as u64
184    }
185    
186    /// Calculate yield for a position
187    pub fn calculate_yield(
188        principal: u64,
189        apy_bps: u16,
190        days: u16,
191    ) -> u64 {
192        let daily_rate = apy_bps as f64 / 10000.0 / 365.0;
193        let multiplier = (1.0 + daily_rate).powi(days as i32);
194        let final_amount = principal as f64 * multiplier;
195        (final_amount - principal as f64) as u64
196    }
197    
198    /// Calculate distribution split
199    pub fn calculate_split(total: u64) -> (u64, u64, u64) {
200        let user = (total as u128 * 8000 / 10000) as u64;
201        let treasury = (total as u128 * 1400 / 10000) as u64;
202        let stakers = total - user - treasury;  // Remainder to avoid rounding loss
203        (user, treasury, stakers)
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210    
211    #[test]
212    fn lamport_conversion_roundtrip() {
213        let cred = 42.5;
214        let lamports = utils::cred_to_lamports(cred);
215        let back = utils::lamports_to_cred(lamports);
216        assert!((back - cred).abs() < 0.000001);
217    }
218    
219    #[test]
220    fn split_adds_up() {
221        let total = 1_000_000_000u64;  // 1 Cred
222        let (user, treasury, stakers) = utils::calculate_split(total);
223        assert_eq!(user + treasury + stakers, total);
224        assert_eq!(user, 800_000_000);      // 80%
225        assert_eq!(treasury, 140_000_000);  // 14%
226        assert_eq!(stakers, 60_000_000);    // 6%
227    }
228    
229    #[test]
230    fn yield_calculation() {
231        let principal = 1_000_000_000_000u64;  // 1000 Cred
232        let yield_amount = utils::calculate_yield(principal, 1200, 90); // 12% APY, 90 days
233        // ~29 Cred yield expected
234        assert!(yield_amount > 28_000_000_000);
235        assert!(yield_amount < 32_000_000_000);
236    }
237}