(impl-trait 'SP19S0X20M1GCJ567Y4FDKDYJ8PJACDF5KTZ0Z77B.nft-trait.nft-trait)
(use-trait commission-trait 'SP19S0X20M1GCJ567Y4FDKDYJ8PJACDF5KTZ0Z77B.commission-trait.commission)
(define-non-fungible-token stacks-korea-og-nft 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 u1000)
;; 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://QmaxRaKyM6BoYUtWq9dBRWR7AfZNcG2Eh9g59959HA4SQ2/")
;; 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-korea-og-nft 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-korea-og-nft
(ok (nft-get-owner? stacks-korea-og-nft 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-korea-og-nft 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-korea-og-nft 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-korea-og-nft 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-korea-og-nft 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-korea-og-nft id) ERR-NOT-AUTHORIZED)))
(asserts! (is-eq owner contract-caller) ERR-NOT-OWNER)
(map-delete market id)
(nft-burn? stacks-korea-og-nft 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
)
)