Customizable Wrapper

Explanations

All actions are stored in a main Smart Contract that the admin checks and performs operations based on what is stored in that moment on the queues.

Errors and constants

(define-constant err-invalid (err u300))
(define-constant err-too-many-pending-requests (err u200))
(define-constant err-not-owner (err u100))
(define-constant err-component-type-invalid (err u501))

(define-constant background-type "background-type")
(define-constant car-type "car-type")
(define-constant rim-type "rim-type")
(define-constant head-type "head-type")
(define-constant miami-type "miami")
(define-constant nyc-type "nyc")

(define-constant burn-address 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG)

Contract owner

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

Disassemble

;; eg. case
;;(contract-call? .degens mint-uri 'STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 "uriNiceDegen")
;; ::set_tx_sender STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.upgrade-contract add-disassemble-work-in-queue u1)
;; ::set_tx_sender ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.upgrade-contract disassemble-finalize u1 'STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 "DarkPurple" "BentleyBlack" "ClassyCream" "Miami_Syringe_Cigar")
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.cars get-token-uri u1)
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.cars get-owner u1)


;; Queue for work
(define-data-var disassemble-work-queue (list 100 {member: principal, token-id: uint}) (list))

(define-public (disassemble-finalize (token-id uint) (member principal) (background-name (string-ascii 30)) (car-name (string-ascii 30)) (rim-name (string-ascii 30)) (head-name (string-ascii 30))) 
  (begin     
    ;; Check that admin is calling this contract
    (asserts! (is-eq tx-sender (var-get contract-owner)) err-invalid)
    (asserts! (is-eq (some token-id) (get token-id (unwrap-panic (get-disassemble-head-work-queue)))) err-invalid)
    (unwrap-panic (contract-call? .backgrounds mint-name member background-name)) 
    (unwrap-panic (contract-call? .cars mint-name member car-name))
    (unwrap-panic (contract-call? .rims mint-name member rim-name))
    (unwrap-panic (contract-call? .heads mint-name member head-name))
    (pop-disassemble-work-queue)))

(define-read-only (get-disassemble-work-queue)
  ;; Get the actual work-queue so that we can process it
  (ok (var-get disassemble-work-queue)))

(define-read-only (get-disassemble-head-work-queue)
  ;; Get the first element in the work queue so that we can process it
  (ok (element-at (var-get disassemble-work-queue) u0)))

(define-public (prepare-disassemble (token-id uint))
  (add-disassemble-work-in-queue token-id))

(define-public (add-disassemble-work-in-queue (token-id uint))
  (ok 
    (begin
      ;; check user is owner of nft
      (asserts! 
        (is-eq (some tx-sender) (unwrap-panic (contract-call? .degens get-owner token-id))) 
        err-not-owner)    
      ;; transfer fees if not contract-owner
      (if (is-eq tx-sender (var-get contract-owner)) true (try! (fee-processing)))
      (let 
        ((work-queue-value (var-get disassemble-work-queue))
          (value-to-add {token-id: token-id, member: tx-sender}))
        (var-set disassemble-work-queue
          (begin
            ;; check user has not already inserted this
            ;; if the id is already in the queue that means the NFT is burnt => throws err-not-owner when calling for get-owner 
            ;; (asserts! 
            ;;   (is-none (index-of work-queue-value value-to-add))
            ;;   err-invalid
            ;; )
            ;; check user is not abusing the queue
            (asserts! 
              (< (len (filter is-disassemble-value-for-principal work-queue-value)) u5)
              err-too-many-pending-requests)
            (unwrap-panic (contract-call? .degens burn-token token-id))
            (append 
              (unwrap-panic (as-max-len? work-queue-value u99)) 
              value-to-add)))))))


(define-private (pop-disassemble-work-queue)
  (ok (let
    ((work-queue-value (var-get disassemble-work-queue)))
    (var-set disassemble-work-queue
      ;; Remove first element in list
      (filter is-disassemble-first-element work-queue-value)))))


(define-private (is-disassemble-first-element (value {token-id: uint, member: principal}))
  (let
    ((first-element (element-at (var-get disassemble-work-queue) u0)))
    (not 
      (and
        (is-some first-element)
        (is-eq value (unwrap-panic first-element))))))



