Source Code

;; Constants
(define-constant ADMIN-ACCOUNT tx-sender)


;;
;; Errors
(define-constant ERR-UNAUTHORIZED (err u100))
(define-constant ERR-NAME-UNAVAILABLE (err u101))
(define-constant ERR-NAME-NOT-FOUND (err u102))
(define-constant ERR-INVALID-NAME (err u103))
(define-constant ERR-TRANSACTION-FAILED (err u104))
(define-constant ERR-PAYMENT-REQUIRED (err u105))

;; token definitions


;; DATA Maps :::
;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;
(define-map name-registry 
  { name: (string-ascii 255) }
  {
    holder: principal,
    validity: uint,
    listing-amount: uint
  }
)


(define-map name-marketplace
  { name: (string-ascii 255) }
  { 
    buyer: principal,
    purchase-amount: uint 
  }
)

;; data vars
;;
;; Variables
(define-data-var base-fee uint u10000000) ;; 0.1 STX default fee
(define-data-var min-validity-period uint u31536000) ;; 1 year in seconds
(define-data-var max-validity-period uint u157680000) ;; 5 years in seconds

;; data maps
;;
;; Helper function to get current block height as a pseudo-timestamp
(define-read-only (current-time)
  (default-to u0 (get-stacks-block-info? time stacks-block-height))
)

;; public functions
;;
;; Utility Functions
(define-read-only (is-lowercase-letter (char (string-ascii 1)))
  (or 
    (is-eq char "a") (is-eq char "b") (is-eq char "c") (is-eq char "d")
    (is-eq char "e") (is-eq char "f") (is-eq char "g") (is-eq char "h")
    (is-eq char "i") (is-eq char "j") (is-eq char "k") (is-eq char "l")
    (is-eq char "m") (is-eq char "n") (is-eq char "o") (is-eq char "p")
    (is-eq char "q") (is-eq char "r") (is-eq char "s") (is-eq char "t")
    (is-eq char "u") (is-eq char "v") (is-eq char "w") (is-eq char "x")
    (is-eq char "y") (is-eq char "z")
  )
)

;; read only functions
;;
(define-read-only (is-number (char (string-ascii 1)))
  (or
    (is-eq char "0") (is-eq char "1") (is-eq char "2") (is-eq char "3")
    (is-eq char "4") (is-eq char "5") (is-eq char "6") (is-eq char "7")
    (is-eq char "8") (is-eq char "9")
  )
)

;; private functions
;;
(define-read-only (is-valid-character (char (string-ascii 1)))
  (or
    (is-lowercase-letter char)
    (is-number char)
    (is-eq char "-")
  )
)

;; Helper function to check a specific character in the name
(define-private (check-name-char (name (string-ascii 255)) (index uint))
  (if (>= index (len name))
    true
    (is-valid-character (unwrap-panic (element-at name index)))
  )
)

;; Check groups of characters to work around Clarity's recursion limits
(define-private (validate-chars-group-1 (name (string-ascii 255)))
  (and
    (check-name-char name u4)
    (check-name-char name u5)
    (check-name-char name u6)
    (check-name-char name u7)
    (check-name-char name u8)
    (check-name-char name u9)
    (check-name-char name u10)
    (check-name-char name u11)
    (check-name-char name u12)
    (check-name-char name u13)
    (check-name-char name u14)
    (check-name-char name u15)
  )
)

(define-private (validate-chars-group-2 (name (string-ascii 255)))
  (and
    (check-name-char name u16)
    (check-name-char name u17)
    (check-name-char name u18)
    (check-name-char name u19)
    (check-name-char name u20)
    (check-name-char name u21)
    (check-name-char name u22)
    (check-name-char name u23)
    (check-name-char name u24)
    (check-name-char name u25)
    (check-name-char name u26)
    (check-name-char name u27)
  )
)

(define-private (validate-chars-group-3 (name (string-ascii 255)))
  (and
    (check-name-char name u28)
    (check-name-char name u29)
    (check-name-char name u30)
    (check-name-char name u31)
    (check-name-char name u32)
    (check-name-char name u33)
    (check-name-char name u34)
    (check-name-char name u35)
    (check-name-char name u36)
    (check-name-char name u37)
    (check-name-char name u38)
    (check-name-char name u39)
  )
)

(define-private (validate-chars-group-4 (name (string-ascii 255)))
  (and
    (check-name-char name u40)
    (check-name-char name u41)
    (check-name-char name u42)
    (check-name-char name u43)
    (check-name-char name u44)
    (check-name-char name u45)
    (check-name-char name u46)
    (check-name-char name u47)
    (check-name-char name u48)
    (check-name-char name u49)
    (check-name-char name u50)
    (check-name-char name u51)
  )
)

(define-private (validate-chars-group-5 (name (string-ascii 255)))
  (and
    (check-name-char name u52)
    (check-name-char name u53)
    (check-name-char name u54)
    (check-name-char name u55)
    (check-name-char name u56)
    (check-name-char name u57)
    (check-name-char name u58)
    (check-name-char name u59)
    (check-name-char name u60)
    (check-name-char name u61)
    (check-name-char name u62)
    (check-name-char name u63)
  )
)




