Source Code

;; Stakied Oracle - Price and yield rate feed oracle
;; Owner publishes price updates, consumers query with staleness checks

;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u600))
(define-constant err-not-authorized (err u601))
(define-constant err-invalid-price (err u602))
(define-constant err-stale-price (err u603))
(define-constant err-no-price-data (err u604))
(define-constant err-invalid-feed (err u605))
(define-constant err-feed-paused (err u606))
(define-constant err-invalid-threshold (err u607))
(define-constant err-price-deviation (err u608))
(define-constant err-not-updater (err u609))

;; Price precision (6 decimals)
(define-constant price-precision u1000000)
;; Max price deviation allowed per update (10%)
(define-constant max-deviation-bps u1000)
(define-constant bps-denominator u10000)

;; Data variables
(define-data-var staleness-threshold uint u720) ;; ~5 days in blocks
(define-data-var current-round uint u0)

;; Data maps
(define-map price-feeds
  (string-ascii 32) ;; feed-id e.g. "SY-STX", "PT-RATE"
  {
    price: uint,
    updated-at: uint,
    round: uint,
    updater: principal
  }
)

(define-map authorized-updaters principal bool)
(define-map feed-configs
  (string-ascii 32)
  {
    active: bool,
    min-price: uint,
    max-price: uint
  }
)

;; Read-only functions
(define-read-only (get-price (feed-id (string-ascii 32)))
  (let (
    (feed-data (unwrap! (map-get? price-feeds feed-id) err-no-price-data))
    (threshold (var-get staleness-threshold))
  )
    (asserts! (<= (- block-height (get updated-at feed-data)) threshold) err-stale-price)
    (ok {
      price: (get price feed-data),
      updated-at: (get updated-at feed-data),
      round: (get round feed-data)
    })
  )
)

(define-read-only (get-price-unsafe (feed-id (string-ascii 32)))
  (let (
    (feed-data (unwrap! (map-get? price-feeds feed-id) err-no-price-data))
  )
    (ok {
      price: (get price feed-data),
      updated-at: (get updated-at feed-data),
      round: (get round feed-data),
      is-stale: (> (- block-height (get updated-at feed-data)) (var-get staleness-threshold))
    })
  )
)

(define-read-only (get-latest-round)
  (ok (var-get current-round)))

(define-read-only (get-staleness-threshold)
  (ok (var-get staleness-threshold)))

(define-read-only (get-feed-config (feed-id (string-ascii 32)))
  (ok (map-get? feed-configs feed-id)))

(define-read-only (is-feed-stale (feed-id (string-ascii 32)))
  (let (
    (feed-data (unwrap! (map-get? price-feeds feed-id) err-no-price-data))
  )
    (ok (> (- block-height (get updated-at feed-data)) (var-get staleness-threshold)))
  )
)

;; Public functions
(define-public (update-price (feed-id (string-ascii 32)) (new-price uint))
  (begin
    (asserts! (or (is-eq tx-sender contract-owner)
                  (default-to false (map-get? authorized-updaters tx-sender)))
              err-not-updater)
    (asserts! (> new-price u0) err-invalid-price)

    ;; Check feed config bounds if configured
    (match (map-get? feed-configs feed-id)
      config
        (begin
          (asserts! (get active config) err-feed-paused)
          (asserts! (>= new-price (get min-price config)) err-invalid-price)
          (asserts! (<= new-price (get max-price config)) err-invalid-price)
        )
      true ;; no config = no bounds check
    )

    ;; Check price deviation from last update (if exists)
    (match (map-get? price-feeds feed-id)
      existing
        (let (
          (old-price (get price existing))
          (diff (if (> new-price old-price) (- new-price old-price) (- old-price new-price)))
          (deviation-bps (/ (* diff bps-denominator) old-price))
        )
          (asserts! (<= deviation-bps max-deviation-bps) err-price-deviation)
        )
      true ;; first update has no deviation check
    )

    ;; Update price feed
    (let ((new-round (+ (var-get current-round) u1)))
      (map-set price-feeds feed-id {
        price: new-price,
        updated-at: block-height,
        round: new-round,
        updater: tx-sender
      })
      (var-set current-round new-round)

      (print {action: "update-price", feed-id: feed-id, price: new-price, round: new-round})
      (ok new-round)
    )
  )
)

(define-public (set-staleness-threshold (new-threshold uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (asserts! (> new-threshold u0) err-invalid-threshold)
    (var-set staleness-threshold new-threshold)
    (print {action: "set-staleness-threshold", threshold: new-threshold})
    (ok new-threshold)
  )
)

(define-public (authorize-updater (updater principal) (authorized bool))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (map-set authorized-updaters updater authorized)
    (print {action: "authorize-updater", updater: updater, authorized: authorized})
    (ok true)
  )
)

(define-public (configure-feed (feed-id (string-ascii 32)) (active bool) (min-price uint) (max-price uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (asserts! (< min-price max-price) err-invalid-price)
    (map-set feed-configs feed-id {
      active: active,
      min-price: min-price,
      max-price: max-price
    })
    (print {action: "configure-feed", feed-id: feed-id, active: active, min-price: min-price, max-price: max-price})
    (ok true)
  )
)

Functions (10)

FunctionAccessArgs
get-priceread-onlyfeed-id: (string-ascii 32
get-price-unsaferead-onlyfeed-id: (string-ascii 32
get-latest-roundread-only
get-staleness-thresholdread-only
get-feed-configread-onlyfeed-id: (string-ascii 32
is-feed-staleread-onlyfeed-id: (string-ascii 32
update-pricepublicfeed-id: (string-ascii 32
set-staleness-thresholdpublicnew-threshold: uint
authorize-updaterpublicupdater: principal, authorized: bool
configure-feedpublicfeed-id: (string-ascii 32