Source Code

;; ========================================
;; VAULTAYIELD - CORE VAULT CONTRACT
;; ========================================
;; Bitcoin-native yield vault for STX stacking
;; Users deposit STX, earn BTC rewards automatically

;; ========================================
;; CONSTANTS
;; ========================================

(define-constant CONTRACT-OWNER tx-sender)
(define-constant PRECISION u1000000) ;; 6 decimal precision for calculations

;; Error codes
(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-ZERO-AMOUNT (err u101))
(define-constant ERR-INSUFFICIENT-SHARES (err u102))
(define-constant ERR-CALCULATION-ERROR (err u103))
(define-constant ERR-CONTRACT-PAUSED (err u104))
(define-constant ERR-INSUFFICIENT-BALANCE (err u105))
(define-constant ERR-INVALID-FEE-RATE (err u106))

;; Fee constraints
(define-constant MAX-FEE-RATE u200) ;; 2% maximum (200 basis points)
(define-constant DEFAULT-WITHDRAWAL-FEE u50) ;; 0.5% (50 basis points)

;; ========================================
;; DATA VARIABLES
;; ========================================

(define-data-var total-shares uint u0)
(define-data-var total-assets uint u0)
(define-data-var withdrawal-fee-rate uint DEFAULT-WITHDRAWAL-FEE)
(define-data-var fee-balance uint u0)
(define-data-var contract-paused bool false)

;; Phase 2: PoX Stacking Integration
(define-data-var stacking-strategy-contract (optional principal) none)
(define-data-var stacking-enabled bool false)
(define-data-var min-stacking-threshold uint u100000000000000) ;; 100K STX minimum

;; Phase 3: Will add harvest-manager and compound-engine references

;; ========================================
;; DATA MAPS
;; ========================================

(define-map user-shares principal uint)

;; ========================================
;; PRIVATE FUNCTIONS
;; ========================================

(define-private (calculate-shares-to-mint (deposit-amount uint))
  ;; Calculate shares based on current vault state
  ;; Formula: shares = (deposit-amount * total-shares) / total-assets
  ;; For first deposit: shares = deposit-amount (1:1 ratio)
  (let
    (
      (current-total-shares (var-get total-shares))
      (current-total-assets (var-get total-assets))
    )
    (if (is-eq current-total-shares u0)
      ;; First deposit: 1:1 ratio
      (ok deposit-amount)
      ;; Subsequent deposits: proportional to share price
      (ok (/ (* deposit-amount current-total-shares) current-total-assets))
    )
  )
)

(define-private (calculate-stx-to-return (shares-to-burn uint))
  ;; Calculate STX to return based on share price
  ;; Formula: stx-amount = (shares * total-assets) / total-shares
  (let
    (
      (current-total-shares (var-get total-shares))
      (current-total-assets (var-get total-assets))
    )
    (if (is-eq current-total-shares u0)
      ERR-CALCULATION-ERROR
      (ok (/ (* shares-to-burn current-total-assets) current-total-shares))
    )
  )
)

(define-private (apply-withdrawal-fee (amount uint))
  ;; Calculate withdrawal fee and net amount
  ;; Returns: { fee: uint, net-amount: uint }
  (let
    (
      (fee-rate (var-get withdrawal-fee-rate))
      (fee-amount (/ (* amount fee-rate) u10000))
      (net-amount (- amount fee-amount))
    )
    { fee: fee-amount, net-amount: net-amount }
  )
)

;; ========================================
;; PUBLIC FUNCTIONS - USER INTERACTIONS
;; ========================================

(define-public (deposit (amount uint))
  ;; Accept STX from user and mint vault shares
  (let
    (
      (shares-to-mint (unwrap! (calculate-shares-to-mint amount) ERR-CALCULATION-ERROR))
      (sender tx-sender)
    )
    ;; Validation
    (asserts! (not (var-get contract-paused)) ERR-NOT-AUTHORIZED)
    (asserts! (> amount u0) ERR-ZERO-AMOUNT)
    
    ;; Transfer STX from user to contract
    (try! (stx-transfer? amount sender (as-contract tx-sender)))
    
    ;; Update user shares
    (map-set user-shares 
      sender 
      (+ (default-to u0 (map-get? user-shares sender)) shares-to-mint)
    )
    
    ;; Update vault state
    (var-set total-shares (+ (var-get total-shares) shares-to-mint))
    (var-set total-assets (+ (var-get total-assets) amount))
    
    ;; Emit event (using print for Clarity)
    (print {
      event: "deposit",
      user: sender,
      amount: amount,
      shares-minted: shares-to-mint,
      timestamp: block-height
    })
    
    (ok shares-to-mint)
  )
)

