Source Code

;; title: Tokio:Vice by George Grant
;; version: v.1.0.0
;; author: Eriq Ferrari - eriq.btc
;; artist: George Grant - ggtv.btc
;; collection: Tokio:Vice by George Grant
;; description: Tokyo Vice represents an attempt at combining Asian and 
;;    Western Pop Culture / Art Deco with the style of Japanese artist 
;;    Hiroshi Nagai. Owning a piece of that collection will allow you 
;;    additional multipliers in our MEME APP Rewards system.
;; summary: Advanced SIP-009 NFT with built-in Marketplace
;; functions: Pricing, whitelist, and start block management, 
;;    Whitelisted users can mint during the time-locked free mint period,
;;    Minting supports for fungible tokens, Customizable collection limit, 
;;    Maximum supply enforced, Artist royalties enforced and customizable,
;;    Advanced built-in marketplace supports STX and fungible tokens, 
;;    Bulk actions enabled, Ownership transferrable
;; License: MIT

;; This contract implements the SIP-009 community-standard Non-Fungible Token trait
(impl-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait);; 

;; This contract use the SIP-010 community-standard Fungible Token trait
(use-trait sip-010-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait) ;; 

;; SIP-010 transfer implementation
(define-private (transfer-ft (token-contract <sip-010-trait>) (amount uint) (sender principal) (recipient principal))
  (contract-call? token-contract transfer amount sender recipient none)
)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;; NFT ;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Define the NFT's name
(define-non-fungible-token GGTV uint)

;; Keep track of the last minted token ID
(define-data-var last-token-id uint u0)

;; Contract variables
(define-data-var CONTRACT_OWNER (optional principal) (some tx-sender))
(define-data-var TREASURY principal tx-sender) ;; contract treasury
(define-data-var COLLECTION_LIMIT uint u200) ;; Limit to series of 200
(define-data-var START uint u0) ;; starting btc block
(define-data-var MINT_LIMIT uint u1) ;; max number of mints per wallet
(define-data-var FREEMINT_DURATION uint u144) ;; Free mint last 1 day 144 btc blocks

;; Contract constants
(define-constant TIMELOCK u50000) ;; blocks before unlock supply change
(define-constant MAX_SUPPLY u500) ;; collection will never exceed this supply

;; Errors
(define-constant ERR_OWNER_ONLY (err u1000))
(define-constant ERR_OWNERSHIP_RENOUNCED (err false))
(define-constant ERR_NOT_TOKEN_OWNER (err u1002))
(define-constant ERR_ALREADY_STARTED (err u2000))
(define-constant ERR_NOT_STARTED (err u2001))
(define-constant ERR_TOO_EARLY (err u2002))
(define-constant ERR_PAST_BLOCK (err u2003))
(define-constant ERR_SOLD_OUT (err u3000))
(define-constant ERR_FREEMINT_ENDED (err u3001))
(define-constant ERR_ALREADY_MINTED (err u3002))
(define-constant ERR_TOKEN_NOT_ALLOWED (err u3003))



;; Base uri string to fetch metadata
(define-data-var base-uri (string-ascii 80) "https://memecoinstx.com/metadata/GGTV/")

;; Contract mapping
(define-map BALANCE principal uint)
(define-map FREEMINTERS principal uint)
(define-map MINTED principal uint)
(define-map WHITELISTED_TOKEN principal uint) ;; Contract address and price

;; Show the current collection limit (timelocked).
(define-read-only (get-collection-limit)
  (ok (var-get COLLECTION_LIMIT))
)

;; SIP-009 function: Get the last minted token ID.
(define-read-only (get-last-token-id)
  (ok (var-get last-token-id))
)

;; SIP-009 function: Get link where token metadata is hosted
(define-read-only (get-token-uri (id uint))
  (ok (some (concat (var-get base-uri) (int-to-ascii id))))
)

;; SIP-009 function: Get the owner of a given token
(define-read-only (get-owner (id uint))
  (ok (nft-get-owner? GGTV id))
)

