Source Code

;; Staking Contract
;; Manages community staking pools, rewards, and slashing

(define-constant ERR-UNAUTHORIZED (err u6001))
(define-constant ERR-INSUFFICIENT-STAKE (err u6002))
(define-constant ERR-POOL-NOT-FOUND (err u6003))
(define-constant ERR-INVALID-AMOUNT (err u6004))
(define-constant ERR-LOCKED (err u6005))

(define-constant MIN-STAKE-AMOUNT u1000)
(define-constant REWARD-RATE u5) ;; 5% annual reward rate

;; Staking pool for each startup
(define-map staking-pools
    { startup-id: uint }
    {
        total-staked: uint,
        total-rewards: uint,
        reward-rate: uint,
        created-at: uint,
        last-reward-distribution: uint
    }
)

;; Individual stakes
(define-map stakes
    { staker: principal, startup-id: uint }
    {
        amount: uint,
        staked-at: uint,
        last-claim: uint,
        accumulated-rewards: uint
    }
)

;; Create staking pool for a startup
(define-public (create-staking-pool
    (startup-id uint)
    (reward-rate uint)
)
    (let
        (
            (timestamp (unwrap! (get-block-info? time u0) ERR-UNAUTHORIZED))
        )
        (asserts! (is-none (map-get? staking-pools { startup-id: startup-id })) ERR-POOL-NOT-FOUND)
        (ok (map-set staking-pools
            { startup-id: startup-id }
            {
                total-staked: u0,
                total-rewards: u0,
                reward-rate: reward-rate,
                created-at: timestamp,
                last-reward-distribution: timestamp
            }
        ))
    )
)

;; Stake tokens to support a startup
(define-public (stake-tokens
    (startup-id uint)
    (amount uint)
)
    (let
        (
            (staker tx-sender)
            (pool (unwrap! (map-get? staking-pools { startup-id: startup-id }) ERR-POOL-NOT-FOUND))
            (existing-stake (map-get? stakes { staker: staker, startup-id: startup-id }))
            (timestamp (unwrap! (get-block-info? time u0) ERR-UNAUTHORIZED))
        )
        (asserts! (>= amount MIN-STAKE-AMOUNT) ERR-INVALID-AMOUNT)
        (if (is-some existing-stake)
            (let
                (
                    (stake (unwrap! existing-stake ERR-UNAUTHORIZED))
                    (new-amount (+ (get amount stake) amount))
                )
                (begin
                    (map-set stakes
                        { staker: staker, startup-id: startup-id }
                        {
                            amount: new-amount,
                            staked-at: (get staked-at stake),
                            last-claim: timestamp,
                            accumulated-rewards: (get accumulated-rewards stake)
                        }
                    )
                    (map-set staking-pools
                        { startup-id: startup-id }
                        {
                            total-staked: (+ (get total-staked pool) amount),
                            total-rewards: (get total-rewards pool),
                            reward-rate: (get reward-rate pool),
                            created-at: (get created-at pool),
                            last-reward-distribution: (get last-reward-distribution pool)
                        }
                    )
                    (ok true)
                )
            )
            (begin
                (map-set stakes
                    { staker: staker, startup-id: startup-id }
                    {
                        amount: amount,
                        staked-at: timestamp,
                        last-claim: timestamp,
                        accumulated-rewards: u0
                    }
                )
                (map-set staking-pools
                    { startup-id: startup-id }
                    {
                        total-staked: (+ (get total-staked pool) amount),
                        total-rewards: (get total-rewards pool),
                        reward-rate: (get reward-rate pool),
                        created-at: (get created-at pool),
                        last-reward-distribution: (get last-reward-distribution pool)
                    }
                )
                (ok true)
            )
        )
    )
)

