Source Code

;; Simple Staking Contract
;; Stake tokens and earn rewards

(define-constant err-insufficient-balance (err u100))
(define-constant err-no-stake (err u101))
(define-constant err-not-owner (err u102))

(define-constant reward-rate u10) ;; 10% per reward cycle
(define-constant reward-cycle u2016) ;; ~2 weeks in blocks

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

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

;; Stake tokens
(define-public (stake (amount uint))
    (let
        (
            (existing-stake (map-get? stakes tx-sender))
        )
        (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
        
        (match existing-stake
            stake-data
            ;; Update existing stake
            (begin
                (map-set stakes tx-sender {
                    amount: (+ (get amount stake-data) amount),
                    start-block: (get start-block stake-data),
                    last-claim-block: (get last-claim-block stake-data)
                })
            )
            ;; Create new stake
            (map-set stakes tx-sender {
                amount: amount,
                start-block: block-height,
                last-claim-block: block-height
            })
        )
        
        (var-set total-staked (+ (var-get total-staked) amount))
        (ok true)
    )
)

;; Unstake tokens
(define-public (unstake (amount uint))
    (let
        (
            (stake-data (unwrap! (map-get? stakes tx-sender) err-no-stake))
        )
        (asserts! (>= (get amount stake-data) amount) err-insufficient-balance)
        
        ;; Claim rewards first
        (try! (claim-rewards))
        
        ;; Transfer staked tokens back
        (try! (as-contract (stx-transfer? amount tx-sender tx-sender)))
        
        (if (is-eq (get amount stake-data) amount)
            ;; Remove stake completely
            (map-delete stakes tx-sender)
            ;; Update stake amount
            (map-set stakes tx-sender (merge stake-data {
                amount: (- (get amount stake-data) amount)
            }))
        )
        
        (var-set total-staked (- (var-get total-staked) amount))
        (ok true)
    )
)

;; Claim rewards
(define-public (claim-rewards)
    (let
        (
            (stake-data (unwrap! (map-get? stakes tx-sender) err-no-stake))
            (rewards (calculate-rewards tx-sender))
        )
        (if (> rewards u0)
            (begin
                ;; In production, mint reward tokens or transfer from reserve
                (map-set stakes tx-sender (merge stake-data {
                    last-claim-block: block-height
                }))
                (print {event: "rewards-claimed", user: tx-sender, amount: rewards})
                (ok rewards)
            )
            (ok u0)
        )
    )
)

;; Calculate pending rewards
(define-read-only (calculate-rewards (staker principal))
    (match (map-get? stakes staker)
        stake-data
        (let
            (
                (blocks-passed (- block-height (get last-claim-block stake-data)))
                (cycles-completed (/ blocks-passed reward-cycle))
            )
            (/ (* (* (get amount stake-data) cycles-completed) reward-rate) u100)
        )
        u0
    )
)

;; Read-only functions
(define-read-only (get-stake (staker principal))
    (map-get? stakes staker)
)

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

(define-read-only (get-pending-rewards (staker principal))
    (calculate-rewards staker)
)

Functions (7)

FunctionAccessArgs
stakepublicamount: uint
unstakepublicamount: uint
claim-rewardspublic
calculate-rewardsread-onlystaker: principal
get-stakeread-onlystaker: principal
get-total-stakedread-only
get-pending-rewardsread-onlystaker: principal