external-oracle-data-bridge

SPD5ETF2HZ921C8RJG2MHPAN7SSP9AYEYD5GSP84

Source Code

;; title: oracle-integration
;; version: 1.0.0
;; summary: Connect off-chain data to smart contracts
;; description: Oracle registration, data feed requests, price feeds, and reputation tracking

;; constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-found (err u101))
(define-constant err-unauthorized (err u102))
(define-constant err-oracle-inactive (err u103))
(define-constant err-insufficient-stake (err u104))
(define-constant err-invalid-signature (err u105))
(define-constant err-request-expired (err u106))

;; data vars
(define-data-var request-nonce uint u0)
(define-data-var min-oracle-stake uint u1000000000) ;; 1000 STX
(define-data-var response-window uint u144) ;; ~1 day
(define-data-var min-consensus-oracles uint u3)

;; data maps
(define-map registered-oracles
    { oracle: principal }
    {
        stake: uint,
        reputation: uint,
        total-responses: uint,
        correct-responses: uint,
        active: bool,
        registered-at: uint
    }
)

(define-map data-requests
    { request-id: uint }
    {
        requester: principal,
        query: (string-utf8 200),
        callback-contract: (optional principal),
        status: uint,
        created-at: uint,
        expires-at: uint
    }
)

(define-map oracle-responses
    { request-id: uint, oracle: principal }
    {
        data: (buff 256),
        timestamp: uint,
        signature: (buff 65)
    }
)

(define-map response-count
    { request-id: uint }
    {
        total: uint
    }
)

(define-map price-feeds
    { asset-pair: (string-ascii 20) }
    {
        price: uint,
        decimals: uint,
        last-updated: uint,
        oracle: principal
    }
)

(define-map consensus-results
    { request-id: uint }
    {
        result-data: (buff 256),
        oracle-count: uint,
        finalized-at: uint
    }
)

;; read only functions
(define-read-only (get-oracle (oracle principal))
    (map-get? registered-oracles { oracle: oracle })
)

(define-read-only (get-data-request (request-id uint))
    (map-get? data-requests { request-id: request-id })
)

(define-read-only (get-oracle-response (request-id uint) (oracle principal))
    (map-get? oracle-responses { request-id: request-id, oracle: oracle })
)

(define-read-only (get-response-count (request-id uint))
    (default-to { total: u0 } (map-get? response-count { request-id: request-id }))
)

(define-read-only (get-price-feed (asset-pair (string-ascii 20)))
    (map-get? price-feeds { asset-pair: asset-pair })
)

(define-read-only (get-consensus-result (request-id uint))
    (map-get? consensus-results { request-id: request-id })
)

(define-read-only (is-oracle-active (oracle principal))
    (match (map-get? registered-oracles { oracle: oracle })
        oracle-data (get active oracle-data)
        false
    )
)

;; public functions
(define-public (register-oracle (stake-amount uint))
    (begin
        (asserts! (>= stake-amount (var-get min-oracle-stake)) err-insufficient-stake)

        ;; In production, transfer stake
        ;; (try! (stx-transfer? stake-amount tx-sender (as-contract tx-sender)))

        (map-set registered-oracles
            { oracle: tx-sender }
            {
                stake: stake-amount,
                reputation: u100,
                total-responses: u0,
                correct-responses: u0,
                active: true,
                registered-at: stacks-block-time
            }
        )

        (ok true)
    )
)

(define-public (deactivate-oracle)
    (let
        (
            (oracle-data (unwrap! (map-get? registered-oracles { oracle: tx-sender }) err-not-found))
        )
        (map-set registered-oracles
            { oracle: tx-sender }
            (merge oracle-data { active: false })
        )

        ;; In production, return stake
        ;; (try! (as-contract (stx-transfer? (get stake oracle-data) tx-sender tx-sender)))

        (ok true)
    )
)

(define-public (request-data-feed
    (query (string-utf8 200))
    (callback-contract (optional principal))
)
    (let
        (
            (request-id (+ (var-get request-nonce) u1))
            (expires-at (+ stacks-block-time (var-get response-window)))
        )
        (map-set data-requests
            { request-id: request-id }
            {
                requester: tx-sender,
                query: query,
                callback-contract: callback-contract,
                status: u1, ;; Active
                created-at: stacks-block-time,
                expires-at: expires-at
            }
        )

        (map-set response-count { request-id: request-id } { total: u0 })
        (var-set request-nonce request-id)
        (ok request-id)
    )
)