;; SIP-009 function: Transfer NFT token to another owner.
(define-public (transfer
    (id uint)
    (sender principal)
    (recipient principal)
  )
  (let (
    (owner (unwrap-panic (unwrap! (get-owner id) ERR_NOT_MINTED)))
  )
    (asserts! (is-eq tx-sender owner) ERR_NOT_TOKEN_OWNER)
    (map-delete MARKET id)
    ;; Increase the wallet balance
    (add-one recipient)
    ;; Decrease the wallet balance
    (remove-one sender)
    (print {
        topic: "transfer",
        sender: sender,
        recipient: recipient,
    })
    (nft-transfer? GGTV id sender recipient)
  )
)

;; Read-only. Show the owner address
(define-read-only (show-the-contract-owner)
  (ok (var-get CONTRACT_OWNER))
)

;; helper to check the contract caller is the owner
(define-private (is-admin)
  (let (
    (owner (var-get CONTRACT_OWNER))
  )
  (if (is-some owner)
    (is-eq contract-caller (unwrap-panic owner))
    false
  )
  )
)

;; Admin function to change the owner address
(define-public (admin-change-owner (address principal)) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (var-set CONTRACT_OWNER (some address))
    (print {
        topic: "new owner",
        owner: address,
    })
    (ok true)
  )
)

;; Admin function to renounce the ownership
(define-public (admin-renounce-ownership) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (var-set CONTRACT_OWNER none)
    (print {
        topic: "ownership renounced",
        owner: none,
    })
    (ok true)
  )
)

;; Admin function to start the free mint
(define-public (admin-set-token-base-ui (uri (string-ascii 80))) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (var-set base-uri uri)
    (print {
        topic: "metadata-update",
        base-uri: uri,
        block: burn-block-height
    })
    (print { ;; common notification for metadata update
      notification: "token-metadata-update", 
      payload: { token-class: "nft", contract-id: (as-contract tx-sender) }
    })
    (ok true)
  )
)

;; Admin function to change the start for free mint
(define-public (admin-change-start (block uint)) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (asserts! (or (< burn-block-height (var-get START)) (is-eq (var-get START) u0)) ERR_ALREADY_STARTED)
    (asserts! (< burn-block-height block) ERR_PAST_BLOCK)
    (var-set START block)
    (print {
        topic: "start",
        block: block
    })
    (ok true)
  )
)

;; Admin function to change the duration of the free mint
;; first you need to configure a start date or will fail
(define-public (admin-change-freemint-duration (blocks uint)) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (asserts! (or (< burn-block-height (var-get START)) (is-eq (var-get START) u0)) ERR_ALREADY_STARTED)
    (var-set FREEMINT_DURATION blocks)
    (print {
        topic: "freemint duration",
        blocks: blocks
    })
    (ok true)
  )
)

;; Admin function to change the collection limit
;; Can be executed after one year from mint.
(define-public (admin-change-supply (supply uint)) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (asserts! (and (> supply (var-get COLLECTION_LIMIT)) (<= supply MAX_SUPPLY)) ERR_OUT_OF_RANGE)
    (asserts! (> burn-block-height (+ (var-get START) TIMELOCK)) ERR_TOO_EARLY)
    (var-set COLLECTION_LIMIT supply)
    (print {
        topic: "supply extended",
        supply: supply
    })
    (ok true)
  )
)

;; Admin function to change the mints per wallet
(define-public (admin-change-mints-per-wallet (mints uint)) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (var-set MINT_LIMIT mints)
    (print {
        topic: "new mint limit",
        mints: mints
    })
    (ok true)
  )
)

;; Admin function to change the treasury address
(define-public (admin-change-treasury (address principal)) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (var-set TREASURY address)
    (print {
        topic: "new treasury address",
        treasury: address,
    })
    (ok true)
  )
)

;; Admin function to change the whitelisted tokens
(define-public (admin-add-token (address principal) (price uint)) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (map-set WHITELISTED_TOKEN address price)
    (print {
        topic: "new token whitelisted",
        contract: address,
        price: price,
    })
    (ok true)
  )
)

;; Admin function to remove the whitelisted tokens
(define-public (admin-remove-token (address principal) ) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (map-delete WHITELISTED_TOKEN address )
    (print {
        topic: "token removed",
        contract: address,
    })
    (ok true)
  )
)

;; Admin function to add free minters
(define-public (admin-add-freeminters (freeminters (list 100 {address: principal, quantity: uint}))) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (fold check-err (map add-freeminter freeminters) (ok true))
  )
)