(define-public (deposit-with-stacking (amount uint) (lock-cycles uint))
  ;; Deposit STX and automatically delegate to stacking
  ;; Phase 2: Manual stacking delegation
  ;; Phase 3: Automatic stacking based on threshold
  (let
    (
      (shares-to-mint (unwrap! (calculate-shares-to-mint amount) ERR-CALCULATION-ERROR))
      (sender tx-sender)
      (current-shares (default-to u0 (map-get? user-shares sender)))
      (stacking-enabled-flag (var-get stacking-enabled))
      (stacking-contract (var-get stacking-strategy-contract))
    )
    ;; Validation
    (asserts! (not (var-get contract-paused)) ERR-NOT-AUTHORIZED)
    (asserts! (> amount u0) ERR-ZERO-AMOUNT)
    (asserts! stacking-enabled-flag ERR-NOT-AUTHORIZED)
    (asserts! (is-some stacking-contract) ERR-NOT-AUTHORIZED)
    
    ;; Transfer STX from user to contract
    (try! (stx-transfer? amount sender (as-contract tx-sender)))
    
    ;; Mint shares for user
    (map-set user-shares sender (+ current-shares shares-to-mint))
    
    ;; Update vault totals
    (var-set total-shares (+ (var-get total-shares) shares-to-mint))
    (var-set total-assets (+ (var-get total-assets) amount))
    
    ;; Delegate to stacking strategy if threshold met
    (if (>= (var-get total-assets) (var-get min-stacking-threshold))
      (begin
        ;; TODO: Call stacking-strategy contract
        ;; (try! (as-contract (contract-call? 
        ;;   .stacking-strategy delegate-vault-stx amount lock-cycles)))
        (print { event: "stacking-triggered", amount: amount, cycles: lock-cycles })
        true
      )
      (begin
        (print { event: "below-threshold", current: (var-get total-assets), threshold: (var-get min-stacking-threshold) })
        true
      )
    )
    
    ;; Emit deposit event
    (print { event: "deposit-with-stacking", user: sender, amount: amount, shares: shares-to-mint, cycles: lock-cycles })
    
    (ok shares-to-mint)
  )
)

(define-public (withdraw (shares uint))
  ;; Burn vault shares and return STX to user
  (let
    (
      (sender tx-sender)
      (user-balance (default-to u0 (map-get? user-shares sender)))
      (gross-stx-amount (unwrap! (calculate-stx-to-return shares) ERR-CALCULATION-ERROR))
      (fee-calculation (apply-withdrawal-fee gross-stx-amount))
      (fee-amount (get fee fee-calculation))
      (net-stx-amount (get net-amount fee-calculation))
    )
    ;; Validation
    (asserts! (not (var-get contract-paused)) ERR-NOT-AUTHORIZED)
    (asserts! (> shares u0) ERR-ZERO-AMOUNT)
    (asserts! (>= user-balance shares) ERR-INSUFFICIENT-BALANCE)
    
    ;; Update user shares
    (map-set user-shares sender (- user-balance shares))
    
    ;; Update vault state
    (var-set total-shares (- (var-get total-shares) shares))
    (var-set total-assets (- (var-get total-assets) gross-stx-amount))
    (var-set fee-balance (+ (var-get fee-balance) fee-amount))
    
    ;; Transfer STX back to user
    (try! (as-contract (stx-transfer? net-stx-amount tx-sender sender)))
    
    ;; Emit event
    (print {
      event: "withdrawal",
      user: sender,
      shares-burned: shares,
      stx-returned: net-stx-amount,
      fee-collected: fee-amount,
      timestamp: block-height
    })
    
    (ok net-stx-amount)
  )
)

;; ========================================
;; PUBLIC FUNCTIONS - ADMIN ONLY
;; ========================================

