Source Code

;; Time-Locked Savings
;; Lock funds for a period and earn bonus rewards

(define-constant contract-owner tx-sender)
(define-constant err-invalid-duration (err u700))
(define-constant err-deposit-not-found (err u701))
(define-constant err-still-locked (err u702))
(define-constant err-already-withdrawn (err u703))

(define-constant min-lock-period u144) ;; ~1 day
(define-constant max-lock-period u52560) ;; ~1 year

(define-map deposits
    { deposit-id: uint }
    {
        depositor: principal,
        amount: uint,
        lock-until: uint,
        bonus-rate: uint,
        withdrawn: bool
    }
)

(define-data-var deposit-counter uint u0)

;; Create time-locked deposit
(define-public (create-deposit (amount uint) (lock-duration uint))
    (let
        (
            (deposit-id (+ (var-get deposit-counter) u1))
            (bonus-rate (calculate-bonus-rate lock-duration))
        )
        (asserts! (>= lock-duration min-lock-period) err-invalid-duration)
        (asserts! (<= lock-duration max-lock-period) err-invalid-duration)
        
        (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
        
        (map-set deposits
            { deposit-id: deposit-id }
            {
                depositor: tx-sender,
                amount: amount,
                lock-until: (+ stacks-block-height lock-duration),
                bonus-rate: bonus-rate,
                withdrawn: false
            }
        )
        (var-set deposit-counter deposit-id)
        (ok deposit-id)
    )
)

;; Withdraw after lock period
(define-public (withdraw (deposit-id uint))
    (let
        (
            (deposit (unwrap! (map-get? deposits { deposit-id: deposit-id }) err-deposit-not-found))
            (total-amount (+ (get amount deposit) (calculate-bonus (get amount deposit) (get bonus-rate deposit))))
        )
        (asserts! (is-eq tx-sender (get depositor deposit)) err-deposit-not-found)
        (asserts! (>= stacks-block-height (get lock-until deposit)) err-still-locked)
        (asserts! (not (get withdrawn deposit)) err-already-withdrawn)
        
        (try! (as-contract (stx-transfer? total-amount tx-sender (get depositor deposit))))
        
        (map-set deposits
            { deposit-id: deposit-id }
            (merge deposit { withdrawn: true })
        )
        (ok total-amount)
    )
)

;; Calculate bonus rate based on lock duration
(define-private (calculate-bonus-rate (duration uint))
    (if (>= duration u26280)
        u20 ;; 20% for 6 months+
        (if (>= duration u8760)
            u10 ;; 10% for 2 months+
            u5 ;; 5% for minimum lock
        )
    )
)

;; Calculate bonus amount
(define-private (calculate-bonus (amount uint) (rate uint))
    (/ (* amount rate) u100)
)

;; Read-only functions
(define-read-only (get-deposit (deposit-id uint))
    (map-get? deposits { deposit-id: deposit-id })
)

(define-read-only (get-withdrawal-amount (deposit-id uint))
    (match (map-get? deposits { deposit-id: deposit-id })
        deposit (ok (+ (get amount deposit) (calculate-bonus (get amount deposit) (get bonus-rate deposit))))
        err-deposit-not-found
    )
)

Functions (6)

FunctionAccessArgs
create-depositpublicamount: uint, lock-duration: uint
withdrawpublicdeposit-id: uint
calculate-bonus-rateprivateduration: uint
calculate-bonusprivateamount: uint, rate: uint
get-depositread-onlydeposit-id: uint
get-withdrawal-amountread-onlydeposit-id: uint