Source Code

;; title: gated.so 
;; version: 0.001
;; summary: Ability to create pages 
;; description: Smart contract to create and manage pages on gated.so

;;Define Constant
    ;; Error codes
    (define-constant ERR_PAGE_EXISTS (err u100))
    (define-constant ERR_INVALID_PAGE (err u101))
    (define-constant ERR_TITLE_INVALID_LENGTH (err u102))
    (define-constant ERR_DESC_INVALID_LENGTH (err u103))
    (define-constant ERR_UNAUTHORIZED (err u104))
    (define-constant ERR_NOT_TOKEN_OWNER (err u105))
    (define-constant ERR_INVALID_TOKEN (err u106))


    ;; Min and max length constraints
    (define-constant MIN_TITLE_LENGTH u1)
    (define-constant MAX_TITLE_LENGTH u64)
    (define-constant MIN_DESC_LENGTH u1)
    (define-constant MAX_DESC_LENGTH u256)

    ;; hardcoded gated address
    (define-constant gated 'SP2BRB6P0BK6T35DHTGXCV6MZ5TGRN5E0RKZ1T8B5)
    
    ;; define non-fungible-token
    (define-non-fungible-token gated-page uint)

    ;; Data variables
        ;; page-id
        (define-data-var token-id uint u0)
        ;; gated-fee set to 1 STX
        (define-data-var gated-fee uint u1000000)
        ;; set contract-owner
        (define-data-var contract-owner principal tx-sender)

;; maps
    (define-map pages 
        {token-id: uint} 
        {   
        owner: principal,  
        active: bool, 
        title: (string-ascii 64), 
        description: (string-ascii 256),
        metadata-uri:  (optional (string-ascii 256))}
    )

    ;; NFT transfer function
(define-public (transfer (id uint) (sender principal) (recipient principal))
    (begin
        (asserts! (is-eq tx-sender sender) ERR_NOT_TOKEN_OWNER)
        ;; Add NFT transfer
        (try! (nft-transfer? gated-page id sender recipient))
        ;; Update pages map
        (match (map-get? pages {token-id: id})
            token-data
            (begin
                (asserts! (is-eq (get owner token-data) sender) ERR_NOT_TOKEN_OWNER)
                (map-set pages
                    {token-id: id}
                    (merge token-data {owner: recipient})
                )
                (ok true)
            )
            ERR_INVALID_TOKEN
        )
    )
)

    ;; Private functions
    (define-private (validate-string-length-title (str (string-ascii 64)) (min uint) (max uint))
    (let
        (
            (str-length (len str))  ;; Changed from 'len' to 'str-length'
        )
        (and (>= str-length min) (<= str-length max))
    )
)

    (define-private (validate-string-length-description (str (string-ascii 256)) (min uint) (max uint))
        (let
        (
            (str-length (len str))  ;; Changed from 'len' to 'str-length'
        )
        (and (>= str-length min) (<= str-length max))
    )
)

;; Add a new page
    (define-public (add-page (title (string-ascii 64)) (description (string-ascii 256)) (metadata-uri (string-ascii 256))) 
        (let
            (
                (new-id (+ (var-get token-id) u1))
            )
            (begin
                ;; Input validation
                (asserts! (validate-string-length-title title MIN_TITLE_LENGTH MAX_TITLE_LENGTH) ERR_TITLE_INVALID_LENGTH)
                (asserts! (validate-string-length-description description MIN_DESC_LENGTH MAX_DESC_LENGTH) ERR_DESC_INVALID_LENGTH)

                ;; Check if page already exists
                (asserts! (is-none (map-get? pages { token-id: new-id })) ERR_PAGE_EXISTS)

                ;; Process payment for page
                (try! (stx-transfer? (var-get gated-fee) tx-sender gated))

                ;; mint NFT 
                (try! (nft-mint? gated-page new-id tx-sender))  

                ;; Add the page with metadata
                (map-set pages 
                    { token-id: new-id }
                    {
                        owner: tx-sender,
                        active: true,
                        title: title,
                        description: description,
                        metadata-uri:  (some metadata-uri)
                    }
                )
                
                ;; Update the page counter
                (var-set token-id new-id)

                ;; Emit event
                (print {event: "page-created", page-id: new-id, owner: tx-sender})
                
                ;; Return the new page ID
                (ok new-id)
            )
        )
    )

(define-public (update-page (id uint) (title (string-ascii 64)) (description (string-ascii 256)) (metadata-uri (string-ascii 256)) (active bool))
    (begin
        ;; Input validation
        (asserts! (validate-string-length-title title MIN_TITLE_LENGTH MAX_TITLE_LENGTH) ERR_TITLE_INVALID_LENGTH)
        (asserts! (validate-string-length-description description MIN_DESC_LENGTH MAX_DESC_LENGTH) ERR_DESC_INVALID_LENGTH)

        ;; Check if page exists and get it
        (match (map-get? pages {token-id: id})
            existing-page-data  ;; More descriptive name
            (begin
                ;; Verify the stored owner matches the sender
                (asserts! (is-eq (get owner existing-page-data) tx-sender) ERR_UNAUTHORIZED)

                ;; Update the page
                (some (map-set pages
                    {token-id: id}
                    {
                        owner: tx-sender,
                        active: active,
                        title: title,
                        description: description,
                        metadata-uri:  (some metadata-uri)
                    }
                ))

                ;; Emit event
                (print {event: "page-updated", page-id: id, owner: tx-sender})

                ;; Return success
                (ok true)
            )
            ERR_INVALID_PAGE
        )
    )
)

;; Contract owner functions
(define-public (update-fee (new-fee uint))
    (begin
        (asserts! (is-eq tx-sender (var-get contract-owner)) ERR_UNAUTHORIZED)
        (var-set gated-fee new-fee)
        (print {event: "fee-updated", new-fee: new-fee})
        (ok true)
    )
)

;; Read page details
    (define-read-only (get-page (page uint))
        (map-get? pages { token-id: page })
    )


;; Add these required read-only functions
(define-read-only (get-last-token-id)
    (ok (var-get token-id))
)

(define-read-only (get-token-uri (id uint))
    (match (map-get? pages {token-id: id})
        token-data (ok (get metadata-uri token-data))
        ERR_INVALID_TOKEN
    )
)

(define-read-only (get-owner (id uint))
    (ok (nft-get-owner? gated-page id))
)

Functions (10)

FunctionAccessArgs
transferpublicid: uint, sender: principal, recipient: principal
validate-string-length-titleprivatestr: (string-ascii 64
validate-string-length-descriptionprivatestr: (string-ascii 256
add-pagepublictitle: (string-ascii 64
update-pagepublicid: uint, title: (string-ascii 64
update-feepublicnew-fee: uint
get-pageread-onlypage: uint
get-last-token-idread-only
get-token-uriread-onlyid: uint
get-ownerread-onlyid: uint