Source Code

;; title: STXSynAssets
;; version:
;; summary:
;; description:

;; Error code
(define-constant ERROR-NOT-AUTHORIZED (err u100))
(define-constant ERROR-TOKEN-BALANCE-TOO-LOW (err u101))
(define-constant ERROR-INVALID-TOKEN-QUANTITY (err u102))
(define-constant ERROR-ORACLE-DATA-STALE (err u103))
(define-constant ERROR-COLLATERAL-DEPOSIT-INSUFFICIENT (err u104))
(define-constant ERROR-COLLATERAL-BELOW-THRESHOLD (err u105))
(define-constant ERROR-PRICE-OUT-OF-BOUNDS (err u106))
(define-constant ERROR-CALCULATION-OVERFLOW (err u107))
(define-constant ERROR-INVALID-TRANSFER-RECIPIENT (err u108))
(define-constant ERROR-AMOUNT-MUST-BE-POSITIVE (err u109))
(define-constant ERROR-VAULT-NOT-FOUND (err u110))

;; System constants
(define-constant SYSTEM-ADMIN tx-sender)
(define-constant PRICE-VALIDITY-PERIOD-BLOCKS u900) ;; 15 minutes in blocks
(define-constant COLLATERAL-SAFETY-RATIO u150) ;; 150%
(define-constant COLLATERAL-LIQUIDATION-RATIO u120) ;; 120%
(define-constant MIN-TOKEN-ISSUANCE-AMOUNT u100000000) ;; 1.00 tokens (8 decimals)
(define-constant MAX-ALLOWED-PRICE u1000000000000) ;; Price ceiling for safety
(define-constant UINT_MAX u340282366920938463463374607431768211455) ;; 2^128 - 1

;; Contract principal - UPDATE THIS after deployment with actual contract address
;; Example: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.stx-collaatx
(define-constant CONTRACT-OWNER tx-sender)

;; System state variables
(define-data-var oracle-update-block-height uint u0)
(define-data-var current-market-price uint u0)
(define-data-var global-token-supply uint u0)

;; Data storage structures
(define-map user-token-holdings
  principal
  uint
)
(define-map collateralized-positions
  principal
  {
    staked-collateral: uint,
    issued-synthetic-tokens: uint,
    position-open-price: uint,
  }
)

;;;; Private Function
;; Internal helper functions
(define-private (process-token-transfer
    (sender principal)
    (receiver principal)
    (transfer-amount uint)
  )
  (let ((sender-current-balance (get-user-token-balance sender)))
    ;; Safety checks
    (asserts! (> transfer-amount u0) ERROR-AMOUNT-MUST-BE-POSITIVE)
    (asserts! (not (is-eq sender receiver)) ERROR-INVALID-TRANSFER-RECIPIENT)
    (asserts! (>= sender-current-balance transfer-amount)
      ERROR-TOKEN-BALANCE-TOO-LOW
    )
    (asserts! (is-some (map-get? user-token-holdings sender))
      ERROR-NOT-AUTHORIZED
    )

    (match (secure-add (get-user-token-balance receiver) transfer-amount)
      receiver-new-balance (match (secure-subtract sender-current-balance transfer-amount)
        sender-new-balance (begin
          (map-set user-token-holdings sender sender-new-balance)
          (map-set user-token-holdings receiver receiver-new-balance)
          (ok true)
        )
        error ERROR-CALCULATION-OVERFLOW
      )
      error ERROR-CALCULATION-OVERFLOW
    )
  )
)

;; Protected arithmetic operations
(define-private (secure-multiply
    (value-a uint)
    (value-b uint)
  )
  (let ((product-result (* value-a value-b)))
    (asserts! (or (is-eq value-a u0) (is-eq (/ product-result value-a) value-b))
      ERROR-CALCULATION-OVERFLOW
    )
    (ok product-result)
  )
)

(define-private (secure-add
    (value-a uint)
    (value-b uint)
  )
  (let ((sum-result (+ value-a value-b)))
    (asserts! (>= sum-result value-a) ERROR-CALCULATION-OVERFLOW)
    (ok sum-result)
  )
)

(define-private (secure-subtract
    (value-a uint)
    (value-b uint)
  )
  (begin
    (asserts! (>= value-a value-b) ERROR-CALCULATION-OVERFLOW)
    (ok (- value-a value-b))
  )
)

;; Query functions
(define-read-only (get-user-token-balance (account-holder principal))
  (default-to u0 (map-get? user-token-holdings account-holder))
)

