Source Code

;; Outcome token contract for prediction market shares
;; Compatible with Clarity 2.1 and SIP-010 standards.
;; This contract manages the minting, burning, and transfer of binary outcome tokens.

(define-constant ERR_UNAUTHORIZED (err u1001))        ;; Caller has no permission for this action
(define-constant ERR_INVALID_MARKET (err u1002))      ;; Market ID does not exist
(define-constant ERR_INVALID_OUTCOME (err u1003))     ;; Outcome ID is not YES (1) or NO (0)
(define-constant ERR_INSUFFICIENT_BALANCE (err u1004)) ;; User lacks required token balance
(define-constant ERR_ALREADY_INITIALIZED (err u1005)) ;; Market/Token already initialized


(define-map token-id-yes-map
  uint
  uint
)
(define-map token-id-no-map
  uint
  uint
)
(define-map token-metadata
  uint
  {
    name: (string-ascii 32),
    symbol: (string-ascii 10),
    decimals: uint,
    market-id: uint,
    outcome: uint,
  }
)
(define-map balances
  {
    owner: principal,
    token-id: uint,
  }
  uint
)
(define-map total-supply-map
  uint
  uint
)
(define-map authorized-admins principal bool)


(define-data-var contract-owner principal 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM)

(define-read-only (is-admin (user principal))
  (default-to false (map-get? authorized-admins user))
)

(define-public (add-admin (new-admin principal))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) ERR_UNAUTHORIZED)
    (ok (map-set authorized-admins new-admin true))
  )
)

(define-public (remove-admin (admin principal))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) ERR_UNAUTHORIZED)
    (ok (map-set authorized-admins admin false))
  )
)


;; Looks up the token identifier for a given market and outcome type
;; Outcome 1 represents YES, outcome 0 represents NO
(define-read-only (get-token-id
    (market-id uint)
    (outcome uint)
  )
  (if (is-eq outcome u1)
    (ok (default-to u0 (map-get? token-id-yes-map market-id)))
    (ok (default-to u0 (map-get? token-id-no-map market-id)))
  )
)

;; Returns stored information about a specific token type
(define-read-only (get-token-metadata (token-id uint))
  (ok (map-get? token-metadata token-id))
)

;; Checks how many shares of a specific token a user owns
(define-read-only (get-balance
    (token-id uint)
    (owner principal)
  )
  (ok (default-to u0
    (map-get? balances {
      owner: owner,
      token-id: token-id,
    })
  ))
)

;; SIP-010 related getters
(define-read-only (get-name (token-id uint))
  (ok (get name (default-to {name: "Unknown", symbol: "???", decimals: u6, market-id: u0, outcome: u0} (map-get? token-metadata token-id))))
)

(define-read-only (get-symbol (token-id uint))
  (ok (get symbol (default-to {name: "Unknown", symbol: "???", decimals: u6, market-id: u0, outcome: u0} (map-get? token-metadata token-id))))
)

(define-read-only (get-decimals (token-id uint))
  (ok (get decimals (default-to {name: "Unknown", symbol: "???", decimals: u6, market-id: u0, outcome: u0} (map-get? token-metadata token-id))))
)

(define-read-only (get-token-uri (token-id uint))
  (ok none)
)


;; Returns the total number of shares minted for a token type
(define-read-only (get-total-supply (token-id uint))
  (ok (default-to u0 (map-get? total-supply-map token-id)))
)

;; Moves shares between user accounts
;; Requires sender to authorize the transfer
(define-public (transfer
    (token-id uint)
    (amount uint)
    (sender principal)
    (recipient principal)
  )
  (begin
    (asserts! (is-eq tx-sender sender) ERR_UNAUTHORIZED)
    (asserts!
      (>=
        (default-to u0
          (map-get? balances {
            owner: sender,
            token-id: token-id,
          })
        )
        amount
      )
      ERR_INSUFFICIENT_BALANCE
    )
    (map-set balances {
      owner: sender,
      token-id: token-id,
    }
      (-
        (default-to u0
          (map-get? balances {
            owner: sender,
            token-id: token-id,
          })
        )
        amount
      ))
    (map-set balances {
      owner: recipient,
      token-id: token-id,
    }
      (+
        (default-to u0
          (map-get? balances {
            owner: recipient,
            token-id: token-id,
          })
        )
        amount
      ))
    (ok true)
  )
)

