Source Code

;; Advanced Staking Rewards System
;; Users stake STX and earn rewards over time with flexible lock periods

(define-constant CONTRACT_OWNER tx-sender)
(define-constant ERR_NOT_AUTHORIZED (err u401))
(define-constant ERR_NO_STAKE (err u402))
(define-constant ERR_ALREADY_STAKED (err u403))
(define-constant ERR_LOCK_NOT_EXPIRED (err u404))
(define-constant ERR_INSUFFICIENT_REWARDS (err u405))

(define-constant REWARD_RATE u100) ;; 1% per 144 blocks (~24 hours)
(define-constant MIN_STAKE u1000000) ;; 1 STX minimum

(define-data-var total-staked uint u0)
(define-data-var reward-pool uint u0)

(define-map stakes
    principal
    {
        amount: uint,
        staked-at: uint,
        lock-period: uint, ;; in blocks
        last-claim: uint
    }
)

(define-public (stake (amount uint) (lock-period uint))
    (let
        (
            (existing-stake (map-get? stakes tx-sender))
        )
        (asserts! (>= amount MIN_STAKE) ERR_NOT_AUTHORIZED)
        (asserts! (is-none existing-stake) ERR_ALREADY_STAKED)
        
        (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
        
        (map-set stakes tx-sender {
            amount: amount,
            staked-at: stacks-block-height,
            lock-period: lock-period,
            last-claim: stacks-block-height
        })
        
        (var-set total-staked (+ (var-get total-staked) amount))
        (ok true)
    )
)

(define-public (calculate-rewards (user principal))
    (let
        (
            (stake-info (unwrap! (map-get? stakes user) ERR_NO_STAKE))
            (blocks-staked (- stacks-block-height (get last-claim stake-info)))
            (base-reward (/ (* (get amount stake-info) REWARD_RATE) u10000))
            (time-reward (/ (* base-reward blocks-staked) u144))
            ;; Bonus for longer lock periods
            (lock-bonus (if (>= (get lock-period stake-info) u1008)
                (/ time-reward u10) ;; 10% bonus for 7-day lock
                u0
            ))
        )
        (ok (+ time-reward lock-bonus))
    )
)

(define-public (claim-rewards)
    (let
        (
            (stake-info (unwrap! (map-get? stakes tx-sender) ERR_NO_STAKE))
            (rewards (unwrap! (calculate-rewards tx-sender) ERR_NO_STAKE))
        )
        (asserts! (<= rewards (var-get reward-pool)) ERR_INSUFFICIENT_REWARDS)
        
        (try! (as-contract (stx-transfer? rewards tx-sender tx-sender)))
        
        (map-set stakes tx-sender (merge stake-info {last-claim: stacks-block-height}))
        (var-set reward-pool (- (var-get reward-pool) rewards))
        (ok rewards)
    )
)

(define-public (unstake)
    (let
        (
            (stake-info (unwrap! (map-get? stakes tx-sender) ERR_NO_STAKE))
            (unlock-height (+ (get staked-at stake-info) (get lock-period stake-info)))
        )
        (asserts! (>= stacks-block-height unlock-height) ERR_LOCK_NOT_EXPIRED)
        
        ;; Claim any pending rewards first
        (try! (claim-rewards))
        
        ;; Return staked amount
        (try! (as-contract (stx-transfer? (get amount stake-info) tx-sender tx-sender)))
        
        (map-delete stakes tx-sender)
        (var-set total-staked (- (var-get total-staked) (get amount stake-info)))
        (ok true)
    )
)

(define-public (fund-reward-pool (amount uint))
    (begin
        (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
        (var-set reward-pool (+ (var-get reward-pool) amount))
        (ok true)
    )
)

(define-read-only (get-stake (user principal))
    (ok (map-get? stakes user))
)

(define-read-only (get-total-staked)
    (ok (var-get total-staked))
)

Functions (7)

FunctionAccessArgs
stakepublicamount: uint, lock-period: uint
calculate-rewardspublicuser: principal
claim-rewardspublic
unstakepublic
fund-reward-poolpublicamount: uint
get-stakeread-onlyuser: principal
get-total-stakedread-only