Source Code

;; Simple NFT Mint Contract
;; Allows users to mint NFTs with metadata
;; clarity-version: 3

;; Error codes
(define-constant ERR-NOT-AUTHORIZED (err u401))
(define-constant ERR-NOT-FOUND (err u404))
(define-constant ERR-ALREADY-EXISTS (err u409))
(define-constant ERR-MAX-SUPPLY-REACHED (err u410))
(define-constant ERR-INSUFFICIENT-FUNDS (err u411))
(define-constant ERR-INVALID-INPUT (err u413))

;; NFT definition
(define-non-fungible-token BitcoinStamp uint)

;; Constants
(define-constant contract-owner tx-sender)
(define-constant MAX-SUPPLY u10000)
(define-constant MINT-PRICE u1000000) ;; 1 STX (1,000,000 micro-STX)

;; Data variables
(define-data-var last-token-id uint u0)
(define-data-var mint-enabled bool true)
(define-data-var base-uri (string-ascii 256) "ipfs://")

;; Data maps
(define-map token-metadata
    uint
    {
        name: (string-ascii 256),
        uri: (string-ascii 256),
        minted-at: uint,
    }
)

;; Private functions

;; Get next token ID and increment
(define-private (get-next-token-id)
    (let (
            (current-id (var-get last-token-id))
            (next-id (+ current-id u1))
        )
        (asserts! (<= next-id MAX-SUPPLY) ERR-MAX-SUPPLY-REACHED)
        (var-set last-token-id next-id)
        (ok next-id)
    )
)

;; Public functions

;; Mint NFT with metadata (paid)
(define-public (mint
        (name (string-ascii 256))
        (uri (string-ascii 256))
    )
    (let (
            (token-id (unwrap! (get-next-token-id) ERR-MAX-SUPPLY-REACHED))
            (minter tx-sender)
        )
        ;; Validate inputs
        (asserts! (> (len name) u0) ERR-INVALID-INPUT)
        (asserts! (> (len uri) u0) ERR-INVALID-INPUT)

        ;; Check if minting is enabled
        (asserts! (var-get mint-enabled) ERR-NOT-AUTHORIZED)

        ;; Transfer STX from minter to contract owner
        (unwrap! (stx-transfer? MINT-PRICE minter contract-owner)
            ERR-INSUFFICIENT-FUNDS
        )

        ;; Mint the NFT
        (unwrap! (nft-mint? BitcoinStamp token-id minter) ERR-ALREADY-EXISTS)

        ;; Store metadata
        (map-set token-metadata token-id {
            name: name,
            uri: uri,
            minted-at: stacks-block-height,
        })

        (ok token-id)
    )
)

;; Free mint for everyone (unlimited per wallet)
(define-public (free-mint
        (name (string-ascii 256))
        (uri (string-ascii 256))
    )
    (let (
            (token-id (unwrap! (get-next-token-id) ERR-MAX-SUPPLY-REACHED))
            (minter tx-sender)
        )
        ;; Validate inputs
        (asserts! (> (len name) u0) ERR-INVALID-INPUT)
        (asserts! (> (len uri) u0) ERR-INVALID-INPUT)

        ;; Check if minting is enabled
        (asserts! (var-get mint-enabled) ERR-NOT-AUTHORIZED)

        ;; Mint the NFT
        (unwrap! (nft-mint? BitcoinStamp token-id minter) ERR-ALREADY-EXISTS)

        ;; Store metadata
        (map-set token-metadata token-id {
            name: name,
            uri: uri,
            minted-at: stacks-block-height,
        })

        (ok token-id)
    )
)

;; Owner free mint to any recipient
(define-public (owner-mint
        (recipient principal)
        (name (string-ascii 256))
        (uri (string-ascii 256))
    )
    (let ((token-id (unwrap! (get-next-token-id) ERR-MAX-SUPPLY-REACHED)))
        ;; Only contract owner can use this
        (asserts! (is-eq tx-sender contract-owner) ERR-NOT-AUTHORIZED)

        ;; Validate inputs
        (asserts! (> (len name) u0) ERR-INVALID-INPUT)
        (asserts! (> (len uri) u0) ERR-INVALID-INPUT)

        ;; Mint the NFT
        (unwrap! (nft-mint? BitcoinStamp token-id recipient) ERR-ALREADY-EXISTS)

        ;; Store metadata
        (map-set token-metadata token-id {
            name: name,
            uri: uri,
            minted-at: stacks-block-height,
        })

        (ok token-id)
    )
)

;; Transfer NFT
(define-public (transfer
        (token-id uint)
        (sender principal)
        (recipient principal)
    )
    (begin
        ;; Check ownership
        (asserts! (is-eq tx-sender sender) ERR-NOT-AUTHORIZED)
        ;; Transfer the NFT
        (ok (unwrap! (nft-transfer? BitcoinStamp token-id sender recipient)
            ERR-NOT-FOUND
        ))
    )
)

;; Burn NFT
(define-public (burn (token-id uint))
    (let ((owner (unwrap! (nft-get-owner? BitcoinStamp token-id) ERR-NOT-FOUND)))
        ;; Check ownership
        (asserts! (is-eq tx-sender owner) ERR-NOT-AUTHORIZED)
        ;; Burn the NFT
        (ok (unwrap! (nft-burn? BitcoinStamp token-id owner) ERR-NOT-FOUND))
    )
)

;; Admin functions

;; Toggle minting
(define-public (toggle-mint-enabled)
    (begin
        (asserts! (is-eq tx-sender contract-owner) ERR-NOT-AUTHORIZED)
        (var-set mint-enabled (not (var-get mint-enabled)))
        (ok true)
    )
)

;; Update base URI
(define-public (set-base-uri (new-uri (string-ascii 256)))
    (begin
        (asserts! (is-eq tx-sender contract-owner) ERR-NOT-AUTHORIZED)
        (var-set base-uri new-uri)
        (ok true)
    )
)

;; Read-only functions

;; Get token owner
(define-read-only (get-owner (token-id uint))
    (ok (nft-get-owner? BitcoinStamp token-id))
)

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

;; Get token URI
(define-read-only (get-token-uri (token-id uint))
    (match (map-get? token-metadata token-id)
        data (ok (some (get uri data)))
        (ok none)
    )
)

;; Get last minted token ID
(define-read-only (get-last-token-id)
    (ok (var-get last-token-id))
)

;; Get total supply
(define-read-only (get-total-supply)
    (ok (var-get last-token-id))
)

;; Check if minting is enabled
(define-read-only (is-mint-enabled)
    (ok (var-get mint-enabled))
)

;; Get mint price
(define-read-only (get-mint-price)
    (ok MINT-PRICE)
)

;; Get max supply
(define-read-only (get-max-supply)
    (ok MAX-SUPPLY)
)

;; Get base URI
(define-read-only (get-base-uri)
    (ok (var-get base-uri))
)

;; Get contract owner
(define-read-only (get-contract-owner)
    (ok contract-owner)
)

Functions (17)

FunctionAccessArgs
toggle-mint-enabledpublic
get-next-token-idprivate
mintpublicname: (string-ascii 256
free-mintpublicname: (string-ascii 256
owner-mintpublicrecipient: principal, name: (string-ascii 256
burnpublictoken-id: uint
set-base-uripublicnew-uri: (string-ascii 256
get-ownerread-onlytoken-id: uint
get-token-metadataread-onlytoken-id: uint
get-token-uriread-onlytoken-id: uint
get-last-token-idread-only
get-total-supplyread-only
is-mint-enabledread-only
get-mint-priceread-only
get-max-supplyread-only
get-base-uriread-only
get-contract-ownerread-only