Source Code

;; -------------------------------------
;; Errors
;; -------------------------------------
(define-constant ERR_NOT_OWNER u100)
(define-constant ERR_LOCKED u101)
(define-constant ERR_UNLOCKED u102)
(define-constant ERR_NO_BALANCE u103)
(define-constant ERR_TRANSFER_FAILED u104) ;; new: transfer failed

;; -------------------------------------
;; State
;; -------------------------------------
(define-data-var owner principal tx-sender)
(define-data-var unlock-time uint stacks-block-time)

;; Track total deposited STX
(define-data-var total-balance uint u0)

;; Track individual deposits (for refunds)
(define-map deposits
    principal
    uint
)

;; Beneficiaries
(define-map beneficiaries
    principal
    bool
)

;; -------------------------------------
;; Deposit STX
;; -------------------------------------
(define-public (deposit (amount uint))
    (begin
        (asserts! (> amount u0) (err ERR_NO_BALANCE))

        ;; ensure transfer succeeded
        (asserts! (is-ok (stx-transfer? amount tx-sender tx-sender))
            (err ERR_TRANSFER_FAILED)
        )

        ;; Update balances
        (var-set total-balance (+ (var-get total-balance) amount))
        (map-set deposits tx-sender
            (+ amount (default-to u0 (map-get? deposits tx-sender)))
        )

        (print {
            event: "deposit",
            sender: tx-sender,
            amount: amount,
            timestamp: stacks-block-time,
        })

        (ok amount)
    )
)

;; -------------------------------------
;; Refund before unlock
;; -------------------------------------
(define-public (refund)
    (let ((amount (default-to u0 (map-get? deposits tx-sender))))
        (begin
            (asserts! (< stacks-block-time (var-get unlock-time))
                (err ERR_UNLOCKED)
            )
            (asserts! (> amount u0) (err ERR_NO_BALANCE))

            (map-delete deposits tx-sender)
            (var-set total-balance (- (var-get total-balance) amount))

            ;; ensure transfer succeeded
            (asserts! (is-ok (stx-transfer? amount tx-sender tx-sender))
                (err ERR_TRANSFER_FAILED)
            )

            (print {
                event: "refund",
                sender: tx-sender,
                amount: amount,
                timestamp: stacks-block-time,
            })

            (ok amount)
        )
    )
)

;; -------------------------------------
;; Withdraw (owner or beneficiaries)
;; -------------------------------------
(define-public (withdraw (amount uint))
    (begin
        (asserts! (>= stacks-block-time (var-get unlock-time)) (err ERR_LOCKED))
        (asserts!
            (or
                (is-eq tx-sender (var-get owner))
                (is-some (map-get? beneficiaries tx-sender))
            )
            (err ERR_NOT_OWNER)
        )

        (var-set total-balance (- (var-get total-balance) amount))

        ;; ensure transfer succeeded
        (asserts! (is-ok (stx-transfer? amount tx-sender tx-sender))
            (err ERR_TRANSFER_FAILED)
        )

        (print {
            event: "withdraw",
            recipient: tx-sender,
            amount: amount,
            timestamp: stacks-block-time,
        })

        (ok amount)
    )
)

;; -------------------------------------
;; Extend lock time (owner only)
;; -------------------------------------
(define-public (extend-lock (new-unlock uint))
    (begin
        (asserts! (is-eq tx-sender (var-get owner)) (err ERR_NOT_OWNER))
        (asserts! (> new-unlock (var-get unlock-time)) (err ERR_LOCKED))

        (var-set unlock-time new-unlock)

        (print {
            event: "lock-extended",
            new-unlock: new-unlock,
            timestamp: stacks-block-time,
        })

        (ok new-unlock)
    )
)

;; -------------------------------------
;; Manage beneficiaries
;; -------------------------------------
(define-public (add-beneficiary (who principal))
    (begin
        (asserts! (is-eq tx-sender (var-get owner)) (err ERR_NOT_OWNER))
        (map-set beneficiaries who true)

        (print {
            event: "beneficiary-added",
            who: who,
        })

        (ok true)
    )
)

(define-public (remove-beneficiary (who principal))
    (begin
        (asserts! (is-eq tx-sender (var-get owner)) (err ERR_NOT_OWNER))
        (map-delete beneficiaries who)

        (print {
            event: "beneficiary-removed",
            who: who,
        })

        (ok true)
    )
)

;; -------------------------------------
;; Read-only functions
;; -------------------------------------
(define-read-only (get-total-balance)
    (var-get total-balance)
)

(define-read-only (get-unlock-time)
    (var-get unlock-time)
)

(define-read-only (is-unlocked)
    (>= stacks-block-time (var-get unlock-time))
)

(define-read-only (get-user-deposit (user principal))
    (default-to u0 (map-get? deposits user))
)

Functions (10)

FunctionAccessArgs
extend-lockpublicnew-unlock: uint
depositpublicamount: uint
refundpublic
withdrawpublicamount: uint
remove-beneficiarypublicwho: principal
get-total-balanceread-only
get-unlock-timeread-only
is-unlockedread-only
get-user-depositread-onlyuser: principal
add-beneficiarypublicwho: principal