Source Code





;; StacksTokenize -Real World Asset Token Contract
;; Implements advanced features for real-world asset tokenization

;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-found (err u101))
(define-constant err-already-listed (err u102))
(define-constant err-invalid-amount (err u103))
(define-constant err-not-authorized (err u104))
(define-constant err-kyc-required (err u105))
(define-constant err-vote-exists (err u106))
(define-constant err-vote-ended (err u107))
(define-constant err-price-expired (err u108))

(define-constant MAX-ASSET-VALUE u1000000000000) ;; 1 trillion
(define-constant MIN-ASSET-VALUE u1000) ;; 1 thousand
(define-constant MAX-DURATION u144) ;; ~1 day in blocks
(define-constant MIN-DURATION u12) ;; ~1 hour in blocks
(define-constant MAX-KYC-LEVEL u5)
(define-constant MAX-EXPIRY u52560) ;; ~1 year in blocks

;; Add new error codes
(define-constant err-invalid-uri (err u110))
(define-constant err-invalid-value (err u111))
(define-constant err-invalid-duration (err u112))
(define-constant err-invalid-kyc-level (err u113))
(define-constant err-invalid-expiry (err u114))
(define-constant err-invalid-votes (err u115))
(define-constant err-invalid-address (err u116))
(define-constant err-invalid-title (err u117))

;; Data Maps
(define-map assets 
    { asset-id: uint }
    {
        owner: principal,
        metadata-uri: (string-ascii 256),
        asset-value: uint,
        is-locked: bool,
        creation-height: uint,
        last-price-update: uint,
        total-dividends: uint
    }
)

(define-map token-balances
    { owner: principal, asset-id: uint }
    { balance: uint }
)

(define-map kyc-status
    { address: principal }
    { 
        is-approved: bool,
        level: uint,
        expiry: uint 
    }
)

(define-map proposals
    { proposal-id: uint }
    {
        title: (string-ascii 256),
        asset-id: uint,
        start-height: uint,
        end-height: uint,
        executed: bool,
        votes-for: uint,
        votes-against: uint,
        minimum-votes: uint
    }
)

(define-map votes
    { proposal-id: uint, voter: principal }
    { vote-amount: uint }
)

(define-map dividend-claims
    { asset-id: uint, claimer: principal }
    { last-claimed-amount: uint }
)

(define-map price-feeds
    { asset-id: uint }
    {
        price: uint,
        decimals: uint,
        last-updated: uint,
        oracle: principal
    }
)

;; SFTs per asset
(define-constant tokens-per-asset u100000)

;; Helper functions for input validation
(define-private (validate-asset-value (value uint))
    (and 
        (>= value MIN-ASSET-VALUE)
        (<= value MAX-ASSET-VALUE)
    )
)

(define-private (validate-duration (duration uint))
    (and 
        (>= duration MIN-DURATION)
        (<= duration MAX-DURATION)
    )
)

(define-private (validate-kyc-level (level uint))
    (<= level MAX-KYC-LEVEL)
)

(define-private (validate-expiry (expiry uint))
    (and 
        (> expiry stacks-block-height)
        (<= (- expiry stacks-block-height) MAX-EXPIRY)
    )
)



(define-private (validate-minimum-votes (vote-count uint))
    (and 
        (> vote-count u0)
        (<= vote-count tokens-per-asset)
    )
)

(define-private (validate-metadata-uri (uri (string-ascii 256)))
    (and 
        (> (len uri) u0)
        (<= (len uri) u256)
    )
)

(define-public (register-asset 
    (metadata-uri (string-ascii 256)) 
    (asset-value uint))
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (asserts! (validate-metadata-uri metadata-uri) err-invalid-uri)
        (asserts! (validate-asset-value asset-value) err-invalid-value)

        (let 
            ((asset-id (get-next-asset-id)))
            (map-set assets
                { asset-id: asset-id }
                {
                    owner: contract-owner,
                    metadata-uri: metadata-uri,
                    asset-value: asset-value,
                    is-locked: false,
                    creation-height: stacks-block-height,
                    last-price-update: stacks-block-height,
                    total-dividends: u0
                }
            )
            (map-set token-balances
                { owner: contract-owner, asset-id: asset-id }
                { balance: tokens-per-asset }
            )
            (ok asset-id)
        )
    )
)




;; Read Functions
(define-read-only (get-asset-info (asset-id uint))
    (map-get? assets { asset-id: asset-id })
)

(define-read-only (get-balance (owner principal) (asset-id uint))
    (default-to u0
        (get balance
            (map-get? token-balances
                { owner: owner, asset-id: asset-id }
            )
        )
    )
)