(define-read-only (get-circulating-supply)
  (var-get global-token-supply)
)

(define-read-only (get-market-price)
  (var-get current-market-price)
)

(define-read-only (get-position-details (position-owner principal))
  (map-get? collateralized-positions position-owner)
)

(define-read-only (calculate-position-health-ratio (position-owner principal))
  (let (
      (position-data (unwrap! (get-position-details position-owner) (err u0)))
      (latest-price (var-get current-market-price))
    )
    (if (> (get issued-synthetic-tokens position-data) u0)
      (match (secure-multiply (get staked-collateral position-data) u100)
        collateral-value-base (match (secure-multiply collateral-value-base u100)
          collateral-value-adjusted (match (secure-multiply (get issued-synthetic-tokens position-data)
            latest-price
          )
            synthetic-value (ok (/ collateral-value-adjusted synthetic-value))
            error ERROR-CALCULATION-OVERFLOW
          )
          error ERROR-CALCULATION-OVERFLOW
        )
        error ERROR-CALCULATION-OVERFLOW
      )
      (err u0)
    )
  )
)

;;; PUBLIC FUNCTIONS ;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;; 

;; Helper function for contract to send STX (Clarity 4 compatible)
(define-private (contract-send-stx
    (amount uint)
    (recipient principal)
  )
  (stx-transfer? amount tx-sender recipient)
)

;; External functions
(define-public (set-price-feed-value (new-price uint))
  (begin
    (asserts! (is-eq tx-sender SYSTEM-ADMIN) ERROR-NOT-AUTHORIZED)
    (asserts! (> new-price u0) ERROR-PRICE-OUT-OF-BOUNDS)
    (asserts! (< new-price MAX-ALLOWED-PRICE) ERROR-PRICE-OUT-OF-BOUNDS)
    (var-set current-market-price new-price)
    (var-set oracle-update-block-height stacks-block-height)
    (ok true)
  )
)

(define-public (create-synthetic-tokens (token-amount uint))
  (let ((latest-market-price (var-get current-market-price)))
    (asserts! (> token-amount u0) ERROR-AMOUNT-MUST-BE-POSITIVE)
    (asserts! (>= token-amount MIN-TOKEN-ISSUANCE-AMOUNT)
      ERROR-INVALID-TOKEN-QUANTITY
    )
    (asserts!
      (<= (- stacks-block-height (var-get oracle-update-block-height))
        PRICE-VALIDITY-PERIOD-BLOCKS
      )
      ERROR-ORACLE-DATA-STALE
    )

    (match (secure-multiply token-amount (/ latest-market-price u100))
      base-collateral-needed (match (secure-multiply base-collateral-needed (/ COLLATERAL-SAFETY-RATIO u100))
        total-collateral-required (match (stx-transfer? total-collateral-required tx-sender CONTRACT-OWNER)
          transfer-success (begin
            (map-set collateralized-positions tx-sender {
              staked-collateral: total-collateral-required,
              issued-synthetic-tokens: token-amount,
              position-open-price: latest-market-price,
            })
            (match (secure-add (get-user-token-balance tx-sender) token-amount)
              updated-user-balance (begin
                (map-set user-token-holdings tx-sender updated-user-balance)
                (match (secure-add (var-get global-token-supply) token-amount)
                  new-total-supply (begin
                    (var-set global-token-supply new-total-supply)
                    (ok true)
                  )
                  error ERROR-CALCULATION-OVERFLOW
                )
              )
              error ERROR-CALCULATION-OVERFLOW
            )
          )
          error ERROR-COLLATERAL-DEPOSIT-INSUFFICIENT
        )
        error ERROR-CALCULATION-OVERFLOW
      )
      error ERROR-CALCULATION-OVERFLOW
    )
  )
)

