Source Code

;; Stakied Rewards Distributor - Yield rewards distribution engine
;; Distributes accumulated yield to YT holders across maturities

;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u800))
(define-constant err-not-authorized (err u801))
(define-constant err-invalid-amount (err u802))
(define-constant err-no-rewards (err u803))
(define-constant err-invalid-maturity (err u804))
(define-constant err-already-claimed (err u805))
(define-constant err-distributor-paused (err u806))
(define-constant err-no-supply (err u807))
(define-constant err-epoch-not-ended (err u808))
(define-constant err-invalid-epoch (err u809))

;; Precision for reward index calculations
(define-constant index-precision u1000000000000) ;; 12 decimals

;; Data variables
(define-data-var total-distributed uint u0)
(define-data-var current-epoch uint u0)
(define-data-var epoch-length uint u1008) ;; ~7 days in blocks
(define-data-var distributor-paused bool false)

;; Data maps - reward indices per maturity
(define-map reward-indices
  uint ;; maturity
  {
    global-index: uint,
    total-rewards: uint,
    last-update: uint
  }
)

;; Per-user reward tracking
(define-map user-reward-state
  {user: principal, maturity: uint}
  {
    user-index: uint,
    accrued: uint,
    claimed: uint
  }
)

;; Epoch reward pools
(define-map epoch-rewards
  uint ;; epoch-id
  {
    amount: uint,
    start-block: uint,
    end-block: uint,
    distributed: bool
  }
)

;; Read-only functions
(define-read-only (get-reward-index (maturity uint))
  (ok (default-to
    {global-index: u0, total-rewards: u0, last-update: u0}
    (map-get? reward-indices maturity))))

(define-read-only (get-user-reward-state (user principal) (maturity uint))
  (ok (default-to
    {user-index: u0, accrued: u0, claimed: u0}
    (map-get? user-reward-state {user: user, maturity: maturity}))))

(define-read-only (get-pending-rewards (user principal) (maturity uint))
  (let (
    (global-state (default-to
      {global-index: u0, total-rewards: u0, last-update: u0}
      (map-get? reward-indices maturity)))
    (user-state (default-to
      {user-index: u0, accrued: u0, claimed: u0}
      (map-get? user-reward-state {user: user, maturity: maturity})))
    (index-diff (- (get global-index global-state) (get user-index user-state)))
  )
    ;; Simplified: pending = accrued from index diff
    ;; In production this would query YT balance from pt-yt-core
    (ok (+ (get accrued user-state) index-diff))
  )
)

(define-read-only (get-epoch-info (epoch-id uint))
  (ok (map-get? epoch-rewards epoch-id)))

(define-read-only (get-current-epoch)
  (ok (var-get current-epoch)))

(define-read-only (get-distribution-stats)
  (ok {
    total-distributed: (var-get total-distributed),
    current-epoch: (var-get current-epoch),
    epoch-length: (var-get epoch-length),
    paused: (var-get distributor-paused)
  }))

;; Public functions
(define-public (distribute-rewards (maturity uint) (amount uint))
  (begin
    (asserts! (not (var-get distributor-paused)) err-distributor-paused)
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (asserts! (> amount u0) err-invalid-amount)
    (asserts! (> maturity u0) err-invalid-maturity)

    (let (
      (current-state (default-to
        {global-index: u0, total-rewards: u0, last-update: u0}
        (map-get? reward-indices maturity)))
      ;; In production, query actual YT total supply from pt-yt-core
      ;; For now, use a safe default
      (new-index (+ (get global-index current-state) (/ (* amount index-precision) (+ amount u1))))
    )
      (map-set reward-indices maturity {
        global-index: new-index,
        total-rewards: (+ (get total-rewards current-state) amount),
        last-update: block-height
      })

      (var-set total-distributed (+ (var-get total-distributed) amount))

      (print {action: "distribute-rewards", maturity: maturity, amount: amount, new-index: new-index})
      (ok new-index)
    )
  )
)

(define-public (claim-user-rewards (maturity uint))
  (let (
    (pending (unwrap-panic (get-pending-rewards tx-sender maturity)))
    (user-state (default-to
      {user-index: u0, accrued: u0, claimed: u0}
      (map-get? user-reward-state {user: tx-sender, maturity: maturity})))
    (global-state (default-to
      {global-index: u0, total-rewards: u0, last-update: u0}
      (map-get? reward-indices maturity)))
  )
    (asserts! (not (var-get distributor-paused)) err-distributor-paused)
    (asserts! (> pending u0) err-no-rewards)

    ;; Update user state
    (map-set user-reward-state {user: tx-sender, maturity: maturity} {
      user-index: (get global-index global-state),
      accrued: u0,
      claimed: (+ (get claimed user-state) pending)
    })

    (print {action: "claim-user-rewards", user: tx-sender, maturity: maturity, amount: pending})
    (ok pending)
  )
)

(define-public (create-epoch (amount uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (asserts! (> amount u0) err-invalid-amount)

    (let ((new-epoch (+ (var-get current-epoch) u1)))
      (map-set epoch-rewards new-epoch {
        amount: amount,
        start-block: block-height,
        end-block: (+ block-height (var-get epoch-length)),
        distributed: false
      })

      (var-set current-epoch new-epoch)

      (print {action: "create-epoch", epoch: new-epoch, amount: amount})
      (ok new-epoch)
    )
  )
)

(define-public (set-epoch-length (new-length uint))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (asserts! (> new-length u0) err-invalid-amount)
    (var-set epoch-length new-length)
    (print {action: "set-epoch-length", length: new-length})
    (ok new-length)
  )
)

(define-public (set-distributor-paused (paused bool))
  (begin
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (var-set distributor-paused paused)
    (print {action: "set-distributor-paused", paused: paused})
    (ok paused)
  )
)

Functions (11)

FunctionAccessArgs
get-reward-indexread-onlymaturity: uint
get-user-reward-stateread-onlyuser: principal, maturity: uint
get-pending-rewardsread-onlyuser: principal, maturity: uint
get-epoch-inforead-onlyepoch-id: uint
get-current-epochread-only
get-distribution-statsread-only
distribute-rewardspublicmaturity: uint, amount: uint
claim-user-rewardspublicmaturity: uint
create-epochpublicamount: uint
set-epoch-lengthpublicnew-length: uint
set-distributor-pausedpublicpaused: bool