loop-agent-sdk 0.1.0

Trustless agent SDK for Loop Protocol — intent-based execution on Solana.
Documentation

Loop Agent SDK

crates.io docs.rs License: MIT

Trustless agent infrastructure for the Loop Protocol. Build AI agents that capture value, stake Cred, and optimize yield — without ever touching user funds.

Part of the loop-protocol monorepo. For the TypeScript SDK, see sdk/.

Installation

[dependencies]
loop-agent-sdk = "0.1"

MSRV: Rust 1.81.

Architecture

┌─────────────────────────────────────────────────────────┐
│                    LOOP AGENT SDK                       │
├─────────────────┬─────────────────┬─────────────────────┤
│     STATE       │     ACTION      │     PERCEPTION      │
│                 │                 │                     │
│ context_fetch   │ capture_value   │ on_transaction      │
│ context_push    │ stake_cred      │ on_location_hit     │
│ agent_memory    │ claim_yield     │ on_proof_submitted  │
│                 │ optimize_yield  │ on_position_unlock  │
└─────────────────┴─────────────────┴─────────────────────┘

Security Model

Intent-Based Execution: Agents don't move funds. They request actions, and smart contracts execute with enforced rules.

  • Session Keys: Scoped permissions (read/capture/stake) with TTL
  • Trustless Distribution: 80/14/6 split enforced by contract
  • No Key Access: Agents never hold private keys
// Agent requests capture, contract enforces rules
vault.capture_value(&session, merchant_id, proof)?;
// Contract automatically splits: 80% user, 14% treasury, 6% stakers

MCP Compliance

This SDK is Model Context Protocol compliant. Any MCP-compatible AI can interact with Loop vaults:

// From mcp/loop-vault-manifest.json
{
  "tools": [
    { "name": "capture_value", "description": "Submit purchase proof..." },
    { "name": "stake_cred", "description": "Stake for yield..." },
    { "name": "get_vault_status", "description": "Check balances..." }
  ]
}

A user can tell Claude or Gemini: "How's my Loop Vault doing?" and the AI knows exactly how to fetch the data.

Quick Start

use loop_agent_sdk::{
    action::{VaultAction, SessionKey, ZkProof},
    perception::{PerceptionEvent, TransactionEvent},
    state::{StateStore, UserContext},
};

async fn handle_pos_transaction(tx: TransactionEvent) -> Result<(), Error> {
    // 1. Load user context (<100ms target)
    let ctx = state_store.context_fetch(&find_user_by_card(&tx.card_fingerprint)?)?;
    
    // 2. Get scoped session key
    let session = get_session_key(&ctx.pubkey, &[PermissionScope::Capture]).await?;
    
    // 3. Build proof from POS data
    let proof = ZkProof {
        proof_type: ProofType::SquarePos,
        data: tx.raw_payload.to_vec(),
        timestamp: tx.occurred_at,
        amount_cents: tx.amount_cents,
        card_fingerprint: Some(tx.card_fingerprint),
    };
    
    // 4. Capture value (contract enforces 80/14/6 split)
    let result = vault.capture_value(&session, tx.merchant_id.parse()?, proof)?;
    
    println!("Minted {} Cred, {} to user", 
        result.cred_minted, 
        result.user_amount
    );
    
    Ok(())
}

Feature Flags

The SDK is split into composable feature flags so a deployment only pulls in what it needs. The default feature enables the full core (action + perception + state); opt into the runtime-specific features on top.

feature enables pulls in
default = full core SDK: action, perception, state (pure-Rust)
action vault.capture_value, stake_cred, yield actions
perception transaction / location / proof event handling
state context fetch/push, agent memory
lambda AWS Lambda runtime integration lambda_runtime, tokio
dynamodb DynamoDB state backend aws-sdk-dynamodb, aws-config, tokio
privacy HMAC fingerprinting + zeroize for sensitive data hmac, zeroize, rand, aws-sdk-secretsmanager
webhook Fidel / Square / Stripe webhook handlers (implies privacy) hmac
notifications Supabase push notifications (implies dynamodb) reqwest
supabase reputation attestation integration reqwest, tokio
agent the full agent stack: lambda + dynamodb + privacy + webhook + notifications + supabase all of the above
cli loop-cli binary clap