;; Helper functions
(define-private (is-disassemble-value-for-principal (value {token-id: uint, member: principal}))
  (is-eq (get member value) tx-sender)

Assemble

;; eg. case
;; (contract-call? .backgrounds mint-name 'STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 "DarkPurple")
;; (contract-call? .cars mint-name 'STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 "BentleyBlack")
;; (contract-call? .rims mint-name 'STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 "ClassyCream")
;; (contract-call? .heads mint-name 'STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 "NYC_Antenna_BigSmile")
;; ::set_tx_sender STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.upgrade-contract add-assemble-work-in-queue u1 u1 u1 u1)
;; ::set_tx_sender ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM
;; (contract-call? .upgrade-contract assemble-finalize 'STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 "uri-custom")

(define-data-var assemble-work-queue (list 100 {member: principal, background-id: uint, car-id: uint, rim-id: uint, head-id: uint}) (list))

(define-public (assemble-finalize (member principal) (metadata-uri (string-ascii 99))) 
  (begin     
    ;; Check that admin is calling this contract
    (asserts! (is-eq tx-sender (var-get contract-owner)) err-invalid)
    (unwrap-panic (contract-call? .degens mint-uri member metadata-uri))
    (pop-assemble-work-queue)))

(define-read-only (get-assemble-work-queue)
  ;; Get the actual work-queue so that we can process it
  (ok (var-get assemble-work-queue)))

(define-read-only (get-assemble-head-work-queue)
  ;; Get the first element in the work queue so that we can process it
  (ok (element-at (var-get assemble-work-queue) u0)))

(define-public (prepare-assemble (background-id uint) (car-id uint) (rim-id uint) (head-id uint)) 
  (add-assemble-work-in-queue background-id car-id rim-id head-id))

(define-public (add-assemble-work-in-queue (background-id uint) (car-id uint) (rim-id uint) (head-id uint))
  (ok 
    (begin 
      ;; check user is owner of nft
      (asserts! 
        (and
          (is-eq (some tx-sender) (unwrap-panic (contract-call? .backgrounds get-owner background-id)))       
          (and
            (is-eq (some tx-sender) (unwrap-panic (contract-call? .cars get-owner car-id))) 
            (and
              (is-eq (some tx-sender) (unwrap-panic (contract-call? .rims get-owner rim-id))) 
              (is-eq (some tx-sender) (unwrap-panic (contract-call? .heads get-owner head-id))))))
        err-not-owner)    
      (if (is-eq tx-sender (var-get contract-owner)) true (try! (fee-processing)))
      (let ((work-queue-value (var-get assemble-work-queue))
          (value-to-add {
            member: tx-sender, 
            background-id: background-id,
            car-id: car-id,
            rim-id: rim-id,
            head-id: head-id
          }))      
        (var-set assemble-work-queue
          (begin
            ;; check user has not already inserted this
            ;; if the id is already in the queue that means the NFT is burnt => throws err-not-owner when calling for get-owner 
            ;; (asserts! 
            ;;   (is-none (index-of work-queue-value value-to-add))
            ;;   err-invalid
            ;; )
            ;; check user is not abusing the queue
            (asserts! 
              (< (len (filter is-assemble-value-for-principal work-queue-value)) u5)
              err-too-many-pending-requests)
            (unwrap-panic (contract-call? .backgrounds burn-token background-id))
            (unwrap-panic (contract-call? .cars burn-token car-id))
            (unwrap-panic (contract-call? .rims burn-token rim-id))
            (unwrap-panic (contract-call? .heads burn-token head-id))
            (append 
              (unwrap-panic (as-max-len? work-queue-value u99)) 
              value-to-add)))))))
              

(define-private (pop-assemble-work-queue)
  (ok 
    (let
      ((work-queue-value (var-get assemble-work-queue)))
      (var-set assemble-work-queue
        ;; Remove first element in list
        (filter is-assemble-first-element work-queue-value)))))



(define-private (is-assemble-first-element (value {background-id: uint, car-id: uint, rim-id: uint, head-id: uint, member: principal}))
  (let
    ((first-element (element-at (var-get assemble-work-queue) u0)))
    (not 
      (and
        (is-some first-element)
        (is-eq value (unwrap-panic first-element))))))

(define-private (is-assemble-value-for-principal (value {background-id: uint, car-id: uint, rim-id: uint, head-id: uint, member: principal}))
  (is-eq (get member value) tx-sender))

Swap

;; eg. case
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.degens mint-uri 'STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 "nice-link")
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.rims mint-name 'STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 "ClassyCream")
;; ::set_tx_sender STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.upgrade-contract add-swap-work-in-queue u1 u1 "rim-type")
;; ::set_tx_sender ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.upgrade-contract swap-finalize 'STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 "new-nice-link" "ClassyDark" "rim-type")
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.rims get-token-uri u2)
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.degens get-token-uri u4)


(define-data-var swap-work-queue (list 100 {member: principal, degen-id: uint, component-id: uint, component-type: (string-ascii 30)}) (list))

(define-public (swap-finalize (degen-id uint) (member principal) (metadata-uri-dgn (string-ascii 99)) (component-name (string-ascii 30)) (component-type (string-ascii 30)))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) err-invalid)
    (asserts! (is-eq (some degen-id) (get degen-id (unwrap-panic (get-swap-head-work-queue)))) err-invalid)
    (unwrap-panic (contract-call? .degens mint-uri member metadata-uri-dgn))
    (if (is-eq false
          (if (is-eq component-type background-type) 
            (unwrap-panic (contract-call? .backgrounds mint-name member component-name))
            (if (is-eq component-type car-type) 
              (unwrap-panic (contract-call? .cars mint-name member component-name))
              (if (is-eq component-type rim-type) 
                (unwrap-panic (contract-call? .rims mint-name member component-name))  
                (if (is-eq component-type head-type) 
                  (unwrap-panic (contract-call? .heads mint-name member component-name))
                  false))))) ;; component-type invalid
      err-component-type-invalid
      ;; if any mint would have failed the function call would have been rolled back
      (pop-swap-work-queue))))