;; Unstake tokens
(define-public (unstake-tokens
    (startup-id uint)
    (amount uint)
)
    (let
        (
            (staker tx-sender)
            (stake (unwrap! (map-get? stakes { staker: staker, startup-id: startup-id }) ERR-INSUFFICIENT-STAKE))
            (pool (unwrap! (map-get? staking-pools { startup-id: startup-id }) ERR-POOL-NOT-FOUND))
            (current-amount (get amount stake))
        )
        (asserts! (>= current-amount amount) ERR-INSUFFICIENT-STAKE)
        (let
            (
                (new-amount (- current-amount amount))
            )
            (begin
                (if (is-eq new-amount u0)
                    (map-delete stakes { staker: staker, startup-id: startup-id })
                    (map-set stakes
                        { staker: staker, startup-id: startup-id }
                        {
                            amount: new-amount,
                            staked-at: (get staked-at stake),
                            last-claim: (get last-claim stake),
                            accumulated-rewards: (get accumulated-rewards stake)
                        }
                    )
                )
                (map-set staking-pools
                    { startup-id: startup-id }
                    {
                        total-staked: (- (get total-staked pool) amount),
                        total-rewards: (get total-rewards pool),
                        reward-rate: (get reward-rate pool),
                        created-at: (get created-at pool),
                        last-reward-distribution: (get last-reward-distribution pool)
                    }
                )
                (ok true)
            )
        )
    )
)

;; Calculate and claim rewards
(define-public (claim-rewards
    (startup-id uint)
)
    (let
        (
            (staker tx-sender)
            (stake (unwrap! (map-get? stakes { staker: staker, startup-id: startup-id }) ERR-INSUFFICIENT-STAKE))
            (pool (unwrap! (map-get? staking-pools { startup-id: startup-id }) ERR-POOL-NOT-FOUND))
            (timestamp (unwrap! (get-block-info? time u0) ERR-UNAUTHORIZED))
            (time-elapsed (- timestamp (get last-claim stake)))
            (seconds-per-year u31536000)
        )
        (let
            (
                (rewards (/ (* (* (get amount stake) (get reward-rate pool)) time-elapsed) (* seconds-per-year u100)))
                (total-rewards (+ (get accumulated-rewards stake) rewards))
            )
            (begin
                (map-set stakes
                    { staker: staker, startup-id: startup-id }
                    {
                        amount: (get amount stake),
                        staked-at: (get staked-at stake),
                        last-claim: timestamp,
                        accumulated-rewards: total-rewards
                    }
                )
                (ok total-rewards)
            )
        )
    )
)

;; Distribute milestone rewards to stakers
(define-public (distribute-milestone-rewards
    (startup-id uint)
    (reward-amount uint)
)
    (let
        (
            (pool (unwrap! (map-get? staking-pools { startup-id: startup-id }) ERR-POOL-NOT-FOUND))
            (timestamp (unwrap! (get-block-info? time u0) ERR-UNAUTHORIZED))
        )
        (ok (map-set staking-pools
            { startup-id: startup-id }
            {
                total-staked: (get total-staked pool),
                total-rewards: (+ (get total-rewards pool) reward-amount),
                reward-rate: (get reward-rate pool),
                created-at: (get created-at pool),
                last-reward-distribution: timestamp
            }
        ))
    )
)

;; Get staking pool info
(define-read-only (get-staking-pool (startup-id uint))
    (map-get? staking-pools { startup-id: startup-id })
)

;; Get stake info
(define-read-only (get-stake (staker principal) (startup-id uint))
    (map-get? stakes { staker: staker, startup-id: startup-id })
)

;; Calculate pending rewards
(define-read-only (calculate-pending-rewards
    (staker principal)
    (startup-id uint)
)
    (let
        (
            (stake (map-get? stakes { staker: staker, startup-id: startup-id }))
            (pool (map-get? staking-pools { startup-id: startup-id }))
        )
        (if (and (is-some stake) (is-some pool))
            (let
                (
                    (stake-data (unwrap! stake ERR-UNAUTHORIZED))
                    (pool-data (unwrap! pool ERR-UNAUTHORIZED))
                    (timestamp (unwrap! (get-block-info? time u0) ERR-UNAUTHORIZED))
                    (time-elapsed (- timestamp (get last-claim stake-data)))
                    (seconds-per-year u31536000)
                )
                (ok (+ (get accumulated-rewards stake-data) (/ (* (* (get amount stake-data) (get reward-rate pool-data)) time-elapsed) (* seconds-per-year u100))))
            )
            (ok u0)
        )
    )
)

Functions (2)

FunctionAccessArgs
get-staking-poolread-onlystartup-id: uint
get-stakeread-onlystaker: principal, startup-id: uint