GameFi
  • General Intro
  • 📃R & D
  • 💻Workshop
    • 🏎️NFT Web App Integration
      • 📄Prerequirements
      • Decentralized Storage
      • Smart Contract NFTs
      • Creating the React Dapp
      • Updating Startup File, Wallet Connect & Main Menu
      • Fetching NFTs, Stacks and Hiro API
      • Rendering NFTs owned
      • Selecting an NFT
      • Mapping Scenes
      • Creating the playable game
  • 🪙Trustless Rewards - M1
    • General idea
    • Flow Lobbies
    • Smart Contract
  • 🎨Customizable NFTs - M2
    • General Idea
    • Flow Customizable NFTs
    • Smart Contracts
      • Component
      • Customizable Wrapper
  • 🎁Lootbox on Chain - M3
    • General Idea
    • Tech Explained
    • Smart Contracts
      • Item
      • Lootbox
      • Lootbox Manager
  • 📝Message Signing
    • General Idea
    • GameFi Use Cases
    • App Explained
  • 🪵SFTs
    • General Idea & Base SFTs Static Deployments
    • Metadata Structure
    • Static Flow and Smart Contracts
    • Advanced SFTs Dynamic Deployments
    • Dynamic Flow and Smart Contracts
    • Dapp Integrating SFTs
      • Front End
      • Back End
    • Resources
  • ⚡Subnets
    • Overall for subnets
  • Roadmap
  • 💾Decentralized Storage
    • Gaia
    • Pinata
    • Host SFTs and NFTs into Pinata
    • Gaia integration to host game resources
  • 🔗External Knowledge
    • Getting Started
    • Hiro API
    • Stacks Docs
    • Clarity Book
    • Hiro Tutorials
Powered by GitBook
On this page
  • Preface
  • Explanations
  • Traits used
  • Errors
  • Variable Data Stored - Maps and Vars
  • SIP009: Standard Implementations for NFTs
  • Item Name-URI
  • Item Mint

Was this helpful?

  1. Customizable NFTs - M2
  2. Smart Contracts

Component

Preface

It is exemplified and used with the background contracts for easier understanding.

The more general version will be component instead of background and the name of the components

component-1, component-2, component-3, etc. instead of actual names for the components.

Explanations

All Smart Contracts for Components follow the same structure. Below is exemplified with the Background Smart Contract

Traits used

This trait enforces that the standard for NFTs is implemented in this smart contract through SIP009.

(use-trait nft-trait .nft-trait.nft-trait)

Errors

Errors for different cases such as:

  • should be called by owner of lootbox, but someone else called

  • the name does not exist in the map for components.

  • doens not have the rights to call, but tried

(define-constant err-owner-only (err u100))
(define-constant err-invalid-name (err u301))
(define-constant err-no-rights (err u403))

Variable Data Stored - Maps and Vars

Every NFT has a token-url value attributed. This helps in calling the get-token-uri function.

Map name-url - keeps all types of items the collection has. On deployment it will have DarkPurple, Emerald, Goldie, Orange, Purple, Sunset with their token-URI.

Also keep contract-owner to let him perform mints, add and update new item-names to the collection and remove old ones.

(define-map token-url { token-id: uint } { url: (string-ascii 256) })
;; this is used if we want for a given attribute value to give a specific url
;; eg. purple background -> ipfs://dasd..
(define-map name-url { name: (string-ascii 30)} { url: (string-ascii 256) })

;; Populated map on deployment
(map-set name-url  {name: "DarkPurple"} {url: "ipfs://QmSUD8LoZL4ChE1LRmhcACsP1FJCaHuWpW8FXEtedD1rPo/DarkPurple.json"})
(map-set name-url  {name: "Emerald"} {url: "ipfs://QmSUD8LoZL4ChE1LRmhcACsP1FJCaHuWpW8FXEtedD1rPo/Emerald.json"})
(map-set name-url  {name: "Goldie"} {url: "ipfs://QmSUD8LoZL4ChE1LRmhcACsP1FJCaHuWpW8FXEtedD1rPo/Goldie.json"})
(map-set name-url  {name: "Orange"} {url: "ipfs://QmSUD8LoZL4ChE1LRmhcACsP1FJCaHuWpW8FXEtedD1rPo/Orange.json"})
(map-set name-url  {name: "Purple"} {url: "ipfs://QmSUD8LoZL4ChE1LRmhcACsP1FJCaHuWpW8FXEtedD1rPo/Purple.json"})
(map-set name-url  {name: "Sunset"} {url: "ipfs://QmSUD8LoZL4ChE1LRmhcACsP1FJCaHuWpW8FXEtedD1rPo/Sunset.json"})