(define-private (add-freeminter (wallet {address: principal, quantity: uint}))
  (let (
    (address (get address wallet))
    (quantity (get quantity wallet))
  ) 
  (map-set FREEMINTERS address quantity)
  (print {
        topic: "added to freeminters",
        wallet: address,
        quantity: quantity,
    })
  (ok true)
  )
)

;; Admin airdrop
(define-public (admin-airdrop (recipient principal)) 
  ;; Create the new token ID by incrementing the last minted ID.
  (let ((id (+ (var-get last-token-id) u1)) )
    ;; Only the contract owner can airdrop a token.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    ;; Ensure the collection stays within the limit.
    (asserts! (< (var-get last-token-id) (var-get COLLECTION_LIMIT)) ERR_SOLD_OUT)
    ;; Mint the NFT and send it to the given recipient.
    (try! (nft-mint? GGTV id recipient))
    ;; Update the last minted token ID.
    (var-set last-token-id id)
    ;; Increase the wallet balance
    (add-one recipient)
    ;; Print mint information
    (print {
        topic: "airdrop",
        recipient: recipient,
        id: id,
        balance: (get-owner-balance recipient),
    })
    ;; Return a success status and the newly minted NFT ID.
    (ok id)
  )
)

;; Mint a new NFT for free
(define-public (freemint)
  ;; Create the new token ID by incrementing the last minted ID.
  (let ((id (+ (var-get last-token-id) u1)) (freemints (available-freemints)))
    ;; Ensure the collection stays within the limit.
    (asserts! (< (var-get last-token-id) (var-get COLLECTION_LIMIT)) ERR_SOLD_OUT)
    (asserts! (>= burn-block-height (var-get START)) ERR_NOT_STARTED)
    (asserts! (< burn-block-height (+ (var-get FREEMINT_DURATION) (var-get START))) ERR_FREEMINT_ENDED)
    (asserts! (> (available-freemints) u0) ERR_ALREADY_MINTED)
    ;; Mint the NFT and send it to the given recipient.
    (try! (nft-mint? GGTV id contract-caller))
    (map-set FREEMINTERS contract-caller (- freemints u1))
    ;; Update the last minted token ID.
    (var-set last-token-id id)
    ;; Increase the wallet balance
    (add-one contract-caller)
    ;; Print mint information
    (print {
        topic: "freemint",
        recipient: contract-caller,
        id: id,
        balance: (get-owner-balance contract-caller),
        available_free_mints: (- freemints u1),
    })
    ;; Return a success status and the newly minted NFT ID.
    (ok id)
  )
)

;; Mint a new NFT.
(define-public (mint (token <sip-010-trait>))
  ;; Create the new token ID by incrementing the last minted ID.
  (let (
    (id (+ (var-get last-token-id) u1)) 
    (mints (available-mints))
    (price (get-token-price (contract-of token)))
    )
    ;; Ensure the collection stays within the limit.
    (asserts! (< (var-get last-token-id) (var-get COLLECTION_LIMIT)) ERR_SOLD_OUT)
    (asserts! (> burn-block-height (+ (var-get FREEMINT_DURATION) (var-get START))) ERR_NOT_STARTED)
    (asserts! (> (available-mints) u0) ERR_ALREADY_MINTED)
    (asserts! (> price u0) ERR_TOKEN_NOT_ALLOWED)
    ;; Mint the NFT and send it to the given recipient.
    (try! (nft-mint? GGTV id contract-caller))
    (try! (transfer-ft token price contract-caller (var-get TREASURY)))
    (map-set MINTED contract-caller (+ (default-to u0 (map-get? MINTED contract-caller)) u1))
    ;; Update the last minted token ID.
    (var-set last-token-id id)
    ;; Increase the wallet balance
    (add-one contract-caller)
    ;; Print mint information
    (print {
        topic: "mint",
        recipient: contract-caller,
        id: id,
        balance: (get-owner-balance contract-caller),
        available_mints: (- mints u1),
    })
    ;; Return a success status and the newly minted NFT ID.
    (ok id)
  )
)

;; Read-only. Get the price for a specific token
(define-read-only (get-token-price (address principal)) 
  (default-to u0 (map-get? WHITELISTED_TOKEN address))
)

;; Read-only. Get an owner sfts balance
(define-read-only (get-owner-balance (address principal))
  (default-to u0 (map-get? BALANCE address))
)

;; Helper to get the remaining mints for the contract-caller
(define-read-only (available-mints)
  (- (var-get MINT_LIMIT) (default-to u0  (map-get? MINTED contract-caller)))
)