(define-read-only (get-proposal (proposal-id uint))
    (map-get? proposals { proposal-id: proposal-id })
)

(define-read-only (get-vote (proposal-id uint) (voter principal))
    (map-get? votes { proposal-id: proposal-id, voter: voter })
)

(define-read-only (get-price-feed (asset-id uint))
    (map-get? price-feeds { asset-id: asset-id })
)

(define-read-only (get-last-claim (asset-id uint) (claimer principal))
    (default-to u0
        (get last-claimed-amount
            (map-get? dividend-claims
                { asset-id: asset-id, claimer: claimer }
            )
        )
    )
)

;; Private Functions
(define-private (get-next-asset-id)
    (default-to u1
        (get-last-asset-id)
    )
)

(define-private (get-next-proposal-id)
    (default-to u1
        (get-last-proposal-id)
    )
)

(define-private (get-last-asset-id)
    none
)

(define-private (get-last-proposal-id)
    none
)


(define-public (claim-dividends (asset-id uint))
    (let
        (
            (asset (unwrap! (get-asset-info asset-id) err-not-found))
            (balance (get-balance tx-sender asset-id))
            (last-claim (get-last-claim asset-id tx-sender))
            (total-dividends (get total-dividends asset))
            (claimable-amount (/ (* balance (- total-dividends last-claim)) tokens-per-asset))
        )
        (asserts! (> claimable-amount u0) err-invalid-amount)
        (ok (map-set dividend-claims
            { asset-id: asset-id, claimer: tx-sender }
            { last-claimed-amount: total-dividends }
        ))
    )
)

(define-public (create-proposal 
    (asset-id uint)
    (title (string-ascii 256))
    (duration uint)
    (minimum-votes uint))
    (begin
        (asserts! (validate-duration duration) err-invalid-duration)
        (asserts! (validate-minimum-votes minimum-votes) err-invalid-votes)
        (asserts! (validate-metadata-uri title) err-invalid-title)
        (asserts! (>= (get-balance tx-sender asset-id) (/ tokens-per-asset u10)) err-not-authorized)

        (let
            ((proposal-id (get-next-proposal-id)))
            (ok (map-set proposals
                { proposal-id: proposal-id }
                {
                    title: title,
                    asset-id: asset-id,
                    start-height: stacks-block-height,
                    end-height: (+ stacks-block-height duration),
                    executed: false,
                    votes-for: u0,
                    votes-against: u0,
                    minimum-votes: minimum-votes
                }
            ))
        )
    )
)

(define-public (vote 
    (proposal-id uint)
    (vote-for bool)
    (amount uint))
    (let
        (
            (proposal (unwrap! (get-proposal proposal-id) err-not-found))
            (asset-id (get asset-id proposal))
            (balance (get-balance tx-sender asset-id))
        )
        (begin
            (asserts! (>= balance amount) err-invalid-amount)
            (asserts! (< stacks-block-height (get end-height proposal)) err-vote-ended)
            (asserts! (is-none (get-vote proposal-id tx-sender)) err-vote-exists)

            (map-set votes
                { proposal-id: proposal-id, voter: tx-sender }
                { vote-amount: amount }
            )
            (ok (map-set proposals
                { proposal-id: proposal-id }
                (merge proposal
                    {
                        votes-for: (if vote-for
                            (+ (get votes-for proposal) amount)
                            (get votes-for proposal)
                        ),
                        votes-against: (if vote-for
                            (get votes-against proposal)
                            (+ (get votes-against proposal) amount)
                        )
                    }
                ))
            )
        )
    )
)

Functions (20)

FunctionAccessArgs
validate-asset-valueprivatevalue: uint
validate-durationprivateduration: uint
validate-kyc-levelprivatelevel: uint
validate-expiryprivateexpiry: uint
validate-minimum-votesprivatevote-count: uint
validate-metadata-uriprivateuri: (string-ascii 256
register-assetpublicmetadata-uri: (string-ascii 256
get-asset-inforead-onlyasset-id: uint
get-balanceread-onlyowner: principal, asset-id: uint
get-proposalread-onlyproposal-id: uint
get-voteread-onlyproposal-id: uint, voter: principal
get-price-feedread-onlyasset-id: uint
get-last-claimread-onlyasset-id: uint, claimer: principal
get-next-asset-idprivate
get-next-proposal-idprivate
get-last-asset-idprivate
get-last-proposal-idprivate
claim-dividendspublicasset-id: uint
create-proposalpublicasset-id: uint, title: (string-ascii 256
votepublicproposal-id: uint, vote-for: bool, amount: uint