Source Code

;; UserProfile Contract
;; This contract manages user profiles, including usernames, bios, ratings, and reputation scores.
;; It provides functions for users to register, update their profiles, and rate each other.

;; --- Constants ---
;; Defines immutable values used throughout the contract for error handling and configuration.

(define-constant CONTRACT_OWNER tx-sender) ;; Sets the contract deployer as the owner.
(define-constant ERR_NOT_AUTHORIZED (err u100)) ;; Error for unauthorized actions.
(define-constant ERR_USER_NOT_FOUND (err u101)) ;; Error when a user profile cannot be found.
(define-constant ERR_INVALID_RATING (err u102)) ;; Error for invalid rating values (must be between 1 and 5).
(define-constant ERR_SELF_RATING (err u103)) ;; Error when a user attempts to rate themselves.
(define-constant ERR_ALREADY_REGISTERED (err u104)) ;; Error if a user is already registered.
(define-constant ERR_INVALID_INPUT (err u105)) ;; Error for invalid input parameters.
(define-constant ERR_DATA_STORE_FAILURE (err u106)) ;; Error for data storage failures.
(define-constant ERR_INVALID_PRINCIPAL (err u107)) ;; Error for invalid principal addresses.
(define-constant ERR_INVALID_USERNAME (err u108)) ;; Error for invalid usernames.
(define-constant ERR_INVALID_BIO (err u109)) ;; Error for invalid bio lengths.
(define-constant ERR_INVALID_EMAIL (err u110)) ;; Error for invalid email formats or lengths.

;; --- Data Maps ---
;; Defines the data structures used to store user profile information.

(define-map Users principal
  {
    username: (string-utf8 64), ;; The user's chosen username.
    bio: (string-utf8 256), ;; A short biography or description.
    email: (string-utf8 64), ;; The user's email address.
    registration-date: uint, ;; The block height of the user's registration.
    total-ratings: uint, ;; The total number of ratings the user has received.
    rating-sum: uint, ;; The sum of all ratings received.
    reputation-score: uint ;; A calculated score based on ratings and other factors.
  }
)

(define-map UserAuthorization principal bool) ;; Stores authorization status for specific users.

;; --- Private Functions ---
;; Helper functions for internal contract use.

;; Checks if a string is within the specified length constraints.
(define-private (is-valid-string (str (string-utf8 256)) (max-len uint))
  (and (>= (len str) u1) (<= (len str) max-len))
)

;; Checks if a principal is a valid user address.
(define-private (is-valid-principal (user principal))
  (not (is-eq user (as-contract tx-sender)))
)

;; Validates the format and length of a username.
(define-private (validate-username (username (string-utf8 64)))
  (if (is-valid-string username u64)
    (ok username)
    (err ERR_INVALID_USERNAME))
)

;; Validates the length of a user's bio.
(define-private (validate-bio (bio (string-utf8 256)))
  (if (is-valid-string bio u256)
    (ok bio)
    (err ERR_INVALID_BIO))
)

;; Validates the format and length of an email address.
(define-private (validate-email (email (string-utf8 64)))
  (if (is-valid-string email u64)
    (ok email)
    (err ERR_INVALID_EMAIL))
)

;; Sets the user data in the Users map.
(define-private (set-user-data (user principal) (data {
    username: (string-utf8 64),
    bio: (string-utf8 256),
    email: (string-utf8 64),
    registration-date: uint,
    total-ratings: uint,
    rating-sum: uint,
    reputation-score: uint
  }))
  (if (map-set Users user data)
    (ok true)
    (err ERR_DATA_STORE_FAILURE))
)

;; --- Read-Only Functions ---
;; Functions for retrieving data from the contract without making state changes.

;; Retrieves a user's profile.
;; @param user: The principal of the user to retrieve.
;; @returns (ok {<user-data>}): The user's profile data or an error if not found.
(define-read-only (get-user-profile (user principal))
  (match (map-get? Users user)
    user-data (ok user-data)
    (err ERR_USER_NOT_FOUND)))

;; Retrieves a user's average rating.
;; @param user: The principal of the user.
;; @returns (ok uint): The user's average rating or 0 if no ratings.
(define-read-only (get-user-rating (user principal))
  (match (map-get? Users user)
    user-data (let (
      (total-ratings (get total-ratings user-data))
      (rating-sum (get rating-sum user-data))
    )
    (if (> total-ratings u0)
      (ok (/ rating-sum total-ratings))
      (ok u0)))
    (err ERR_USER_NOT_FOUND)
  )
)

;; Checks if a user is authorized.
;; @param user: The principal to check.
;; @returns (ok bool): True if the user is authorized.
(define-read-only (is-user-authorized (user principal))
  (ok (default-to false (map-get? UserAuthorization user))))


;; --- Public Functions ---
;; Functions that can be called by any user.

