stacks-3ridge-seoul-meetup-2023-nft

SP3FDQ01MQTCT2E4820M28NHMW6CKMMFVBC8KSRR8

Source Code

(impl-trait 'SPBGN2NDFZ1PT6TFV6127F53KCP9HHHEB8JHYS34.nft-trait.nft-trait)
(use-trait commission-trait 'SPBGN2NDFZ1PT6TFV6127F53KCP9HHHEB8JHYS34.commission-trait.commission)

(define-non-fungible-token stacks-3ridge-seoul-meetup-2023 uint)

;; Storage
(define-map token-count principal uint)
(define-map market uint {price: uint, commission: principal})

;; Define Constants
(define-constant CONTRACT-OWNER tx-sender)
(define-constant TOKEN-LIMIT u200)

;; Define error codes
(define-constant ERR-SOLD-OUT (err u100))
(define-constant ERR-WRONG-COMMISSION (err u102))
(define-constant ERR-NOT-AUTHORIZED (err u103))
(define-constant ERR-NOT-FOUND (err u104))
(define-constant ERR-METADATA-FROZEN (err u105))
(define-constant ERR-MINT-ALREADY-SET (err u106))
(define-constant ERR-LISTING (err u107))
(define-constant ERR-FAILED-TO-TRANSFER-STX (err u108))
(define-constant ERR-NOT-OWNER (err u109))

;; Define Variables
(define-data-var last-id uint u0)
(define-data-var metadata-frozen bool false)

;; Store the root token uri used to query metadata
(define-data-var base-token-uri (string-ascii 210) "ipfs://Qmegbw6yrxx412hfTgPmHXNoUo1YjWvXSXAb4FahScdrRF/")

;; Store the mint address allowed to trigger minting
(define-map mint-address bool principal)

;; Token count for account
(define-read-only (get-balance (account principal))
  (default-to u0
    (map-get? token-count account)))

(define-private (trnsfr (id uint) (sender principal) (recipient principal))
  (match (nft-transfer? stacks-3ridge-seoul-meetup-2023 id sender recipient)
        success
          (let
            ((sender-balance (get-balance sender))
            (recipient-balance (get-balance recipient)))
              (map-set token-count
                    sender
                    (- sender-balance u1))
              (map-set token-count
                    recipient
                    (+ recipient-balance u1))
              (ok success))
        error (err error)))

;; SIP009: Transfer token to a specified principal
(define-public (transfer (id uint) (sender principal) (recipient principal))
  (begin
    (asserts! (is-eq tx-sender sender) ERR-NOT-AUTHORIZED)
    (asserts! (is-none (map-get? market id)) ERR-LISTING)
    (trnsfr id sender recipient)))

;; SIP009: Get the owner of the specified token ID
(define-read-only (get-owner (id uint))
  ;; Make sure to replace stacks-3ridge-seoul-meetup-2023
  (ok (nft-get-owner? stacks-3ridge-seoul-meetup-2023 id)))

;; SIP009: Get the last token ID
(define-read-only (get-last-token-id)
  (ok (var-get last-id)))

;; SIP009: Get the token URI. You can set it to any other URI
(define-read-only (get-token-uri (token-id uint))
  (ok (some (concat (concat (var-get base-token-uri) "metadata") ".json"))))

;; Mint new NFT - can only be called from the mint address
(define-public (mint (new-owner principal))
    (let ((next-id (+ u1 (var-get last-id))))
      (asserts! (called-from-mint) ERR-NOT-AUTHORIZED)
      (asserts! (< (var-get last-id) TOKEN-LIMIT) ERR-SOLD-OUT)
      (match (nft-mint? stacks-3ridge-seoul-meetup-2023 next-id new-owner)
        success
        (let
        ((current-balance (get-balance new-owner)))
          (begin
            (var-set last-id next-id)
            (map-set token-count
              new-owner
              (+ current-balance u1)
            )
            (ok true)))
        error (err (* error u10000)))))

(define-public (treasure-mint (new-owner principal))
    (let ((next-id (+ u1 (var-get last-id))))
      (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
      (asserts! (called-from-mint) ERR-NOT-AUTHORIZED)
      (asserts! (< (var-get last-id) TOKEN-LIMIT) ERR-SOLD-OUT)
      (match (nft-mint? stacks-3ridge-seoul-meetup-2023 next-id new-owner)
        success
        (let
        ((current-balance (get-balance new-owner)))
          (begin
            (var-set last-id next-id)
            (map-set token-count
              new-owner
              (+ current-balance u1)
            )
            (ok true)))
        error (err (* error u10000)))))

(define-private (is-sender-owner (id uint))
  (let ((owner (unwrap! (nft-get-owner? stacks-3ridge-seoul-meetup-2023 id) false)))
    (or (is-eq tx-sender owner) (is-eq contract-caller owner))))

;; Marketplace function
(define-read-only (get-listing-in-ustx (id uint))
  (map-get? market id))

;; Marketplace function
(define-public (list-in-ustx (id uint) (price uint) (comm <commission-trait>))
  (let ((listing  {price: price, commission: (contract-of comm)}))
    (asserts! (is-sender-owner id) ERR-NOT-AUTHORIZED)
    (map-set market id listing)
    (print (merge listing {a: "list-in-ustx", id: id}))
    (ok true)))

;; Marketplace function
(define-public (unlist-in-ustx (id uint))
  (begin
    (asserts! (is-sender-owner id) ERR-NOT-AUTHORIZED)
    (map-delete market id)
    (print {a: "unlist-in-ustx", id: id})
    (ok true)))

;; Marketplace function
(define-public (buy-in-ustx (id uint) (comm <commission-trait>))
  (let ((owner (unwrap! (nft-get-owner? stacks-3ridge-seoul-meetup-2023 id) ERR-NOT-FOUND))
      (listing (unwrap! (map-get? market id) ERR-LISTING))
      (price (get price listing)))
    (asserts! (is-eq (contract-of comm) (get commission listing)) ERR-WRONG-COMMISSION)
    (try! (stx-transfer? price tx-sender owner))
    (try! (contract-call? comm pay id price))
    (try! (trnsfr id owner tx-sender))
    (map-delete market id)
    (print {a: "buy-in-ustx", id: id})
    (ok true)))

;; Manage function
(define-public (burn (id uint))
    (let ((owner (unwrap! (nft-get-owner? stacks-3ridge-seoul-meetup-2023 id) ERR-NOT-AUTHORIZED)))
        (asserts! (is-eq owner contract-caller) ERR-NOT-OWNER)
        (map-delete market id)
        (nft-burn? stacks-3ridge-seoul-meetup-2023 id contract-caller)
    )
)

;; Set base token uri
(define-public (set-base-token-uri (new-base-token-uri (string-ascii 80)))
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (asserts! (not (var-get metadata-frozen)) ERR-METADATA-FROZEN)
    (var-set base-token-uri new-base-token-uri)
    (ok true)))

;; Reveal
(define-public (reveal (reveal-uri (string-ascii 80)))
  (begin
    (try! (set-base-token-uri reveal-uri))
    (ok true)))

;; Freeze metadata
(define-public (freeze-metadata)
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (var-set metadata-frozen true)
    (ok true)))

;; Manage the Mint
(define-private (called-from-mint)
  (let ((the-mint
          (unwrap! (map-get? mint-address true)
                    false)))
    (is-eq contract-caller the-mint)))

;; Set mint address - can only be called once
(define-public (set-mint-address)
  (let ((the-mint (map-get? mint-address true)))
    (asserts! (and (is-none the-mint)
              (map-insert mint-address true tx-sender))
                ERR-MINT-ALREADY-SET)
    (ok tx-sender)))

;; Utils to convert an uint to string
;; Clarity doesn't support uint-to-string natively for now
;; Code for uint to string
(define-constant LIST_40 (list
  true true true true true true true true true true
  true true true true true true true true true true
  true true true true true true true true true true
  true true true true true true true true true true
))

(define-read-only (uint-to-string (value uint))
  (get return (fold uint-to-string-clojure LIST_40 {value: value, return: ""}))
)

(define-read-only (uint-to-string-clojure (i bool) (data {value: uint, return: (string-ascii 40)}))
  (if (> (get value data) u0)
    {
      value: (/ (get value data) u10),
      return: (unwrap-panic (as-max-len? (concat (unwrap-panic (element-at "0123456789" (mod (get value data) u10))) (get return data)) u40))
    }
    data
  )
)

Functions (20)

FunctionAccessArgs
get-balanceread-onlyaccount: principal
trnsfrprivateid: uint, sender: principal, recipient: principal
transferpublicid: uint, sender: principal, recipient: principal
get-ownerread-onlyid: uint
get-last-token-idread-only
get-token-uriread-onlytoken-id: uint
mintpublicnew-owner: principal
treasure-mintpublicnew-owner: principal
is-sender-ownerprivateid: uint
get-listing-in-ustxread-onlyid: uint
list-in-ustxpublicid: uint, price: uint, comm: <commission-trait>
unlist-in-ustxpublicid: uint
buy-in-ustxpublicid: uint, comm: <commission-trait>
burnpublicid: uint
set-base-token-uripublicnew-base-token-uri: (string-ascii 80
revealpublicreveal-uri: (string-ascii 80
freeze-metadatapublic
called-from-mintprivate
set-mint-addresspublic
uint-to-stringread-onlyvalue: uint