(define-public (set-withdrawal-fee (new-rate uint))
  ;; Update withdrawal fee rate (only owner)
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (asserts! (<= new-rate MAX-FEE-RATE) ERR-INVALID-FEE-RATE)
    (var-set withdrawal-fee-rate new-rate)
    (ok true)
  )
)

(define-public (collect-fees (recipient principal))
  ;; Withdraw accumulated fees (only owner)
  (let
    (
      (accumulated-fees (var-get fee-balance))
    )
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (asserts! (> accumulated-fees u0) ERR-ZERO-AMOUNT)
    
    ;; Reset fee balance
    (var-set fee-balance u0)
    
    ;; Transfer fees
    (try! (as-contract (stx-transfer? accumulated-fees tx-sender recipient)))
    (ok accumulated-fees)
  )
)

;; ========================================
;; PUBLIC FUNCTIONS - PHASE 2 ADMIN CONFIG
;; ========================================

(define-public (set-stacking-strategy (strategy-address principal))
  ;; Set authorized stacking strategy contract
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (var-set stacking-strategy-contract (some strategy-address))
    (ok true)
  )
)

;; Phase 3: Will add set-harvest-manager and set-compound-engine functions

(define-public (enable-stacking)
  ;; Enable stacking feature
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (var-set stacking-enabled true)
    (ok true)
  )
)

(define-public (set-stacking-threshold (threshold uint))
  ;; Update minimum STX threshold for stacking
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (asserts! (> threshold u0) ERR-ZERO-AMOUNT)
    (var-set min-stacking-threshold threshold)
    (ok true)
  )
)

(define-public (pause-contract)
  ;; Emergency pause (only owner)
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (var-set contract-paused true)
    (ok true)
  )
)

(define-public (unpause-contract)
  ;; Resume operations (only owner)
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (var-set contract-paused false)
    (ok true)
  )
)

;; ========================================
;; READ-ONLY FUNCTIONS
;; ========================================

(define-read-only (get-share-price)
  ;; Calculate current price per share
  ;; Returns price with PRECISION decimal places
  (let
    (
      (current-total-shares (var-get total-shares))
      (current-total-assets (var-get total-assets))
    )
    (if (is-eq current-total-shares u0)
      PRECISION ;; 1.0 (1:1 for first deposit)
      (/ (* current-total-assets PRECISION) current-total-shares)
    )
  )
)

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

(define-read-only (get-user-stx-value (user principal))
  ;; Calculate user's STX value based on current shares
  (let
    (
      (shares (get-user-shares user))
      (share-price (get-share-price))
    )
    (/ (* shares share-price) PRECISION)
  )
)

(define-read-only (get-total-assets)
  ;; Return total STX held by vault
  (var-get total-assets)
)

(define-read-only (get-total-shares)
  ;; Return total vault shares issued
  (var-get total-shares)
)

(define-read-only (get-withdrawal-fee-rate)
  ;; Return current withdrawal fee rate
  (var-get withdrawal-fee-rate)
)

(define-read-only (get-accumulated-fees)
  ;; Return total fees collected
  (var-get fee-balance)
)

(define-read-only (is-paused)
  ;; Check if contract is paused
  (var-get contract-paused)
)

(define-read-only (get-contract-owner)
  ;; Return contract owner address
  CONTRACT-OWNER
)

Functions (22)

FunctionAccessArgs
calculate-shares-to-mintprivatedeposit-amount: uint
calculate-stx-to-returnprivateshares-to-burn: uint
apply-withdrawal-feeprivateamount: uint
depositpublicamount: uint
deposit-with-stackingpublicamount: uint, lock-cycles: uint
withdrawpublicshares: uint
set-withdrawal-feepublicnew-rate: uint
collect-feespublicrecipient: principal
set-stacking-strategypublicstrategy-address: principal
enable-stackingpublic
set-stacking-thresholdpublicthreshold: uint
pause-contractpublic
unpause-contractpublic
get-share-priceread-only
get-user-sharesread-onlyuser: principal
get-user-stx-valueread-onlyuser: principal
get-total-assetsread-only
get-total-sharesread-only
get-withdrawal-fee-rateread-only
get-accumulated-feesread-only
is-pausedread-only
get-contract-ownerread-only