Source Code

;; cedar-desk - Staking & Yield Protocol
;; Stake tokens and earn proportional rewards

(define-constant CONTRACT-OWNER tx-sender)
(define-constant REWARD-RATE u100)
(define-constant LOCK-PERIOD u144)
(define-constant ERR-OWNER-ONLY (err u100))
(define-constant ERR-ZERO-AMOUNT (err u101))
(define-constant ERR-NO-STAKE (err u102))
(define-constant ERR-STILL-LOCKED (err u103))
(define-constant ERR-INSUFFICIENT-REWARDS (err u104))

(define-map stakers principal
  { amount: uint, start-block: uint, last-claim: uint, rewards-earned: uint })

(define-data-var total-staked uint u0)
(define-data-var reward-pool uint u0)
(define-data-var protocol-fee uint u30)
(define-data-var is-paused bool false)

(define-private (calculate-rewards (staker principal))
  (match (map-get? stakers staker)
    stake-data
      (let ((blocks-elapsed (- block-height (get last-claim stake-data)))
            (stake-amount (get amount stake-data)))
        (* (* stake-amount REWARD-RATE) blocks-elapsed))
    u0))

(define-public (stake (amount uint))
  (begin
    (asserts! (not (var-get is-paused)) (err u105))
    (asserts! (> amount u0) ERR-ZERO-AMOUNT)
    (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
    (match (map-get? stakers tx-sender)
      existing (map-set stakers tx-sender
        { amount: (+ (get amount existing) amount),
          start-block: block-height,
          last-claim: block-height,
          rewards-earned: (+ (get rewards-earned existing) (calculate-rewards tx-sender)) })
      (map-set stakers tx-sender
        { amount: amount, start-block: block-height, last-claim: block-height, rewards-earned: u0 }))
    (var-set total-staked (+ (var-get total-staked) amount))
    (ok true)))

(define-public (unstake (amount uint))
  (let ((stake-data (unwrap! (map-get? stakers tx-sender) ERR-NO-STAKE)))
    (asserts! (>= (get amount stake-data) amount) ERR-INSUFFICIENT-REWARDS)
    (asserts! (>= block-height (+ (get start-block stake-data) LOCK-PERIOD)) ERR-STILL-LOCKED)
    (try! (as-contract (stx-transfer? amount tx-sender contract-caller)))
    (if (is-eq amount (get amount stake-data))
      (map-delete stakers tx-sender)
      (map-set stakers tx-sender (merge stake-data { amount: (- (get amount stake-data) amount) })))
    (var-set total-staked (- (var-get total-staked) amount))
    (ok true)))

(define-public (claim-rewards)
  (let ((pending (calculate-rewards tx-sender))
        (stake-data (unwrap! (map-get? stakers tx-sender) ERR-NO-STAKE)))
    (asserts! (> pending u0) ERR-INSUFFICIENT-REWARDS)
    (map-set stakers tx-sender (merge stake-data { last-claim: block-height }))
    (ok pending)))

(define-public (fund-rewards (amount uint))
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-OWNER-ONLY)
    (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
    (ok (var-set reward-pool (+ (var-get reward-pool) amount)))))

(define-read-only (get-stake-info (staker principal)) (ok (map-get? stakers staker)))
(define-read-only (get-total-staked) (ok (var-get total-staked)))
(define-read-only (get-pending-rewards (staker principal)) (ok (calculate-rewards staker)))
(define-read-only (get-protocol-stats)
  (ok { total-staked: (var-get total-staked), reward-pool: (var-get reward-pool), rate: REWARD-RATE }))

Functions (9)

FunctionAccessArgs
calculate-rewardsprivatestaker: principal
stakepublicamount: uint
unstakepublicamount: uint
claim-rewardspublic
fund-rewardspublicamount: uint
get-stake-inforead-onlystaker: principal
get-total-stakedread-only
get-pending-rewardsread-onlystaker: principal
get-protocol-statsread-only