Source Code

;; Agent Registry - On-chain AI agent marketplace
;; Agents register with endpoint, capabilities, pricing.
;; Reputation is tracked per-agent: successful calls, revenue, rating.

(define-constant CONTRACT_OWNER tx-sender)

;; -- errors ---------------------------------------------
(define-constant ERR_OWNER_ONLY       (err u300))
(define-constant ERR_NOT_FOUND        (err u301))
(define-constant ERR_DUPLICATE         (err u302))
(define-constant ERR_UNAUTHORIZED      (err u303))
(define-constant ERR_INVALID_RATING    (err u304))
(define-constant ERR_SELF_RATE         (err u305))
(define-constant ERR_INACTIVE          (err u306))
(define-constant ERR_INVALID_PRICE     (err u307))

;; -- state ----------------------------------------------
(define-data-var next-agent-id uint u1)
(define-data-var total-payments uint u0)
(define-data-var total-revenue  uint u0)

;; agent-id -> metadata
(define-map agents uint {
  owner:          principal,
  name:           (string-utf8 60),
  endpoint:       (string-ascii 200),
  description:    (string-utf8 280),
  price-per-call: uint,           ;; micro-STX (1 STX = 1_000_000)
  capabilities:   (string-utf8 200),
  active:         bool,
  registered-at:  uint            ;; block height
})

;; owner principal -> agent-id (one agent per wallet for simplicity)
(define-map owner-index principal uint)

;; agent-id -> reputation
(define-map reputation uint {
  successful-calls: uint,
  failed-calls:     uint,
  total-revenue:    uint,
  rating-sum:       uint,         ;; sum of all ratings (1-5)
  rating-count:     uint          ;; how many ratings given
})

;; who rated whom (prevents double-rating per caller)
(define-map rated-by { agent-id: uint, rater: principal } bool)

;; -- register agent -------------------------------------
(define-public (register-agent
    (name           (string-utf8 60))
    (endpoint       (string-ascii 200))
    (description    (string-utf8 280))
    (price-per-call uint)
    (capabilities   (string-utf8 200))
  )
  (let ((id (var-get next-agent-id)))
    (asserts! (is-none (map-get? owner-index tx-sender)) ERR_DUPLICATE)
    (asserts! (> price-per-call u0) ERR_INVALID_PRICE)
    (map-set agents id {
      owner:          tx-sender,
      name:           name,
      endpoint:       endpoint,
      description:    description,
      price-per-call: price-per-call,
      capabilities:   capabilities,
      active:         true,
      registered-at:  block-height
    })
    (map-set owner-index tx-sender id)
    (map-set reputation id {
      successful-calls: u0,
      failed-calls:     u0,
      total-revenue:    u0,
      rating-sum:       u0,
      rating-count:     u0
    })
    (var-set next-agent-id (+ id u1))
    (print { event: "agent-registered", id: id, name: name, owner: tx-sender })
    (ok id)
  )
)

;; -- update agent (owner only) --------------------------
(define-public (update-agent
    (agent-id       uint)
    (endpoint       (string-ascii 200))
    (description    (string-utf8 280))
    (price-per-call uint)
    (capabilities   (string-utf8 200))
  )
  (let ((agent (unwrap! (map-get? agents agent-id) ERR_NOT_FOUND)))
    (asserts! (is-eq tx-sender (get owner agent)) ERR_UNAUTHORIZED)
    (asserts! (> price-per-call u0) ERR_INVALID_PRICE)
    (map-set agents agent-id (merge agent {
      endpoint:       endpoint,
      description:    description,
      price-per-call: price-per-call,
      capabilities:   capabilities
    }))
    (print { event: "agent-updated", id: agent-id })
    (ok true)
  )
)

;; -- toggle active (owner only) -------------------------
(define-public (set-agent-active (agent-id uint) (is-active bool))
  (let ((agent (unwrap! (map-get? agents agent-id) ERR_NOT_FOUND)))
    (asserts! (is-eq tx-sender (get owner agent)) ERR_UNAUTHORIZED)
    (map-set agents agent-id (merge agent { active: is-active }))
    (print { event: "agent-status", id: agent-id, active: is-active })
    (ok true)
  )
)