(define-read-only (has-valid-name-chars (name (string-ascii 255)))
  (let
    ((name-len (len name)))
    (and
      ;; Check first 4 characters
      (is-valid-character (unwrap-panic (element-at name u0)))
      (is-valid-character (unwrap-panic (element-at name u1)))
      (is-valid-character (unwrap-panic (element-at name u2)))
      (is-valid-character (unwrap-panic (element-at name u3)))
      ;; Check remaining characters in groups
      (or
        (<= name-len u4)
        (and
          (>= name-len u5)
          (validate-chars-group-1 name)
          (or
            (<= name-len u16)
            (and
              (>= name-len u17)
              (validate-chars-group-2 name)
              (or
                (<= name-len u28)
                (and
                  (>= name-len u29)
                  (validate-chars-group-3 name)
                  (or
                    (<= name-len u40)
                    (and
                      (>= name-len u41)
                      (validate-chars-group-4 name)
                      (or
                        (<= name-len u52)
                        (and
                          (>= name-len u53)
                          (validate-chars-group-5 name)
                        )
                      )
                    )
                  )
                )
              )
            )
          )
        )
      )
    )
  )
)

(define-read-only (is-valid-name (name (string-ascii 255)))
  (let 
    (
      (name-len (len name))
      (first-char (unwrap-panic (element-at name u0)))
      (last-char (unwrap-panic (element-at name (- name-len u1))))
    )
    (and 
      (>= name-len u3)  ;; Minimum 3 characters
      (<= name-len u64) ;; Maximum 64 characters
      (not (is-eq first-char "-"))  ;; Cannot start with hyphen
      (not (is-eq last-char "-"))   ;; Cannot end with hyphen
      (has-valid-name-chars name)
    )
  )
)

(define-read-only (get-name-info (name (string-ascii 255)))
  (begin 
    (asserts! (is-valid-name name) ERR-INVALID-NAME)
    (ok (unwrap! (map-get? name-registry { name: name }) ERR-NAME-NOT-FOUND))
  )
)

(define-public (register-name 
  (name (string-ascii 255)) 
  (period uint)
)
  (begin
    ;; Validate name format first
    (asserts! (is-valid-name name) ERR-INVALID-NAME)

    ;; Check name availability 
    (asserts! (is-none (map-get? name-registry { name: name })) ERR-NAME-UNAVAILABLE)

    ;; Validate registration period
    (asserts! 
      (and 
        (>= period (var-get min-validity-period))
        (<= period (var-get max-validity-period))
      ) 
      ERR-INVALID-NAME
    )

    ;; Calculate total cost
    (let 
      (
        (registration-cost 
          (* (var-get base-fee) 
             (/ period u31536000) ;; Pro-rate by year
          )
        )
        (current-timestamp (current-time))
      )

      ;; Transfer registration fee
      (try! (stx-transfer? registration-cost tx-sender ADMIN-ACCOUNT))

      ;; Register name
      (ok (map-set name-registry 
        { name: name }
        {
          holder: tx-sender,
          validity: (+ current-timestamp period),
          listing-amount: u0
        }
      ))
    )
  )
)

(define-public (extend-validity 
  (name (string-ascii 255))
  (additional-period uint)
)
  (begin
    ;; Validate name format first
    (asserts! (is-valid-name name) ERR-INVALID-NAME)

    ;; Get and verify name info
    (let
      (
        (name-info (try! (get-name-info name)))
        (current-holder (get holder name-info))
        (current-validity (get validity name-info))
        (extension-cost 
          (* (var-get base-fee) 
             (/ additional-period u31536000)
          )
        )
        (current-timestamp (current-time))
      )

      ;; Validate caller is holder
      (asserts! (is-eq current-holder tx-sender) ERR-UNAUTHORIZED)

      ;; Transfer extension fee
      (try! (stx-transfer? extension-cost tx-sender ADMIN-ACCOUNT))

      ;; Update name validity
      (ok (map-set name-registry 
        { name: name }
        (merge name-info {
          validity: (+ current-timestamp additional-period)
        })
      ))
    )
  )
)

Functions (15)

FunctionAccessArgs
current-timeread-only
is-lowercase-letterread-onlychar: (string-ascii 1
is-numberread-onlychar: (string-ascii 1
is-valid-characterread-onlychar: (string-ascii 1
check-name-charprivatename: (string-ascii 255
validate-chars-group-1privatename: (string-ascii 255
validate-chars-group-2privatename: (string-ascii 255
validate-chars-group-3privatename: (string-ascii 255
validate-chars-group-4privatename: (string-ascii 255
validate-chars-group-5privatename: (string-ascii 255
has-valid-name-charsread-onlyname: (string-ascii 255
is-valid-nameread-onlyname: (string-ascii 255
get-name-inforead-onlyname: (string-ascii 255
register-namepublicname: (string-ascii 255
extend-validitypublicname: (string-ascii 255