Source Code

;; title: subscription-manager
;; version: 1.0.0
;; summary: Manages user subscriptions to AI Agents
;; description: Tracks compute cycle quotas, subscription tiers, and usage for AI interactions.

;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u200))
(define-constant err-not-found (err u201))
(define-constant err-unauthorized (err u202))
(define-constant err-insufficient-funds (err u203))
(define-constant err-quota-exceeded (err u204))

;; Subscription Tiers
(define-constant TIER-FREE u0)
(define-constant TIER-PRO u1)

;; Data Maps
(define-map subscriptions
    { user: principal, agent-id: uint }
    {
        tier: uint,
        cycles-used: uint,
        cycles-limit: uint,
        expires-at: uint,
        is-active: bool
    }
)

;; Read-Only Functions
(define-read-only (get-subscription (user principal) (agent-id uint))
    (map-get? subscriptions { user: user, agent-id: agent-id })
)

(define-read-only (has-cycles-available (user principal) (agent-id uint) (requested-cycles uint))
    (match (map-get? subscriptions { user: user, agent-id: agent-id })
        sub (and 
                (get is-active sub)
                (> (get expires-at sub) stacks-block-height)
                (<= (+ (get cycles-used sub) requested-cycles) (get cycles-limit sub))
            )
        false
    )
)

;; Public Functions

;; Subscribe to an agent (simplified: direct payment to agent creator)
;; In a full implementation, this would read the price from agent-registry.
;; For Aegis demo, we pass the price and send it (could be verified against registry via tr).
(define-public (subscribe (agent-id uint) (tier uint) (price uint) (creator principal))
    (let
        (
            (duration u4320) ;; roughly 30 days in blocks
            (limit (if (is-eq tier TIER-PRO) u10000 u100)) ;; Pro=10k cycles, Free=100
        )
        ;; Transfer STX if not free
        (if (> price u0)
            (try! (stx-transfer? price tx-sender creator))
            true
        )
        
        (map-set subscriptions
            { user: tx-sender, agent-id: agent-id }
            {
                tier: tier,
                cycles-used: u0,
                cycles-limit: limit,
                expires-at: (+ stacks-block-height duration),
                is-active: true
            }
        )
        (ok true)
    )
)

;; Track usage (called by an authorized relayer or the escrow contract)
(define-public (track-usage (user principal) (agent-id uint) (cycles uint))
    (let
        (
            (sub (unwrap! (map-get? subscriptions { user: user, agent-id: agent-id }) err-not-found))
            (new-usage (+ (get cycles-used sub) cycles))
        )
        ;; For now, any user can call this, but ideally restricted to the oracle/escrow
        (asserts! (<= new-usage (get cycles-limit sub)) err-quota-exceeded)
        
        (map-set subscriptions
            { user: user, agent-id: agent-id }
            (merge sub { cycles-used: new-usage })
        )
        (ok new-usage)
    )
)

Functions (4)

FunctionAccessArgs
get-subscriptionread-onlyuser: principal, agent-id: uint
has-cycles-availableread-onlyuser: principal, agent-id: uint, requested-cycles: uint
subscribepublicagent-id: uint, tier: uint, price: uint, creator: principal
track-usagepublicuser: principal, agent-id: uint, cycles: uint