Source Code

;; StackPulse Fee Vault V3
;; Upgrades from V2:
;; - Enhanced error handling
;; - Optimized gas usage
;; - Better revenue tracking per tier
;; - Improved referral system
;; - Enhanced event logging for chainhooks
;;
;; Features:
;; - Subscription fee collection
;; - Revenue tracking per tier
;; - Withdrawal to treasury
;; - Referral bonus tracking

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

(define-constant CONTRACT-OWNER tx-sender)
(define-constant TREASURY-ADDRESS tx-sender)
(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-INVALID-AMOUNT (err u101))
(define-constant ERR-INSUFFICIENT-BALANCE (err u102))
(define-constant ERR-INVALID-TIER (err u103))
(define-constant ERR-ZERO-AMOUNT (err u104))
(define-constant ERR-SELF-REFERRAL (err u105))
(define-constant ERR-NO-EARNINGS (err u106))

;; Subscription prices in microSTX
(define-constant PRICE-FREE u0)
(define-constant PRICE-BASIC u5000000)       ;; 5 STX
(define-constant PRICE-PRO u15000000)        ;; 15 STX  
(define-constant PRICE-PREMIUM u45000000)    ;; 45 STX

;; Platform fee percentage (10%)
(define-constant PLATFORM-FEE-BPS u1000)     ;; 10% in basis points (1000/10000)
(define-constant REFERRAL-BONUS-BPS u500)    ;; 5% referral bonus
(define-constant MAX-TIER u3)

;; ============================================
;; DATA STORAGE
;; ============================================

(define-data-var total-collected uint u0)
(define-data-var total-fees uint u0)
(define-data-var total-subscriptions uint u0)
(define-data-var total-referral-paid uint u0)  ;; V3: Track total referral payouts
(define-data-var contract-balance uint u0)
(define-data-var contract-version (string-ascii 8) "v3.0.0")

;; Track revenue by tier
(define-map tier-revenue uint uint)

;; Track user payments with V3 enhanced data
(define-map user-payments principal 
  {
    total-paid: uint,
    last-payment: uint,
    subscription-count: uint,
    current-tier: uint       ;; V3: Track current tier
  }
)

;; Referral tracking
(define-map referral-earnings principal uint)
(define-map referrer-of principal principal)
(define-map referral-count principal uint)  ;; V3: Count of referrals per user

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

(define-read-only (get-version)
  (var-get contract-version)
)

(define-read-only (get-subscription-price (tier uint))
  (if (is-eq tier u0) PRICE-FREE
    (if (is-eq tier u1) PRICE-BASIC
      (if (is-eq tier u2) PRICE-PRO
        (if (is-eq tier u3) PRICE-PREMIUM
          u0))))
)

(define-read-only (get-tier-revenue (tier uint))
  (default-to u0 (map-get? tier-revenue tier))
)

(define-read-only (get-user-payments (user principal))
  (map-get? user-payments user)
)

(define-read-only (get-referral-earnings (referrer principal))
  (default-to u0 (map-get? referral-earnings referrer))
)

(define-read-only (get-referral-count (referrer principal))
  (default-to u0 (map-get? referral-count referrer))
)

(define-read-only (get-referrer (user principal))
  (map-get? referrer-of user)
)

(define-read-only (get-vault-stats)
  {
    total-collected: (var-get total-collected),
    total-fees: (var-get total-fees),
    total-subscriptions: (var-get total-subscriptions),
    total-referral-paid: (var-get total-referral-paid),
    contract-balance: (var-get contract-balance),
    tier-0-revenue: (get-tier-revenue u0),
    tier-1-revenue: (get-tier-revenue u1),
    tier-2-revenue: (get-tier-revenue u2),
    tier-3-revenue: (get-tier-revenue u3),
    version: (var-get contract-version)
  }
)

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

;; V3: Validate tier
(define-private (is-valid-tier (tier uint))
  (<= tier MAX-TIER)
)

;; V3: Calculate referral bonus
(define-private (calculate-referral-bonus (amount uint))
  (/ (* amount REFERRAL-BONUS-BPS) u10000)
)

;; ============================================
;; PUBLIC FUNCTIONS
;; ============================================

