Source Code

;; title: verily-fund
;; version:
;; summary:
;; description:

;; private functions
;;
;; Define error constants ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
(define-constant err-not-authorized (err u100))
(define-constant err-token-exists (err u101))
(define-constant err-token-not-found (err u102))
(define-constant err-insufficient-funds (err u103))
(define-constant err-invalid-token-name (err u104))
(define-constant err-invalid-category (err u105))
(define-constant err-invalid-max-supply (err u106))
(define-constant err-invalid-token-price (err u107))
(define-constant err-invalid-recipient (err u108))
(define-constant err-invalid-transfer-amount (err u109))
(define-constant err-insufficient-allowance (err u110))
(define-constant err-invalid-authorized-addr (err u111))
(define-constant err-invalid-price-update (err u112))

;; Counter for token IDs
(define-data-var token-counter uint u0)
(define-data-var contract-admin principal tx-sender)

;;;;;;; MAP ;;;;;;;
;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;; 
;; Define the token structure
(define-map tokens
  { token-id: uint }
  {
    token-name: (string-ascii 64),
    token-category: (string-ascii 32),
    max-supply: uint,
    token-price: uint,
    last-price-update: uint, ;; Added timestamp for price updates
  }
)

;; Define balances structure
(define-map balances
  {
    holder: principal,
    token-id: uint,
  }
  { amount: uint }
)

;; Define allowance structure
(define-map allowances
  {
    holder: principal,
    authorized: principal,
    token-id: uint,
  }
  { allowed-amount: uint }
)

;; Define price history structure
(define-map price-history
  {
    token-id: uint,
    timestamp: uint,
  }
  { price: uint }
)

;; Function to validate token-id
(define-read-only (is-valid-token (token-id uint))
  (is-some (map-get? tokens { token-id: token-id }))
)

;; Get token information
(define-read-only (get-token-info (token-id uint))
  (map-get? tokens { token-id: token-id })
)

;; Get balance of a holder for a specific token
(define-read-only (get-balance
    (holder principal)
    (token-id uint)
  )
  (default-to u0
    (get amount
      (map-get? balances {
        holder: holder,
        token-id: token-id,
      })
    ))
)

;; Get allowance amount
(define-read-only (get-allowance
    (holder principal)
    (spender principal)
    (token-id uint)
  )
  (default-to u0
    (get allowed-amount
      (map-get? allowances {
        holder: holder,
        authorized: spender,
        token-id: token-id,
      })
    ))
)

;; Get price at a specific timestamp
(define-read-only (get-price-at-time
    (token-id uint)
    (timestamp uint)
  )
  (map-get? price-history {
    token-id: token-id,
    timestamp: timestamp,
  })
)

;; Get current token counter value
(define-read-only (get-token-counter)
  (var-get token-counter)
)

;; Get contract admin
(define-read-only (get-contract-admin)
  (var-get contract-admin)
)

;; public functions
;;

;; Function to create a new token
(define-public (mint-token
    (token-name (string-ascii 64))
    (token-category (string-ascii 32))
    (max-supply uint)
    (token-price uint)
  )
  (let (
      (token-id (+ (var-get token-counter) u1))
      (current-time stacks-block-time)
    )
    (asserts! (is-eq tx-sender (var-get contract-admin)) err-not-authorized)
    (asserts! (is-none (map-get? tokens { token-id: token-id })) err-token-exists)
    ;; Input validation
    (asserts! (> (len token-name) u0) err-invalid-token-name)
    (asserts! (> (len token-category) u0) err-invalid-category)
    (asserts! (> max-supply u0) err-invalid-max-supply)
    (asserts! (> token-price u0) err-invalid-token-price)
    (map-set tokens { token-id: token-id } {
      token-name: token-name,
      token-category: token-category,
      max-supply: max-supply,
      token-price: token-price,
      last-price-update: current-time,
    })
    ;; Record initial price in history
    (map-set price-history {
      token-id: token-id,
      timestamp: current-time,
    } { price: token-price }
    )
    (map-set balances {
      holder: (var-get contract-admin),
      token-id: token-id,
    } { amount: max-supply }
    )
    (var-set token-counter token-id)
    (ok token-id)
  )
)

;; Function to transfer tokens
(define-public (transfer
    (token-id uint)
    (amount uint)
    (sender principal)
    (recipient principal)
  )
  (let (
      (sender-balance (default-to u0
        (get amount
          (map-get? balances {
            holder: sender,
            token-id: token-id,
          })
        )))
      (recipient-balance (default-to u0
        (get amount
          (map-get? balances {
            holder: recipient,
            token-id: token-id,
          })
        )))
    )
    ;; Validate inputs
    (asserts! (is-valid-token token-id) err-token-not-found)
    (asserts! (> amount u0) err-invalid-transfer-amount)
    (asserts! (is-eq tx-sender sender) err-not-authorized)
    (asserts! (is-standard recipient) err-invalid-recipient)
    (asserts! (>= sender-balance amount) err-insufficient-funds)

    ;; Update sender balance
    (map-set balances {
      holder: sender,
      token-id: token-id,
    } { amount: (- sender-balance amount) }
    )

    ;; Update recipient balance
    (map-set balances {
      holder: recipient,
      token-id: token-id,
    } { amount: (+ recipient-balance amount) }
    )

    (ok true)
  )
)