(define-public (submit-oracle-data
    (request-id uint)
    (data (buff 256))
    (signature (buff 65))
)
    (let
        (
            (request (unwrap! (map-get? data-requests { request-id: request-id }) err-not-found))
            (oracle-data (unwrap! (map-get? registered-oracles { oracle: tx-sender }) err-not-found))
            (count-data (get-response-count request-id))
        )
        (asserts! (get active oracle-data) err-oracle-inactive)
        (asserts! (< stacks-block-time (get expires-at request)) err-request-expired)

        (map-set oracle-responses
            { request-id: request-id, oracle: tx-sender }
            {
                data: data,
                timestamp: stacks-block-time,
                signature: signature
            }
        )

        (map-set response-count
            { request-id: request-id }
            { total: (+ (get total count-data) u1) }
        )

        (map-set registered-oracles
            { oracle: tx-sender }
            (merge oracle-data {
                total-responses: (+ (get total-responses oracle-data) u1)
            })
        )

        (ok true)
    )
)

(define-public (update-price-feed
    (asset-pair (string-ascii 20))
    (price uint)
    (decimals uint)
)
    (let
        (
            (oracle-data (unwrap! (map-get? registered-oracles { oracle: tx-sender }) err-not-found))
        )
        (asserts! (get active oracle-data) err-oracle-inactive)

        (map-set price-feeds
            { asset-pair: asset-pair }
            {
                price: price,
                decimals: decimals,
                last-updated: stacks-block-time,
                oracle: tx-sender
            }
        )

        (ok true)
    )
)

(define-public (finalize-consensus (request-id uint) (result-data (buff 256)))
    (let
        (
            (request (unwrap! (map-get? data-requests { request-id: request-id }) err-not-found))
            (count-data (get-response-count request-id))
        )
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (asserts! (>= (get total count-data) (var-get min-consensus-oracles)) err-unauthorized)

        (map-set consensus-results
            { request-id: request-id }
            {
                result-data: result-data,
                oracle-count: (get total count-data),
                finalized-at: stacks-block-time
            }
        )

        (ok true)
    )
)

(define-public (update-oracle-reputation (oracle principal) (correct bool))
    (let
        (
            (oracle-data (unwrap! (map-get? registered-oracles { oracle: oracle }) err-not-found))
            (new-correct (if correct (+ (get correct-responses oracle-data) u1) (get correct-responses oracle-data)))
            (new-reputation (if (> (get total-responses oracle-data) u0)
                (/ (* new-correct u100) (get total-responses oracle-data))
                u100
            ))
        )
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)

        (map-set registered-oracles
            { oracle: oracle }
            (merge oracle-data {
                correct-responses: new-correct,
                reputation: new-reputation
            })
        )

        (ok new-reputation)
    )
)

;; Admin functions
(define-public (set-min-oracle-stake (new-stake uint))
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (var-set min-oracle-stake new-stake)
        (ok true)
    )
)

(define-public (set-response-window (new-window uint))
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (var-set response-window new-window)
        (ok true)
    )
)

(define-public (set-min-consensus (new-min uint))
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (var-set min-consensus-oracles new-min)
        (ok true)
    )
)

Functions (17)

FunctionAccessArgs
get-oracleread-onlyoracle: principal
get-data-requestread-onlyrequest-id: uint
get-oracle-responseread-onlyrequest-id: uint, oracle: principal
get-response-countread-onlyrequest-id: uint
get-price-feedread-onlyasset-pair: (string-ascii 20
get-consensus-resultread-onlyrequest-id: uint
is-oracle-activeread-onlyoracle: principal
register-oraclepublicstake-amount: uint
deactivate-oraclepublic
request-data-feedpublicquery: (string-utf8 200
submit-oracle-datapublicrequest-id: uint, data: (buff 256
update-price-feedpublicasset-pair: (string-ascii 20
finalize-consensuspublicrequest-id: uint, result-data: (buff 256
update-oracle-reputationpublicoracle: principal, correct: bool
set-min-oracle-stakepublicnew-stake: uint
set-response-windowpublicnew-window: uint
set-min-consensuspublicnew-min: uint