;; Collect subscription fee
(define-public (collect-subscription-fee (tier uint) (referrer (optional principal)))
  (let
    (
      (caller tx-sender)
      (price (get-subscription-price tier))
      (current-tier-revenue (get-tier-revenue tier))
      (user-data (default-to 
        { total-paid: u0, last-payment: u0, subscription-count: u0, current-tier: u0 }
        (map-get? user-payments caller)))
    )
    ;; V3: Enhanced validation
    (asserts! (is-valid-tier tier) ERR-INVALID-TIER)
    
    ;; Only transfer if not free tier
    (if (> price u0)
      (begin
        ;; Transfer STX to this contract
        (try! (stx-transfer? price caller (as-contract tx-sender)))
        
        ;; V3: Enhanced referral handling
        (match referrer
          ref-addr 
            (if (and (not (is-eq ref-addr caller)) (> price u0))
              (let
                (
                  (referral-bonus (calculate-referral-bonus price))
                  (current-earnings (get-referral-earnings ref-addr))
                  (current-ref-count (get-referral-count ref-addr))
                )
                ;; Track referral
                (map-set referrer-of caller ref-addr)
                (map-set referral-earnings ref-addr (+ current-earnings referral-bonus))
                (map-set referral-count ref-addr (+ current-ref-count u1))
                true
              )
              false)
          false)
        
        ;; Update balances
        (var-set total-collected (+ (var-get total-collected) price))
        (var-set contract-balance (+ (var-get contract-balance) price))
        
        true
      )
      true)
    
    ;; Update tier revenue
    (map-set tier-revenue tier (+ current-tier-revenue price))
    
    ;; V3: Update user payments with current tier
    (map-set user-payments caller {
      total-paid: (+ (get total-paid user-data) price),
      last-payment: block-height,
      subscription-count: (+ (get subscription-count user-data) u1),
      current-tier: tier
    })
    
    ;; Update subscription count
    (var-set total-subscriptions (+ (var-get total-subscriptions) u1))
    
    (print {
      event: "fee-collected",
      version: "v3",
      user: caller,
      tier: tier,
      amount: price,
      referrer: referrer,
      subscription-number: (+ (get subscription-count user-data) u1),
      block: block-height
    })
    
    (ok price)
  )
)

;; Collect platform fee (called on alert triggers, etc.)
(define-public (collect-platform-fee (amount uint) (fee-type (string-ascii 32)))
  (let
    (
      (caller tx-sender)
      (fee-amount (/ (* amount PLATFORM-FEE-BPS) u10000))
    )
    ;; V3: Enhanced validation
    (asserts! (> amount u0) ERR-ZERO-AMOUNT)
    (asserts! (> fee-amount u0) ERR-INVALID-AMOUNT)
    
    ;; Transfer fee
    (try! (stx-transfer? fee-amount caller (as-contract tx-sender)))
    
    ;; Update tracking
    (var-set total-fees (+ (var-get total-fees) fee-amount))
    (var-set contract-balance (+ (var-get contract-balance) fee-amount))
    
    (print {
      event: "platform-fee-collected",
      version: "v3",
      user: caller,
      fee-type: fee-type,
      amount: fee-amount,
      block: block-height
    })
    
    (ok fee-amount)
  )
)

;; Withdraw referral earnings
(define-public (withdraw-referral-earnings)
  (let
    (
      (caller tx-sender)
      (earnings (get-referral-earnings caller))
    )
    ;; V3: Better error message
    (asserts! (> earnings u0) ERR-NO-EARNINGS)
    (asserts! (<= earnings (var-get contract-balance)) ERR-INSUFFICIENT-BALANCE)
    
    ;; Transfer earnings
    (try! (as-contract (stx-transfer? earnings tx-sender caller)))
    
    ;; Reset earnings and update balances
    (map-set referral-earnings caller u0)
    (var-set contract-balance (- (var-get contract-balance) earnings))
    (var-set total-referral-paid (+ (var-get total-referral-paid) earnings))
    
    (print {
      event: "referral-withdrawal",
      version: "v3",
      user: caller,
      amount: earnings,
      block: block-height
    })
    
    (ok earnings)
  )
)

;; ============================================
;; ADMIN FUNCTIONS
;; ============================================

;; Admin: Withdraw to treasury
(define-public (withdraw-to-treasury (amount uint))
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (asserts! (> amount u0) ERR-ZERO-AMOUNT)
    (asserts! (<= amount (var-get contract-balance)) ERR-INSUFFICIENT-BALANCE)
    
    ;; Transfer to treasury
    (try! (as-contract (stx-transfer? amount tx-sender TREASURY-ADDRESS)))
    
    ;; Update balance
    (var-set contract-balance (- (var-get contract-balance) amount))
    
    (print {
      event: "treasury-withdrawal",
      version: "v3",
      amount: amount,
      treasury: TREASURY-ADDRESS,
      remaining-balance: (- (var-get contract-balance) amount),
      block: block-height
    })
    
    (ok amount)
  )
)

;; V3: Admin emergency withdrawal (all funds)
(define-public (emergency-withdraw)
  (let
    (
      (balance (var-get contract-balance))
    )
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (asserts! (> balance u0) ERR-ZERO-AMOUNT)
    
    ;; Transfer all to treasury
    (try! (as-contract (stx-transfer? balance tx-sender TREASURY-ADDRESS)))
    
    ;; Reset balance
    (var-set contract-balance u0)
    
    (print {
      event: "emergency-withdrawal",
      version: "v3",
      amount: balance,
      treasury: TREASURY-ADDRESS,
      block: block-height
    })
    
    (ok balance)
  )
)

Functions (15)

FunctionAccessArgs
get-versionread-only
get-subscription-priceread-onlytier: uint
get-tier-revenueread-onlytier: uint
get-user-paymentsread-onlyuser: principal
get-referral-earningsread-onlyreferrer: principal
get-referral-countread-onlyreferrer: principal
get-referrerread-onlyuser: principal
get-vault-statsread-only
is-valid-tierprivatetier: uint
calculate-referral-bonusprivateamount: uint
collect-subscription-feepublictier: uint, referrer: (optional principal
collect-platform-feepublicamount: uint, fee-type: (string-ascii 32
withdraw-referral-earningspublic
withdraw-to-treasurypublicamount: uint
emergency-withdrawpublic