;; -- record successful call (gateway contract/admin) ----
(define-public (record-call-success (agent-id uint) (payment uint))
  (let (
    (rep (unwrap! (map-get? reputation agent-id) ERR_NOT_FOUND))
    (agent (unwrap! (map-get? agents agent-id) ERR_NOT_FOUND))
  )
    ;; Only the agent owner or contract owner can record
    (asserts! (or (is-eq tx-sender (get owner agent))
                  (is-eq tx-sender CONTRACT_OWNER)) ERR_UNAUTHORIZED)
    (map-set reputation agent-id (merge rep {
      successful-calls: (+ (get successful-calls rep) u1),
      total-revenue:    (+ (get total-revenue rep) payment)
    }))
    (var-set total-payments (+ (var-get total-payments) u1))
    (var-set total-revenue  (+ (var-get total-revenue) payment))
    (print { event: "call-success", agent-id: agent-id, payment: payment })
    (ok true)
  )
)

;; -- record failed call ---------------------------------
(define-public (record-call-failure (agent-id uint))
  (let (
    (rep (unwrap! (map-get? reputation agent-id) ERR_NOT_FOUND))
    (agent (unwrap! (map-get? agents agent-id) ERR_NOT_FOUND))
  )
    (asserts! (or (is-eq tx-sender (get owner agent))
                  (is-eq tx-sender CONTRACT_OWNER)) ERR_UNAUTHORIZED)
    (map-set reputation agent-id (merge rep {
      failed-calls: (+ (get failed-calls rep) u1)
    }))
    (print { event: "call-failure", agent-id: agent-id })
    (ok true)
  )
)

;; -- rate an agent (any principal, once per agent) ------
(define-public (rate-agent (agent-id uint) (rating uint))
  (let (
    (agent (unwrap! (map-get? agents agent-id) ERR_NOT_FOUND))
    (rep   (unwrap! (map-get? reputation agent-id) ERR_NOT_FOUND))
  )
    (asserts! (not (is-eq tx-sender (get owner agent))) ERR_SELF_RATE)
    (asserts! (and (>= rating u1) (<= rating u5)) ERR_INVALID_RATING)
    (asserts! (is-none (map-get? rated-by { agent-id: agent-id, rater: tx-sender })) ERR_DUPLICATE)
    (map-set rated-by { agent-id: agent-id, rater: tx-sender } true)
    (map-set reputation agent-id (merge rep {
      rating-sum:   (+ (get rating-sum rep) rating),
      rating-count: (+ (get rating-count rep) u1)
    }))
    (print { event: "agent-rated", agent-id: agent-id, rating: rating, rater: tx-sender })
    (ok true)
  )
)

;; -- read-only ------------------------------------------
(define-read-only (get-agent (agent-id uint))
  (map-get? agents agent-id)
)

(define-read-only (get-agent-by-owner (owner principal))
  (match (map-get? owner-index owner)
    id (map-get? agents id)
    none
  )
)

(define-read-only (get-agent-id-by-owner (owner principal))
  (map-get? owner-index owner)
)

(define-read-only (get-reputation (agent-id uint))
  (map-get? reputation agent-id)
)

(define-read-only (get-agent-rating (agent-id uint))
  (match (map-get? reputation agent-id)
    rep (if (> (get rating-count rep) u0)
          (ok { average: (/ (get rating-sum rep) (get rating-count rep)),
                count:   (get rating-count rep) })
          (ok { average: u0, count: u0 }))
    (err u301)
  )
)

(define-read-only (get-total-agents)
  (- (var-get next-agent-id) u1)
)

(define-read-only (get-network-stats)
  { total-agents:   (- (var-get next-agent-id) u1),
    total-payments:  (var-get total-payments),
    total-revenue:   (var-get total-revenue) }
)

Functions (13)

FunctionAccessArgs
get-network-statsread-only
register-agentpublicname: (string-utf8 60
update-agentpublicagent-id: uint, endpoint: (string-ascii 200
set-agent-activepublicagent-id: uint, is-active: bool
record-call-successpublicagent-id: uint, payment: uint
record-call-failurepublicagent-id: uint
rate-agentpublicagent-id: uint, rating: uint
get-agentread-onlyagent-id: uint
get-agent-by-ownerread-onlyowner: principal
get-agent-id-by-ownerread-onlyowner: principal
get-reputationread-onlyagent-id: uint
get-agent-ratingread-onlyagent-id: uint
get-total-agentsread-only