;; Registers a new user.
;; @param username: The desired username.
;; @param bio: A short bio.
;; @param email: The user's email address.
;; @returns (ok bool): True if registration is successful.
(define-public (register-user (username (string-utf8 64)) (bio (string-utf8 256)) (email (string-utf8 64)))
  (let (
    (existing-user (map-get? Users tx-sender))
  )
  (asserts! (is-none existing-user) (err ERR_ALREADY_REGISTERED))
  (asserts! (and 
              (> (len username) u0)
              (>= (len username) u1)
              (<= (len username) u64)
              (> (len bio) u0)
              (>= (len bio) u1)
              (<= (len bio) u256)
              (> (len email) u0)
              (>= (len email) u1)
              (<= (len email) u64))
            (err ERR_INVALID_INPUT))
  (let (
    (validated-username (try! (validate-username username)))
    (validated-bio (try! (validate-bio bio)))
    (validated-email (try! (validate-email email)))
  )
    (match (set-user-data tx-sender
      {
        username: validated-username,
        bio: validated-bio,
        email: validated-email,
        registration-date: stacks-block-height,
        total-ratings: u0,
        rating-sum: u0,
        reputation-score: u0
      })
      success (ok true)
      error (err ERR_DATA_STORE_FAILURE))
  ))
)

;; Updates a user's profile.
;; @param bio: The new bio.
;; @param email: The new email address.
;; @returns (ok bool): True if the update is successful.
(define-public (update-profile (bio (string-utf8 256)) (email (string-utf8 64)))
  (begin
    (asserts! (and 
                (> (len bio) u0)
                (>= (len bio) u1)
                (<= (len bio) u256)
                (> (len email) u0)
                (>= (len email) u1)
                (<= (len email) u64))
              (err ERR_INVALID_INPUT))
    (let (
      (validated-bio (try! (validate-bio bio)))
      (validated-email (try! (validate-email email)))
    )
      (match (map-get? Users tx-sender)
        user-data 
          (match (set-user-data tx-sender
            (merge user-data
              {
                bio: validated-bio,
                email: validated-email
              }
            ))
            success (ok true)
            error (err ERR_DATA_STORE_FAILURE))
        (err ERR_USER_NOT_FOUND)
      )
    )
  )
)

;; Rates a user.
;; @param user: The principal of the user to rate.
;; @param rating: The rating value (1-5).
;; @returns (ok bool): True if the rating is successful.
(define-public (rate-user (user principal) (rating uint))
  (begin
    (asserts! (is-valid-principal user) (err ERR_INVALID_PRINCIPAL))
    (asserts! (not (is-eq tx-sender user)) (err ERR_SELF_RATING))
    (asserts! (and (>= rating u1) (<= rating u5)) (err ERR_INVALID_RATING))
    (match (map-get? Users user)
      user-data
        (match (set-user-data user
          (merge user-data
            {
              total-ratings: (+ (get total-ratings user-data) u1),
              rating-sum: (+ (get rating-sum user-data) rating)
            }
          ))
          success (ok true)
          error (err ERR_DATA_STORE_FAILURE))
      (err ERR_USER_NOT_FOUND)
    )
  )
)

;; Calculates a user's reputation score.
;; @param user: The principal of the user.
;; @returns (ok bool): True if the calculation is successful.
(define-public (calculate-reputation (user principal))
  (begin
    (asserts! (is-valid-principal user) (err ERR_INVALID_PRINCIPAL))
    (match (map-get? Users user)
      user-data
        (let (
          (total-ratings (get total-ratings user-data))
          (rating-sum (get rating-sum user-data))
          (avg-rating (if (> total-ratings u0) (/ rating-sum total-ratings) u0))
          (new-reputation (+ (* avg-rating u20) (* total-ratings u2)))
        )
        (match (set-user-data user
          (merge user-data
            {
              reputation-score: new-reputation
            }
          ))
          success (ok true)
          error (err ERR_DATA_STORE_FAILURE)))
      (err ERR_USER_NOT_FOUND)
    )
  )
)

;; Authorizes a user for specific actions.
;; @param user: The principal to authorize.
;; @returns (ok bool): True if authorization is successful.
(define-public (authorize-user (user principal))
  (begin
    (asserts! (is-eq tx-sender CONTRACT_OWNER) (err ERR_NOT_AUTHORIZED))
    (asserts! (is-valid-principal user) (err ERR_INVALID_PRINCIPAL))
    (ok (map-set UserAuthorization user true))
  )
)

;; Revokes a user's authorization.
;; @param user: The principal to revoke authorization from.
;; @returns (ok bool): True if revocation is successful.
(define-public (revoke-authorization (user principal))
  (begin
    (asserts! (is-eq tx-sender CONTRACT_OWNER) (err ERR_NOT_AUTHORIZED))
    (asserts! (is-valid-principal user) (err ERR_INVALID_PRINCIPAL))
    (ok (map-set UserAuthorization user false))
  )
)

;; --- Contract Initialization ---
;; Initializes the contract upon deployment.
(begin
  (map-set UserAuthorization CONTRACT_OWNER true)
  (print "UserProfile contract initialized")
  (ok true)
)

Functions (14)

FunctionAccessArgs
is-valid-stringprivatestr: (string-utf8 256
is-valid-principalprivateuser: principal
validate-usernameprivateusername: (string-utf8 64
validate-bioprivatebio: (string-utf8 256
validate-emailprivateemail: (string-utf8 64
get-user-profileread-onlyuser: principal
get-user-ratingread-onlyuser: principal
is-user-authorizedread-onlyuser: principal
register-userpublicusername: (string-utf8 64
update-profilepublicbio: (string-utf8 256
rate-userpublicuser: principal, rating: uint
calculate-reputationpublicuser: principal
authorize-userpublicuser: principal
revoke-authorizationpublicuser: principal