;; Helper to get the remaining free mints for the contract-caller
(define-read-only (available-freemints)
  (default-to u0 (map-get? FREEMINTERS contract-caller))
)

;; Helper to loop bulk actions
(define-private (check-err (result (response bool uint)) (prior (response bool uint)))
  (match prior ok-value result err-value (err err-value))
)

;; Helper to add one nft to owner balance
(define-private (add-one (address principal)) 
 (map-set BALANCE address (+ (get-owner-balance address) u1))
)

;; Helper to remove one nft to owner balance
(define-private (remove-one (address principal)) 
 (map-set BALANCE address (- (get-owner-balance address) u1))
)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;; Marketplace ;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; This contract implement commission trait
(use-trait commission-trait 'SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.commission-trait.commission) ;; 

;; New Trait. This contract implement commission-ft trait
(use-trait commission-ft-trait 'SP39WYZ7BCDD4E7XSKDFVQSERXYTT9MEFE5683JGR.commission-ft-trait.commission-ft) ;;

;; Marketplace mapping
(define-map MARKET uint {price: uint, commission: principal, token: (optional principal),})
(define-map MARKETPLACE_TOKENS principal bool) 

;; Marketplace variables
(define-data-var ROYALTIES uint u300) ;; 3% royalties

;; Marketplace errors
(define-constant ERR_NOT_LISTED (err u4000))
(define-constant ERR_NOT_MINTED (err u4001))
(define-constant ERR_WRONG_COMMISSION (err u4002))
(define-constant ERR_OUT_OF_RANGE (err u4003))
(define-constant ERR_STX_ONLY (err u4004))
(define-constant ERR_WRONG_TOKEN (err u4005))

;; Admin function to change the artist royalties min u0 (no royaalties) max u1000 (10%)
(define-public (admin-change-royalties (royalties uint)) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (asserts! (and (>= royalties u0) (< royalties u1000)) ERR_OUT_OF_RANGE)
    (var-set ROYALTIES royalties)
    (print {
        topic: "change royalties",
        royalties: royalties,
    })
    (ok true)
  )
)

;; Admin function to whitelist tokens for marketplace
(define-public (admin-add-token-marketplace (address principal)) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (map-set MARKETPLACE_TOKENS address true)
    (print {
        topic: "added token for marketplace",
        token: address,
    })
    (ok true)
  )
)

;; Admin function to remove tokens for marketplace
(define-public (admin-remove-token-marketplace (address principal)) 
  (begin 
    ;; Only the contract owner can start the mint.
    (asserts! (is-admin) ERR_OWNER_ONLY)
    (map-delete MARKETPLACE_TOKENS address)
    (print {
        topic: "removed token for marketplace",
        token: address,
    })
    (ok true)
  )
)

;; Marketplace built-in functions

;; List a NFT in uSTX
(define-public (list-in-ustx (id uint) (price uint) (commission <commission-trait>))
  (let (
    (owner (unwrap-panic (unwrap! (get-owner id) ERR_NOT_MINTED)))
  ) 
    (asserts! (is-eq tx-sender owner) ERR_NOT_TOKEN_OWNER)
    (map-set MARKET id {token: none, price: price, commission: (contract-of commission)})
    (print {
      topic: "list-in-ustx",
      id: id,
      price: price,
      commission: commission,
    })
    (ok true)
  )
)

;; Unlist a NFT
(define-public (unlist-in-ustx (id uint) )
  (let (
    (owner (unwrap-panic (unwrap! (get-owner id) ERR_NOT_MINTED)))
  ) 
    (asserts! (is-eq tx-sender owner) ERR_NOT_TOKEN_OWNER)
    (map-delete MARKET id)
    (print {
      topic: "unlist-in-ustx",
      id: id,
    })
    (ok true)
  )
)