;; Owner
(define-data-var contract-owner principal tx-sender)

;; Store the last issues token ID
(define-data-var last-id uint u0)

SIP009: Standard Implementations for NFTs

;; SIP009: Transfer token to a specified principal
(define-public (transfer (token-id uint) (sender principal) (recipient principal))
  (begin
    (asserts! (is-eq tx-sender sender) err-no-rights)
    (nft-transfer? background token-id sender recipient)))

(define-public (transfer-memo (token-id uint) (sender principal) (recipient principal) (memo (buff 34)))
  (begin 
    (try! (transfer token-id sender recipient))
    (print memo)
    (ok true)))

;; SIP009: Get the owner of the specified token ID
(define-read-only (get-owner (token-id uint))
  ;; Make sure to replace background
  (ok (nft-get-owner? background token-id)))

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

(define-read-only (get-token-uri (token-id uint)) 
  (let ((token-urr (get url (map-get? token-url {token-id: token-id})))) 
  (ok token-urr)))

;; Burn a token
(define-public (burn-token (token-id uint))  
  (begin     
    (asserts! (is-eq (some tx-sender) (nft-get-owner? background token-id) ) err-no-rights)     
    (nft-burn? background token-id tx-sender)))

Item Name-URI

Get-name-url - anyone can get the saved URI for the specified URI

Set-name-url - only admins can store the URI to the name

Remove-name-url - only admins can remove the key name from the map

(define-read-only (get-name-url (name (string-ascii 30)))
  (let ((token-urr (get url (map-get? name-url {name: name})))) 
    (ok token-urr)))

(define-public (set-name-url (name (string-ascii 30)) (url (string-ascii 30))) 
  (begin
    (asserts! 
      (or
        (is-eq tx-sender (var-get contract-owner)) 
        (is-eq tx-sender (var-get contract-lootbox))) 
      err-admin-only)
    (ok (map-set name-url {name: name} {url: url}))))

(define-public (remove-name-url (name (string-ascii 30))) 
  (begin
  (asserts! 
      (or
        (is-eq tx-sender (var-get contract-owner)) 
        (is-eq tx-sender (var-get contract-lootbox))) 
      err-admin-only)
    (ok (map-delete name-url {name: name}))))

Item Mint

There are two ways to mint a new item, by using directly the URI of the NFT, or by minting with the name and getting the URI from the values stored on-chain in the map. Safer and easier to use mint-name as it checks the type of item minted already exists in the map.

;; Internal - Mint new NFT
(define-private (mint (new-owner principal))
  (begin 
    (asserts! (is-eq tx-sender (var-get contract-owner)) err-owner-only)
    (let 
      ((next-id (+ u1 (var-get last-id))))
      (var-set last-id next-id)
      (nft-mint? background next-id new-owner))))

(define-public (mint-url (address principal) (url (string-ascii 256)))
  (begin 
    (asserts! (is-eq tx-sender (var-get contract-owner)) err-owner-only)
    (let 
      ((next-id (+ u1 (var-get last-id))))
      (map-set token-url {token-id: next-id} {url: url})
      (var-set last-id next-id)
      (nft-mint? background next-id address))))

(define-public (mint-name (address principal) (name (string-ascii 30)))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) err-owner-only)
    (let 
    ;; define and assign: next-id and url
      ((next-id (+ u1 (var-get last-id)))
        (url (get url (map-get? name-url {name: name}))))
      (if (is-none url)
        err-invalid-name
        (begin 
          (map-set token-url {token-id: next-id} {url: (unwrap-panic url)})
          (var-set last-id next-id)
          (nft-mint? background next-id address))))))
PreviousSmart ContractsNextCustomizable Wrapper

Last updated 2 years ago

Was this helpful?

🎨