;; Function to approve an address to spend tokens on behalf of holder
(define-public (approve
    (token-id uint)
    (spender principal)
    (amount uint)
  )
  (begin
    ;; Validate inputs
    (asserts! (is-valid-token token-id) err-token-not-found)
    (asserts! (is-standard spender) err-invalid-authorized-addr)
    (asserts! (> amount u0) err-invalid-transfer-amount)

    ;; Set allowance
    (map-set allowances {
      holder: tx-sender,
      authorized: spender,
      token-id: token-id,
    } { allowed-amount: amount }
    )

    (ok true)
  )
)

;; Function to transfer tokens from an approved address
(define-public (transfer-from
    (token-id uint)
    (amount uint)
    (owner principal)
    (recipient principal)
  )
  (let (
      (current-allowance (default-to u0
        (get allowed-amount
          (map-get? allowances {
            holder: owner,
            authorized: tx-sender,
            token-id: token-id,
          })
        )))
      (owner-balance (default-to u0
        (get amount
          (map-get? balances {
            holder: owner,
            token-id: token-id,
          })
        )))
      (recipient-balance (default-to u0
        (get amount
          (map-get? balances {
            holder: recipient,
            token-id: token-id,
          })
        )))
    )
    ;; Validate inputs
    (asserts! (is-valid-token token-id) err-token-not-found)
    (asserts! (> amount u0) err-invalid-transfer-amount)
    (asserts! (is-standard recipient) err-invalid-recipient)
    (asserts! (>= current-allowance amount) err-insufficient-allowance)
    (asserts! (>= owner-balance amount) err-insufficient-funds)

    ;; Update allowance
    (map-set allowances {
      holder: owner,
      authorized: tx-sender,
      token-id: token-id,
    } { allowed-amount: (- current-allowance amount) }
    )

    ;; Update owner balance
    (map-set balances {
      holder: owner,
      token-id: token-id,
    } { amount: (- owner-balance amount) }
    )

    ;; Update recipient balance
    (map-set balances {
      holder: recipient,
      token-id: token-id,
    } { amount: (+ recipient-balance amount) }
    )

    (ok true)
  )
)

;; Function to update token price (admin only)
(define-public (update-price
    (token-id uint)
    (new-price uint)
  )
  (let (
      (token-data (unwrap! (map-get? tokens { token-id: token-id }) err-token-not-found))
      (current-time stacks-block-time)
    )
    ;; Validate inputs
    (asserts! (is-eq tx-sender (var-get contract-admin)) err-not-authorized)
    (asserts! (> new-price u0) err-invalid-price-update)

    ;; Update token price and timestamp
    (map-set tokens { token-id: token-id }
      (merge token-data {
        token-price: new-price,
        last-price-update: current-time,
      })
    )

    ;; Record price in history
    (map-set price-history {
      token-id: token-id,
      timestamp: current-time,
    } { price: new-price }
    )

    (ok true)
  )
)

;; Function to burn tokens (reduce supply)
(define-public (burn
    (token-id uint)
    (amount uint)
  )
  (let ((holder-balance (default-to u0
      (get amount
        (map-get? balances {
          holder: tx-sender,
          token-id: token-id,
        })
      ))))
    ;; Validate inputs
    (asserts! (is-valid-token token-id) err-token-not-found)
    (asserts! (> amount u0) err-invalid-transfer-amount)
    (asserts! (>= holder-balance amount) err-insufficient-funds)

    ;; Reduce holder balance
    (map-set balances {
      holder: tx-sender,
      token-id: token-id,
    } { amount: (- holder-balance amount) }
    )

    (ok true)
  )
)

;; Function to change contract admin (current admin only)
(define-public (set-contract-admin (new-admin principal))
  (begin
    ;; Validate that caller is current admin
    (asserts! (is-eq tx-sender (var-get contract-admin)) err-not-authorized)
    (asserts! (is-standard new-admin) err-invalid-authorized-addr)

    ;; Update contract admin
    (var-set contract-admin new-admin)

    (ok true)
  )
)

Functions (6)

FunctionAccessArgs
is-valid-tokenread-onlytoken-id: uint
get-token-inforead-onlytoken-id: uint
get-token-counterread-only
get-contract-adminread-only
mint-tokenpublictoken-name: (string-ascii 64
set-contract-adminpublicnew-admin: principal