Source Code

(define-non-fungible-token nestable-nft uint)

(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-NOT-FOUND (err u101))
(define-constant ERR-ALREADY-EXISTS (err u102))
(define-constant ERR-INVALID-PARENT (err u103))
(define-constant ERR-PENDING-LIMIT (err u104))
(define-constant ERR-NOT-PENDING (err u105))
(define-constant ERR-IS-EQUIPPED (err u106))
(define-constant ERR-CIRCULAR-REF (err u107))

(define-data-var token-id-nonce uint u0)
(define-data-var max-pending-children uint u128)

(define-map token-parent uint {parent-id: uint, parent-contract: (optional principal)})
(define-map token-children uint (list 200 {child-id: uint, child-contract: principal}))
(define-map pending-children uint (list 128 {child-id: uint, child-contract: principal}))
(define-map token-uri uint (string-ascii 256))

(define-read-only (get-last-token-id)
  (ok (var-get token-id-nonce))
)

(define-read-only (get-token-uri (token uint))
  (ok (map-get? token-uri token))
)

(define-read-only (get-owner (token uint))
  (ok (nft-get-owner? nestable-nft token))
)

(define-read-only (get-direct-owner (token uint))
  (let ((parent-data (default-to {parent-id: u0, parent-contract: none} (map-get? token-parent token))))
    (ok {
      parent-id: (get parent-id parent-data),
      parent-contract: (get parent-contract parent-data),
      is-nft: (if (> (get parent-id parent-data) u0) true false)
    })
  )
)

(define-read-only (get-root-owner (token uint))
  (let ((parent-data (unwrap! (map-get? token-parent token) (ok tx-sender))))
    (if (is-eq (get parent-id parent-data) u0)
      (ok (unwrap! (nft-get-owner? nestable-nft token) ERR-NOT-FOUND))
      (ok (unwrap! (nft-get-owner? nestable-nft (get parent-id parent-data)) ERR-NOT-FOUND))
    )
  )
)

(define-read-only (get-children (token uint))
  (ok (default-to (list) (map-get? token-children token)))
)

(define-read-only (get-pending-children (token uint))
  (ok (default-to (list) (map-get? pending-children token)))
)

(define-read-only (is-root (token uint))
  (let ((parent-data (default-to {parent-id: u0, parent-contract: none} (map-get? token-parent token))))
    (ok (is-eq (get parent-id parent-data) u0))
  )
)

(define-read-only (is-leaf (token uint))
  (let ((children (default-to (list) (map-get? token-children token))))
    (ok (is-eq (len children) u0))
  )
)

(define-public (mint (recipient principal) (uri (string-ascii 256)))
  (let ((new-id (+ (var-get token-id-nonce) u1)))
    (try! (nft-mint? nestable-nft new-id recipient))
    (map-set token-uri new-id uri)
    (map-set token-parent new-id {parent-id: u0, parent-contract: none})
    (var-set token-id-nonce new-id)
    (ok new-id)
  )
)

(define-public (nest-mint (parent-id uint) (recipient principal) (uri (string-ascii 256)))
  (let (
    (new-id (+ (var-get token-id-nonce) u1))
    (parent-owner (unwrap! (nft-get-owner? nestable-nft parent-id) ERR-NOT-FOUND))
  )
    (asserts! (is-eq tx-sender parent-owner) ERR-NOT-AUTHORIZED)
    (try! (nft-mint? nestable-nft new-id recipient))
    (map-set token-uri new-id uri)
    (map-set token-parent new-id {parent-id: parent-id, parent-contract: (some tx-sender)})
    (var-set token-id-nonce new-id)
    (try! (add-pending-child parent-id new-id tx-sender))
    (ok new-id)
  )
)

(define-public (transfer (token uint) (sender principal) (recipient principal))
  (let ((owner (unwrap! (nft-get-owner? nestable-nft token) ERR-NOT-FOUND)))
    (asserts! (or (is-eq tx-sender owner) (is-eq tx-sender sender)) ERR-NOT-AUTHORIZED)
    (try! (nft-transfer? nestable-nft token sender recipient))
    (ok true)
  )
)