;; Buy a NFT in uSTX
(define-public (buy-in-ustx (id uint) (commission <commission-trait>) )
  (let (
    (listing (get-listing id))
    (price (get price listing))
    (token (get token listing))
    (royalties (var-get ROYALTIES))
    (royaltiesAmount (/ (* royalties price) u10000))
    (owner (unwrap-panic (unwrap! (get-owner id) ERR_NOT_MINTED)))
  ) 
    (asserts! (> price u0) ERR_NOT_LISTED)
    (asserts! (is-none token) ERR_STX_ONLY)
    (asserts! (is-eq (contract-of commission) (get commission listing)) ERR_WRONG_COMMISSION)
    (if (> royalties u0) 
        (begin 
          (try! (stx-transfer? (- price royaltiesAmount) tx-sender owner))
          (try! (stx-transfer? royaltiesAmount tx-sender (var-get TREASURY)))
        )
        (try! (stx-transfer? price tx-sender owner))
    )
    (try! (contract-call? commission pay id price))
    (try! (nft-transfer? GGTV id owner tx-sender))
    (map-delete MARKET id)
    ;; Increase the wallet balance
    (add-one tx-sender)
    ;; Decrease the wallet balance
    (remove-one owner)
    (print {
      topic: "buy-in-ustx",
      id: id,
      price: price,
      commission: commission,
      seller: owner,
      taker: tx-sender,
    })
    (print {
        topic: "transfer",
        sender: owner,
        recipient: tx-sender,
    })
    (ok true)
  )
)

;; Marketplace bulk functions

;; List up to 100 NFTs in uSTX
(define-public (list-many (names (list 100 {id: uint, price: uint, commission: <commission-trait>})))
  (fold check-err (map list-single names) (ok true))
)

;; helper to list a single NFT in STX
(define-private (list-single (name {id: uint, price: uint, commission: <commission-trait>}))
  (list-in-ustx (get id name) (get price name) (get commission name))
)

;; Unlist up to 100 NFTs
(define-public (unlist-many (names (list 100 uint))) ;; valid for both stx and ft listing
  (fold check-err (map unlist-single names) (ok true))
)

;; helper to unlist a single NFT
(define-private (unlist-single (id uint))
  (unlist-in-ustx id)
)

;; Buy up to 100 NFTs in uSTX
(define-public (buy-many (names (list 100 {id: uint, commission: <commission-trait>})))
  (fold check-err (map buy-single names) (ok true))
)

;; helper to buy a single NFT in STX
(define-private (buy-single (name {id: uint, commission: <commission-trait>}))
  (buy-in-ustx (get id name) (get commission name))
)

;; Read-only. Get the listing data
(define-read-only (get-listing (id uint))
  (default-to {price: u0, commission: (as-contract tx-sender), token: none} (map-get? MARKET id))
)

;; Fungible token Marketplace built-in functions

;; List a NFT in FT (only allowed tokens)
(define-public (list-in-ft (id uint) (ft <sip-010-trait>) (price uint) (commission <commission-ft-trait>) )
  (let (
    (owner (unwrap-panic (unwrap! (get-owner id) ERR_NOT_MINTED)))
  ) 
    (asserts! (is-eq tx-sender owner) ERR_NOT_TOKEN_OWNER)
    (asserts! (is-token-allowed (contract-of ft)) ERR_TOKEN_NOT_ALLOWED)
    (map-set MARKET id {
            token: (some (contract-of ft)), 
            price: price, 
            commission: (contract-of commission)
            })
    (print {
      topic: "list-in-ft",
      id: id,
      token: ft,
      price: price,
      commission: commission,
    })
    (ok true)
  )
)

;; Buy a NFT in FT (only allowed tokens)
(define-public (buy-in-ft (id uint) (ft <sip-010-trait>) (commission <commission-ft-trait>) )
  (let (
    (listing (get-listing id))
    (price (get price listing))
    (token (get token listing))
    (royalties (var-get ROYALTIES))
    (royaltiesAmount (/ (* royalties price) u10000))
    (owner (unwrap-panic (unwrap! (get-owner id) ERR_NOT_MINTED)))
  ) 
    (asserts! (> price u0) ERR_NOT_LISTED)
    (asserts! (is-eq (unwrap! token ERR_STX_ONLY) (contract-of ft)) ERR_WRONG_TOKEN)
    (asserts! (is-eq (contract-of commission) (get commission listing)) ERR_WRONG_COMMISSION)
    (if (> royalties u0) 
        (begin 
          (try! (transfer-ft ft (- price royaltiesAmount) tx-sender owner))
          (try! (transfer-ft ft royaltiesAmount tx-sender (var-get TREASURY)))
        )
        (try! (transfer-ft ft price tx-sender owner))
    )
    (try! (contract-call? commission pay id price ft))
    (try! (nft-transfer? GGTV id owner tx-sender))
    (map-delete MARKET id)
    ;; Increase the wallet balance
    (add-one tx-sender)
    ;; Decrease the wallet balance
    (remove-one owner)
    (print {
      topic: "buy-in-ft",
      id: id,
      price: price,
      token: token,
      commission: commission,
      seller: owner,
      taker: tx-sender,
    })
    (print {
        topic: "transfer",
        sender: owner,
        recipient: tx-sender,
    })
    (ok true)
  )
)