The loop-agent binary requires the agent feature; the loop-cli binary requires the cli feature. The lambda_handler example requires lambda + dynamodb.

docs.rs builds with all-features = true, so the published documentation reflects every surface.

AWS Lambda Deployment

[dependencies]
loop-agent-sdk = { version = "0.1", features = ["lambda", "dynamodb"] }
use lambda_runtime::{service_fn, LambdaEvent, Error};
use loop_agent_sdk::perception::PerceptionEvent;

#[tokio::main]
async fn main() -> Result<(), Error> {
    lambda_runtime::run(service_fn(handler)).await
}

async fn handler(event: LambdaEvent<PerceptionEvent>) -> Result<(), Error> {
    let (event, _context) = event.into_parts();
    
    match event {
        PerceptionEvent::TransactionDetected(tx) => handle_transaction(tx).await,
        PerceptionEvent::LocationHit(loc) => handle_location(loc).await,
        PerceptionEvent::PositionUnlocking(unlock) => handle_unlock(unlock).await,
        _ => Ok(()),
    }
}

EventBridge Integration

The SDK generates EventBridge rules for you:

use loop_agent_sdk::perception::generate_eventbridge_rules;

let rules = generate_eventbridge_rules("arn:aws:lambda:us-east-1:123456:function:loop-agent");
// Deploy rules to EventBridge

Events trigger your Lambda:

  • loop.pos/TransactionDetected → POS webhook
  • loop.mobile/LocationHit → User enters geofence
  • loop.staking/PositionUnlocking → 24h before unlock

Distribution Policy

Every capture follows this split (enforced on-chain):

Recipient Share Purpose
User 80% Their reward vault
Treasury 14% Protocol sustainability
Stakers 6% Yield for staked positions

Yield Rates

Duration APY
30 days 8%
90 days 12%
180 days 16%
365 days 20%

DynamoDB State Store

The SDK uses DynamoDB for high-speed state persistence (<100ms context reload, <5ms fingerprint lookup).

Table Schema

Table: loop-agent-state

Primary Key: pk (String), sk (String)
GSI: CardFingerprintIndex (fingerprint → user_pubkey)
TTL: Enabled on 'ttl' attribute

Item Types:
  USER#{pubkey} | CONTEXT       → User preferences, vault cache
  USER#{pubkey} | SESSION       → Active session key (auto-expires)
  USER#{pubkey} | PENDING#{ts}  → Pending captures (ring buffer, max 10)
  USER#{pubkey} | LOCATION      → Current merchant (geofence)
  CARD#{fp}     | META          → Card fingerprint → user mapping
  TXN#{id}      | PROCESSED     → Transaction dedup (30 day TTL)

Setup

# Create table with GSI and TTL
./scripts/create-dynamodb-table.sh

# Or use Rust
use loop_agent_sdk::dynamo::{create_table, DynamoConfig};
create_table(&client, &DynamoConfig::default()).await?;

Environment Variables

DYNAMO_TABLE=loop-agent-state      # Table name
DYNAMO_FINGERPRINT_GSI=CardFingerprintIndex  # GSI name
MAX_PENDING_CAPTURES=10            # Ring buffer limit
SESSION_TTL_SECONDS=86400          # 24 hours
PENDING_TTL_SECONDS=1814400        # 21 days
DAX_ENDPOINT=                      # Optional DAX cluster

Usage

use loop_agent_sdk::{DynamoStateStore, PendingCapture};

// Initialize
let store = DynamoStateStore::new().await?;

// Card fingerprint lookup (<5ms)
let user = store.lookup_user_by_card("fp_abc123").await?;

// User context (<100ms with consistent reads)
let ctx = store.context_fetch_async(&user_pubkey).await?;

// Mark transaction processed (dedup)
store.mark_transaction_processed(&txn_id, &user_pubkey, amount).await?;

// Session key with TTL
store.store_session_key(&user_pubkey, &session_data).await?;

// Pending captures (ring buffer)
store.add_pending_capture(&user_pubkey, &capture).await?;

License

MIT - OAR Technologies Inc