;; Creates new shares when users purchase outcome positions
;; Restricted to the authorized market contract
(define-public (mint
    (token-id uint)
    (recipient principal)
    (amount uint)
  )
  (begin
    (asserts! (or (is-eq tx-sender (var-get contract-owner)) (is-admin tx-sender)) ERR_UNAUTHORIZED)

    (map-set balances {
      owner: recipient,
      token-id: token-id,
    }
      (+
        (default-to u0
          (map-get? balances {
            owner: recipient,
            token-id: token-id,
          })
        )
        amount
      ))
    (map-set total-supply-map token-id
      (+ (default-to u0 (map-get? total-supply-map token-id)) amount)
    )
    (ok true)
  )
)

;; Destroys shares when users claim winnings
;; Restricted to the authorized market contract
(define-public (burn
    (token-id uint)
    (owner principal)
    (amount uint)
  )
  (begin
    (asserts! (or (is-eq tx-sender (var-get contract-owner)) (is-admin tx-sender)) ERR_UNAUTHORIZED)

    (asserts!
      (>=
        (default-to u0
          (map-get? balances {
            owner: owner,
            token-id: token-id,
          })
        )
        amount
      )
      ERR_INSUFFICIENT_BALANCE
    )
    (map-set balances {
      owner: owner,
      token-id: token-id,
    }
      (-
        (default-to u0
          (map-get? balances {
            owner: owner,
            token-id: token-id,
          })
        )
        amount
      ))
    (map-set total-supply-map token-id
      (- (default-to u0 (map-get? total-supply-map token-id)) amount)
    )
    (ok true)
  )
)

;; Registers a new pair of outcome tokens when a market is created
;; Sets up metadata and assigns unique token IDs
(define-public (initialize-token
    (market-id uint)
    (token-id-yes uint)
    (token-id-no uint)
    (name-yes (string-ascii 32))
    (name-no (string-ascii 32))
    (symbol-yes (string-ascii 10))
    (symbol-no (string-ascii 10))
  )
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) ERR_UNAUTHORIZED)
    (asserts! (is-none (map-get? token-id-yes-map market-id)) ERR_ALREADY_INITIALIZED)

    (map-set token-id-yes-map market-id token-id-yes)
    (map-set token-id-no-map market-id token-id-no)
    (map-set token-metadata token-id-yes {
      name: name-yes,
      symbol: symbol-yes,
      decimals: u6,
      market-id: market-id,
      outcome: u1,
    })
    (map-set token-metadata token-id-no {
      name: name-no,
      symbol: symbol-no,
      decimals: u6,
      market-id: market-id,
      outcome: u0,
    })
    (ok true)
  )
)

;; Sets the contract owner which will be the market contract address
(define-public (initialize (owner principal))
  (begin
    (asserts! (is-eq tx-sender 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM) ERR_UNAUTHORIZED)
    (var-set contract-owner owner)
    (ok true)
  )
)


(define-read-only (get-contract-owner)
  (ok (var-get contract-owner))
)

(define-read-only (get-token-id-yes (market-id uint))
  (ok (map-get? token-id-yes-map market-id))
)

(define-read-only (get-token-id-no (market-id uint))
  (ok (map-get? token-id-no-map market-id))
)

(define-read-only (get-metadata (token-id uint))
  (ok (map-get? token-metadata token-id))
)

Functions (15)

FunctionAccessArgs
is-adminread-onlyuser: principal
add-adminpublicnew-admin: principal
remove-adminpublicadmin: principal
get-token-metadataread-onlytoken-id: uint
get-nameread-onlytoken-id: uint
get-symbolread-onlytoken-id: uint
get-decimalsread-onlytoken-id: uint
get-token-uriread-onlytoken-id: uint
get-total-supplyread-onlytoken-id: uint
initialize-tokenpublicmarket-id: uint, token-id-yes: uint, token-id-no: uint, name-yes: (string-ascii 32
initializepublicowner: principal
get-contract-ownerread-only
get-token-id-yesread-onlymarket-id: uint
get-token-id-noread-onlymarket-id: uint
get-metadataread-onlytoken-id: uint