(define-public (destroy-synthetic-tokens (token-amount uint))
  (let (
      (position-data (unwrap! (get-position-details tx-sender) ERROR-VAULT-NOT-FOUND))
      (user-balance (get-user-token-balance tx-sender))
    )
    (asserts! (> token-amount u0) ERROR-AMOUNT-MUST-BE-POSITIVE)
    (asserts! (>= user-balance token-amount) ERROR-TOKEN-BALANCE-TOO-LOW)
    (asserts! (>= (get issued-synthetic-tokens position-data) token-amount)
      ERROR-NOT-AUTHORIZED
    )

    (match (secure-multiply (get staked-collateral position-data) token-amount)
      collateral-calculation (let ((collateral-to-release (/ collateral-calculation (get issued-synthetic-tokens position-data))))
        ;; Clarity 4: Removed as-contract. Contract must be authorized to send STX.
        ;; You may need to add authorization logic or use post-conditions.
        (try! (stx-transfer? collateral-to-release CONTRACT-OWNER tx-sender))

        (match (secure-subtract (get staked-collateral position-data)
          collateral-to-release
        )
          remaining-collateral (match (secure-subtract (get issued-synthetic-tokens position-data)
            token-amount
          )
            remaining-tokens (begin
              (map-set collateralized-positions tx-sender {
                staked-collateral: remaining-collateral,
                issued-synthetic-tokens: remaining-tokens,
                position-open-price: (var-get current-market-price),
              })

              (match (secure-subtract user-balance token-amount)
                new-user-balance (begin
                  (map-set user-token-holdings tx-sender new-user-balance)
                  (match (secure-subtract (var-get global-token-supply) token-amount)
                    new-total-supply (begin
                      (var-set global-token-supply new-total-supply)
                      (ok true)
                    )
                    error ERROR-CALCULATION-OVERFLOW
                  )
                )
                error ERROR-CALCULATION-OVERFLOW
              )
            )
            error ERROR-CALCULATION-OVERFLOW
          )
          error ERROR-CALCULATION-OVERFLOW
        )
      )
      error ERROR-CALCULATION-OVERFLOW
    )
  )
)

(define-public (send-synthetic-tokens
    (recipient principal)
    (amount uint)
  )
  (begin
    ;; Validation checks
    (asserts! (> amount u0) ERROR-AMOUNT-MUST-BE-POSITIVE)
    (asserts! (<= amount (get-user-token-balance tx-sender))
      ERROR-TOKEN-BALANCE-TOO-LOW
    )
    (asserts! (not (is-eq tx-sender recipient)) ERROR-INVALID-TRANSFER-RECIPIENT)

    ;; Process the transfer after validations
    (process-token-transfer tx-sender recipient amount)
  )
)

(define-public (add-collateral (collateral-amount uint))
  (let ((position-data (default-to {
      staked-collateral: u0,
      issued-synthetic-tokens: u0,
      position-open-price: u0,
    }
      (get-position-details tx-sender)
    )))
    (asserts! (> collateral-amount u0) ERROR-AMOUNT-MUST-BE-POSITIVE)
    (try! (stx-transfer? collateral-amount tx-sender CONTRACT-OWNER))

    (match (secure-add (get staked-collateral position-data) collateral-amount)
      updated-collateral (begin
        (map-set collateralized-positions tx-sender {
          staked-collateral: updated-collateral,
          issued-synthetic-tokens: (get issued-synthetic-tokens position-data),
          position-open-price: (var-get current-market-price),
        })
        (ok true)
      )
      error ERROR-CALCULATION-OVERFLOW
    )
  )
)

(define-public (force-close-position (position-owner principal))
  (let (
      (position-data (unwrap! (get-position-details position-owner) ERROR-VAULT-NOT-FOUND))
      (health-ratio (unwrap! (calculate-position-health-ratio position-owner)
        ERROR-NOT-AUTHORIZED
      ))
    )
    (asserts! (< health-ratio COLLATERAL-LIQUIDATION-RATIO) ERROR-NOT-AUTHORIZED)

    ;; Transfer liquidated collateral to caller
    ;; Clarity 4: Removed as-contract. Contract must be authorized to send STX.
    ;; You may need to add authorization logic or use post-conditions.
    (try! (stx-transfer? (get staked-collateral position-data) CONTRACT-OWNER tx-sender))

    ;; Remove the liquidated position
    (map-delete collateralized-positions position-owner)

    ;; Remove tokens from circulation
    (map-set user-token-holdings position-owner u0)
    (match (secure-subtract (var-get global-token-supply)
      (get issued-synthetic-tokens position-data)
    )
      updated-supply (begin
        (var-set global-token-supply updated-supply)
        (ok true)
      )
      error ERROR-CALCULATION-OVERFLOW
    )
  )
)

Functions (10)

FunctionAccessArgs
get-user-token-balanceread-onlyaccount-holder: principal
get-circulating-supplyread-only
get-market-priceread-only
get-position-detailsread-onlyposition-owner: principal
calculate-position-health-ratioread-onlyposition-owner: principal
set-price-feed-valuepublicnew-price: uint
create-synthetic-tokenspublictoken-amount: uint
destroy-synthetic-tokenspublictoken-amount: uint
add-collateralpubliccollateral-amount: uint
force-close-positionpublicposition-owner: principal