Source Code

;; STX DeFi Yield Vault Contract
;; Handles yield generation for deposited STX
;; Mainnet deployment

;; Constants
(define-constant CONTRACT_OWNER tx-sender)
(define-constant ERR_UNAUTHORIZED (err u2000))
(define-constant ERR_INSUFFICIENT_BALANCE (err u2001))
(define-constant ERR_INVALID_AMOUNT (err u2002))
(define-constant ERR_NO_DEPOSIT (err u2003))
(define-constant ERR_ALREADY_INITIALIZED (err u2004))
(define-constant ERR_NOT_INITIALIZED (err u2005))
(define-constant ERR_VAULT_EMPTY (err u2006))

;; Yield rate: 8% APY (stored as basis points, 800 = 8%)
(define-constant VAULT_YIELD_RATE u800)
;; Blocks per year (approx 52,560 with 10-min blocks)
(define-constant BLOCKS_PER_YEAR u52560)
;; Precision for calculations
(define-constant PRECISION u10000)
;; Minimum deposit
(define-constant MIN_DEPOSIT u1000)  ;; 0.001 STX in micro-STX

;; Data Variables
(define-data-var total-vault-deposits uint u0)
(define-data-var total-shares uint u0)
(define-data-var is-initialized bool false)
(define-data-var last-yield-block uint u0)
(define-data-var accumulated-yield uint u0)

;; Data Maps
;; User shares in the vault
(define-map user-shares principal uint)
;; User deposit block for tracking
(define-map user-deposit-block principal uint)

;; Authorization check
(define-private (is-authorized)
  (is-eq tx-sender CONTRACT_OWNER)
)

;; Initialize the vault
(define-public (initialize)
  (begin
    (asserts! (is-authorized) ERR_UNAUTHORIZED)
    (asserts! (not (var-get is-initialized)) ERR_ALREADY_INITIALIZED)
    (var-set is-initialized true)
    (var-set last-yield-block block-height)
    (ok true)
  )
)

;; Read-only functions

;; Get user's share balance
(define-read-only (get-user-shares (user principal))
  (default-to u0 (map-get? user-shares user))
)

;; Calculate current share price (in micro-STX per share)
(define-read-only (get-share-price)
  (let (
    (total-shares-val (var-get total-shares))
    (total-assets (get-total-assets))
  )
    (if (is-eq total-shares-val u0)
      PRECISION  ;; 1:1 initial price
      (/ (* total-assets PRECISION) total-shares-val)
    )
  )
)

;; Calculate total assets including yield
(define-read-only (get-total-assets)
  (+ (var-get total-vault-deposits) (get-pending-yield))
)

;; Calculate pending yield since last update
(define-read-only (get-pending-yield)
  (let (
    (total-deposits (var-get total-vault-deposits))
    (last-block (var-get last-yield-block))
    (blocks-elapsed (- block-height last-block))
    (yield-per-block (/ VAULT_YIELD_RATE BLOCKS_PER_YEAR))
  )
    (+ (var-get accumulated-yield)
       (/ (* (* total-deposits yield-per-block) blocks-elapsed) PRECISION))
  )
)

;; Get user's STX value of their shares
(define-read-only (get-user-value (user principal))
  (let (
    (shares (get-user-shares user))
    (share-price (get-share-price))
  )
    (/ (* shares share-price) PRECISION)
  )
)

;; Calculate shares for a deposit amount
(define-read-only (calculate-shares-for-deposit (amount uint))
  (let (
    (total-shares-val (var-get total-shares))
    (total-assets (get-total-assets))
  )
    (if (is-eq total-shares-val u0)
      amount  ;; 1:1 for first deposit
      (/ (* amount total-shares-val) total-assets)
    )
  )
)

;; Calculate STX for a share redemption
(define-read-only (calculate-stx-for-shares (shares uint))
  (let (
    (total-shares-val (var-get total-shares))
    (total-assets (get-total-assets))
  )
    (if (is-eq total-shares-val u0)
      u0
      (/ (* shares total-assets) total-shares-val)
    )
  )
)

;; Get vault statistics
(define-read-only (get-vault-stats)
  {
    total-deposits: (var-get total-vault-deposits),
    total-shares: (var-get total-shares),
    total-assets: (get-total-assets),
    share-price: (get-share-price),
    pending-yield: (get-pending-yield),
    apy: VAULT_YIELD_RATE
  }
)