;; Fungible token Marketplace bulk functions

;; List up to 100 NFTs in FT (only allowed tokens)
(define-public (list-many-ft (names (list 100 {id: uint, token: <sip-010-trait>, price: uint, commission: <commission-ft-trait>})))
  (fold check-err (map list-single-ft names) (ok true))
)

;; helper to list a single NFT in FT (only allowed tokens)
(define-private (list-single-ft (name {id: uint, price: uint, token: <sip-010-trait>, commission: <commission-ft-trait>}))
  (list-in-ft (get id name) (get token name) (get price name) (get commission name))
)

;; Buy up to 100 NFTs in FT (only allowed tokens)
(define-public (buy-many-ft (names (list 100 {id: uint, token: <sip-010-trait>, commission: <commission-ft-trait>})))
  (fold check-err (map buy-single-ft names) (ok true))
)

;; helper to buy a single NFT in FT (only allowed tokens)
(define-private (buy-single-ft (name {id: uint, token: <sip-010-trait>, commission: <commission-ft-trait>}))
  (buy-in-ft (get id name) (get token name) (get commission name))
)

;; Read-only. Get the Marketplace allowed tokens
(define-read-only (is-token-allowed (address principal))
  (default-to false (map-get? MARKETPLACE_TOKENS address))
)

Functions (44)

FunctionAccessArgs
transfer-ftprivatetoken-contract: <sip-010-trait>, amount: uint, sender: principal, recipient: principal
get-collection-limitread-only
get-last-token-idread-only
get-token-uriread-onlyid: uint
get-ownerread-onlyid: uint
show-the-contract-ownerread-only
is-adminprivate
admin-change-ownerpublicaddress: principal
admin-renounce-ownershippublic
admin-set-token-base-uipublicuri: (string-ascii 80
admin-change-startpublicblock: uint
admin-change-freemint-durationpublicblocks: uint
admin-change-supplypublicsupply: uint
admin-change-mints-per-walletpublicmints: uint
admin-change-treasurypublicaddress: principal
admin-add-tokenpublicaddress: principal, price: uint
admin-add-freeminterspublicfreeminters: (list 100 {address: principal, quantity: uint}
add-freeminterprivatewallet: {address: principal, quantity: uint}
admin-airdroppublicrecipient: principal
freemintpublic
mintpublictoken: <sip-010-trait>
get-token-priceread-onlyaddress: principal
get-owner-balanceread-onlyaddress: principal
available-mintsread-only
available-freemintsread-only
check-errprivateresult: (response bool uint
add-oneprivateaddress: principal
remove-oneprivateaddress: principal
admin-change-royaltiespublicroyalties: uint
admin-add-token-marketplacepublicaddress: principal
admin-remove-token-marketplacepublicaddress: principal
list-in-ustxpublicid: uint, price: uint, commission: <commission-trait>
list-manypublicnames: (list 100 {id: uint, price: uint, commission: <commission-trait>}
list-singleprivatename: {id: uint, price: uint, commission: <commission-trait>}
unlist-manypublicnames: (list 100 uint
unlist-singleprivateid: uint
buy-manypublicnames: (list 100 {id: uint, commission: <commission-trait>}
buy-singleprivatename: {id: uint, commission: <commission-trait>}
get-listingread-onlyid: uint
list-many-ftpublicnames: (list 100 {id: uint, token: <sip-010-trait>, price: uint, commission: <commission-ft-trait>}
list-single-ftprivatename: {id: uint, price: uint, token: <sip-010-trait>, commission: <commission-ft-trait>}
buy-many-ftpublicnames: (list 100 {id: uint, token: <sip-010-trait>, commission: <commission-ft-trait>}
buy-single-ftprivatename: {id: uint, token: <sip-010-trait>, commission: <commission-ft-trait>}
is-token-allowedread-onlyaddress: principal