(define-read-only (get-swap-work-queue)
  ;; Get the actual work-queue so that we can process it
  (ok (var-get swap-work-queue)))

(define-read-only (get-swap-head-work-queue)
  ;; Get the first element in the work queue so that we can process it
  (ok (element-at (var-get swap-work-queue) u0)))

(define-public (prepare-swap  (degen-id uint) (component-id uint) (component-type (string-ascii 30)))
  (add-swap-work-in-queue degen-id component-id component-type))

(define-public (add-swap-work-in-queue (degen-id uint) (component-id uint) (component-type (string-ascii 30)))
  (ok 
    (begin 
      ;; check user is owner of nft
      (asserts! 
        (and
          (is-eq (some tx-sender) (unwrap-panic (contract-call? .degens get-owner degen-id)))       
          (is-eq (some tx-sender)
              (if (is-eq component-type background-type) 
                (unwrap-panic (contract-call? .backgrounds get-owner component-id))
                (if (is-eq component-type car-type) 
                  (unwrap-panic (contract-call? .cars get-owner component-id))
                  (if (is-eq component-type rim-type) 
                    (unwrap-panic (contract-call? .rims get-owner component-id))  
                    (if (is-eq component-type head-type) 
                      (unwrap-panic (contract-call? .heads get-owner component-id))
                      none)))))) ;; component-type invalid
        err-not-owner)    
      (if (is-eq tx-sender (var-get contract-owner)) true (try! (fee-processing)))
      (let 
        ((work-queue-value (var-get swap-work-queue))
          (value-to-add { 
            member: tx-sender,
            degen-id: degen-id,
            component-id: component-id,
            component-type: component-type
            }))      
        (var-set swap-work-queue
          (begin
            ;; check user has not already inserted this
            ;; if the id is already in the queue that means the NFT is burnt => throws err-not-owner when calling for get-owner 
            ;; (asserts! 
            ;;   (is-none (index-of work-queue-value value-to-add))
            ;;   err-invalid
            ;; )
            ;; check user is not abusing the queue
            (asserts! 
              (< (len (filter is-swap-value-for-principal work-queue-value)) u5)
              err-too-many-pending-requests)
            (unwrap-panic (contract-call? .degens burn-token degen-id))
            (if (is-eq component-type background-type) 
              (unwrap-panic (contract-call? .backgrounds burn-token component-id))
              (if (is-eq component-type car-type) 
                (unwrap-panic (contract-call? .cars burn-token component-id))
                (if (is-eq component-type rim-type) 
                  (unwrap-panic (contract-call? .rims burn-token component-id))  
                  (if (is-eq component-type head-type) 
                    (unwrap-panic (contract-call? .heads burn-token component-id))
                    false))));; component-type invalid               
            (append 
              (unwrap-panic (as-max-len? work-queue-value u99)) 
              value-to-add)))))))


(define-private (pop-swap-work-queue)
  (ok 
    (let
      ((work-queue-value (var-get swap-work-queue)))
      (var-set swap-work-queue
        ;; Remove first element in list
        (filter is-swap-first-element work-queue-value)))))


(define-private (is-swap-first-element (value {degen-id: uint, component-id: uint, component-type: (string-ascii 30), member: principal}))
  (let
    ((first-element (element-at (var-get swap-work-queue) u0)))
    (not 
      (and
        (is-some first-element)
        (is-eq value (unwrap-panic first-element))))))