;; Internal: Update yield
(define-private (update-yield)
  (let (
    (pending (get-pending-yield))
  )
    (var-set accumulated-yield pending)
    (var-set last-yield-block block-height)
    pending
  )
)

;; Public functions

;; Deposit STX into the vault
(define-public (vault-deposit (amount uint))
  (let (
    (current-shares (get-user-shares tx-sender))
    (shares-to-mint (calculate-shares-for-deposit amount))
  )
    (asserts! (var-get is-initialized) ERR_NOT_INITIALIZED)
    (asserts! (>= amount MIN_DEPOSIT) ERR_INVALID_AMOUNT)
    
    ;; Update yield before deposit
    (update-yield)
    
    ;; Transfer STX to vault
    (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
    
    ;; Mint shares to user
    (map-set user-shares tx-sender (+ current-shares shares-to-mint))
    (map-set user-deposit-block tx-sender block-height)
    
    ;; Update totals
    (var-set total-vault-deposits (+ (var-get total-vault-deposits) amount))
    (var-set total-shares (+ (var-get total-shares) shares-to-mint))
    
    (ok {
      deposited: amount,
      shares-minted: shares-to-mint,
      total-shares: (+ current-shares shares-to-mint)
    })
  )
)

;; Withdraw STX from the vault
(define-public (vault-withdraw (shares uint))
  (let (
    (current-shares (get-user-shares tx-sender))
    (stx-to-withdraw (calculate-stx-for-shares shares))
    (vault-balance (var-get total-vault-deposits))
  )
    (asserts! (var-get is-initialized) ERR_NOT_INITIALIZED)
    (asserts! (> shares u0) ERR_INVALID_AMOUNT)
    (asserts! (<= shares current-shares) ERR_INSUFFICIENT_BALANCE)
    (asserts! (<= stx-to-withdraw vault-balance) ERR_VAULT_EMPTY)
    
    ;; Update yield before withdrawal
    (update-yield)
    
    ;; Burn shares
    (let (
      (new-shares (- current-shares shares))
    )
      (if (is-eq new-shares u0)
        (begin
          (map-delete user-shares tx-sender)
          (map-delete user-deposit-block tx-sender)
        )
        (map-set user-shares tx-sender new-shares)
      )
      
      ;; Update totals
      (var-set total-shares (- (var-get total-shares) shares))
      (var-set total-vault-deposits (- (var-get total-vault-deposits) stx-to-withdraw))
      
      ;; Transfer STX to user
      (try! (as-contract (stx-transfer? stx-to-withdraw tx-sender tx-sender)))
      
      (ok {
        withdrawn: stx-to-withdraw,
        shares-burned: shares,
        remaining-shares: new-shares
      })
    )
  )
)

;; Withdraw all shares
(define-public (vault-withdraw-all)
  (let (
    (current-shares (get-user-shares tx-sender))
  )
    (asserts! (> current-shares u0) ERR_NO_DEPOSIT)
    (vault-withdraw current-shares)
  )
)

;; Admin: Add yield to the vault (simulates yield generation)
;; In production, this would come from actual yield sources
(define-public (add-yield (amount uint))
  (begin
    (asserts! (is-authorized) ERR_UNAUTHORIZED)
    (asserts! (> amount u0) ERR_INVALID_AMOUNT)
    
    ;; Transfer yield to vault
    (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
    
    ;; Add to accumulated yield
    (var-set accumulated-yield (+ (var-get accumulated-yield) amount))
    
    (ok amount)
  )
)

;; Admin: Emergency withdraw (only for stuck funds)
(define-public (emergency-withdraw (amount uint))
  (begin
    (asserts! (is-authorized) ERR_UNAUTHORIZED)
    (try! (as-contract (stx-transfer? amount tx-sender CONTRACT_OWNER)))
    (ok amount)
  )
)

Functions (16)

FunctionAccessArgs
is-authorizedprivate
initializepublic
get-user-sharesread-onlyuser: principal
get-share-priceread-only
get-total-assetsread-only
get-pending-yieldread-only
get-user-valueread-onlyuser: principal
calculate-shares-for-depositread-onlyamount: uint
calculate-stx-for-sharesread-onlyshares: uint
get-vault-statsread-only
update-yieldprivate
vault-depositpublicamount: uint
vault-withdrawpublicshares: uint
vault-withdraw-allpublic
add-yieldpublicamount: uint
emergency-withdrawpublicamount: uint