(define-public (nest-transfer (token uint) (sender principal) (parent-id uint))
  (let ((owner (unwrap! (nft-get-owner? nestable-nft token) ERR-NOT-FOUND)))
    (asserts! (or (is-eq tx-sender owner) (is-eq tx-sender sender)) ERR-NOT-AUTHORIZED)
    (asserts! (not (is-eq token parent-id)) ERR-CIRCULAR-REF)
    (map-set token-parent token {parent-id: parent-id, parent-contract: (some tx-sender)})
    (try! (add-pending-child parent-id token tx-sender))
    (ok true)
  )
)

(define-public (accept-child (parent-id uint) (child-index uint) (child-id uint) (child-contract principal))
  (let (
    (parent-owner (unwrap! (nft-get-owner? nestable-nft parent-id) ERR-NOT-FOUND))
    (pending (unwrap! (map-get? pending-children parent-id) ERR-NOT-PENDING))
    (child-data (unwrap! (element-at pending child-index) ERR-NOT-FOUND))
    (current-children (default-to (list) (map-get? token-children parent-id)))
  )
    (asserts! (is-eq tx-sender parent-owner) ERR-NOT-AUTHORIZED)
    (asserts! (is-eq (get child-id child-data) child-id) ERR-NOT-FOUND)
    (asserts! (is-eq (get child-contract child-data) child-contract) ERR-NOT-FOUND)
    (let (
      (before (if (> child-index u0) 
                (unwrap! (slice? pending u0 child-index) ERR-NOT-FOUND)
                (list)))
      (after (if (< (+ child-index u1) (len pending))
               (unwrap! (slice? pending (+ child-index u1) (len pending)) ERR-NOT-FOUND)
               (list)))
      (new-pending (unwrap! (as-max-len? (concat before after) u128) ERR-NOT-FOUND))
    )
      (map-set pending-children parent-id new-pending)
      (map-set token-children parent-id (unwrap! (as-max-len? (append current-children child-data) u200) ERR-NOT-FOUND))
      (ok true)
    )
  )
)

(define-public (reject-all-children (parent-id uint) (max-rejections uint))
  (let ((parent-owner (unwrap! (nft-get-owner? nestable-nft parent-id) ERR-NOT-FOUND)))
    (asserts! (is-eq tx-sender parent-owner) ERR-NOT-AUTHORIZED)
    (map-delete pending-children parent-id)
    (ok true)
  )
)

(define-public (burn (token uint) (max-recursive-burns uint))
  (let (
    (owner (unwrap! (nft-get-owner? nestable-nft token) ERR-NOT-FOUND))
    (children (default-to (list) (map-get? token-children token)))
  )
    (asserts! (is-eq tx-sender owner) ERR-NOT-AUTHORIZED)
    (asserts! (is-eq (len children) u0) ERR-IS-EQUIPPED)
    (try! (nft-burn? nestable-nft token owner))
    (map-delete token-parent token)
    (map-delete token-uri token)
    (ok u0)
  )
)

(define-private (add-pending-child (parent-id uint) (child-id uint) (child-contract principal))
  (let (
    (current-pending (default-to (list) (map-get? pending-children parent-id)))
    (new-child {child-id: child-id, child-contract: child-contract})
  )
    (asserts! (< (len current-pending) (var-get max-pending-children)) ERR-PENDING-LIMIT)
    (map-set pending-children parent-id (unwrap! (as-max-len? (append current-pending new-child) u128) ERR-PENDING-LIMIT))
    (ok true)
  )
)

Functions (17)

FunctionAccessArgs
get-last-token-idread-only
get-token-uriread-onlytoken: uint
get-ownerread-onlytoken: uint
get-direct-ownerread-onlytoken: uint
get-root-ownerread-onlytoken: uint
get-childrenread-onlytoken: uint
get-pending-childrenread-onlytoken: uint
is-rootread-onlytoken: uint
is-leafread-onlytoken: uint
mintpublicrecipient: principal, uri: (string-ascii 256
nest-mintpublicparent-id: uint, recipient: principal, uri: (string-ascii 256
transferpublictoken: uint, sender: principal, recipient: principal
nest-transferpublictoken: uint, sender: principal, parent-id: uint
accept-childpublicparent-id: uint, child-index: uint, child-id: uint, child-contract: principal
reject-all-childrenpublicparent-id: uint, max-rejections: uint
burnpublictoken: uint, max-recursive-burns: uint
add-pending-childprivateparent-id: uint, child-id: uint, child-contract: principal