(define-private (is-swap-value-for-principal (value {degen-id: uint, component-id: uint, component-type: (string-ascii 30), member: principal}))
  (is-eq (get member value) tx-sender)
)

Merge



;; eg. case
;; ::set_tx_sender STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.miami-degens claim)
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.nyc-degens claim)
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.upgrade-contract add-merge-work-in-queue u1 "miami")
;; ::set_tx_sender ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.upgrade-contract get-merge-work-queue)
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.upgrade-contract merge-finalize 'STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 "nice-new-nft")
;; (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.degens get-token-uri u1)

(define-data-var merge-work-queue (list 100 {member: principal, degen-id: uint, degen-type: (string-ascii 30)}) (list))

(define-public (merge-finalize (degen-id uint) (member principal) (metadata-uri-dgn (string-ascii 99)))
  (begin
    (asserts! (is-eq tx-sender (var-get contract-owner)) err-invalid)
    (asserts! (is-eq (some degen-id) (get degen-id (unwrap-panic (get-merge-head-work-queue)))) err-invalid)
    (unwrap-panic (contract-call? .degens mint-uri member metadata-uri-dgn))
    (pop-merge-work-queue)))

(define-read-only (get-merge-work-queue)
  ;; Get the actual work-queue so that we can process it
  (ok (var-get merge-work-queue)))

(define-read-only (get-merge-head-work-queue)
  ;; Get the first element in the work queue so that we can process it
  (ok (element-at (var-get merge-work-queue) u0)))

(define-public (prepare-upgrade (degen-id uint) (degen-type (string-ascii 30)))
  (add-merge-work-in-queue degen-id degen-type))

(define-public (add-merge-work-in-queue (degen-id uint) (degen-type (string-ascii 30)))
  (ok 
    (begin
      ;; check user is owner of old Degen nft
      (asserts! 
        (is-eq (some tx-sender) 
          (if (is-eq degen-type miami-type)
            (unwrap-panic (contract-call? .miami-degens get-owner degen-id))
            (if (is-eq degen-type nyc-type)
              (unwrap-panic (contract-call? .nyc-degens get-owner degen-id))
              none))) 
        err-not-owner)    
      (if (is-eq tx-sender (var-get contract-owner)) true (try! (fee-processing)))
      (let ((work-queue-value (var-get merge-work-queue))
        (value-to-add { 
          member: tx-sender,
          degen-id: degen-id,
          degen-type: degen-type
          }))
        (var-set merge-work-queue
          (begin
            ;; already insured by the fact that the nft is burned
            ;; ;; check user has not already inserted this
            ;; (asserts! 
            ;;   (is-none (index-of work-queue-value value-to-add))
            ;;   err-invalid
            ;; )
            ;; check user is not abusing the queue
            (asserts! 
              (< (len (filter is-merge-value-for-principal work-queue-value)) u5)
              err-too-many-pending-requests)
            ;; (unwrap-panic (contract-call? .old-degens burn-token degen-id))
            (some (burn-old-nft degen-id degen-type))
            (append 
              (unwrap-panic (as-max-len? work-queue-value u99)) 
              value-to-add)))))))


(define-private (pop-merge-work-queue)
  (ok 
    (let
      ((work-queue-value (var-get merge-work-queue)))
      (var-set merge-work-queue
        ;; Remove first element in list
        (filter is-merge-first-element work-queue-value)))))


(define-private (burn-old-nft (degen-id uint) (degen-type (string-ascii 30))) 
  (if (is-eq 
        false
        (if (is-eq degen-type miami-type)
          (unwrap-panic (contract-call? .miami-degens transfer degen-id tx-sender burn-address))
          (if (is-eq degen-type nyc-type)
            (unwrap-panic (contract-call? .nyc-degens transfer degen-id tx-sender burn-address))
            false)))
    err-component-type-invalid
    (ok (some degen-id))))


(define-private (is-merge-first-element (value {degen-id: uint, degen-type: (string-ascii 30), member: principal}))
  (let
    ((first-element (element-at (var-get merge-work-queue) u0)))
    (not 
      (and
        (is-some first-element)
        (is-eq value (unwrap-panic first-element))))))


(define-private (is-merge-value-for-principal (value {degen-id: uint, degen-type: (string-ascii 30), member: principal}))
  (is-eq (get member value) tx-sender))

Utils

Transfer fee function

;; fees: 0.69 stx
(define-private (fee-processing)
  (stx-transfer? u690000 tx-sender (var-get contract-owner)))

Last updated