Source Code

;; Stacks Timelock - Time-locked Transactions
;; Schedule future transactions and vesting

(define-constant contract-owner tx-sender)
(define-constant err-not-found (err u100))
(define-constant err-not-owner (err u101))
(define-constant err-not-ready (err u102))
(define-constant err-already-executed (err u103))
(define-constant err-cancelled (err u104))

(define-data-var lock-count uint u0)
(define-data-var total-locked uint u0)

(define-map timelocks uint
  {
    creator: principal,
    beneficiary: principal,
    amount: uint,
    unlock-block: uint,
    created-at: uint,
    executed: bool,
    cancelled: bool,
    memo: (optional (string-utf8 128))
  }
)

(define-map user-locks principal (list 50 uint))

(define-read-only (get-timelock (lock-id uint))
  (map-get? timelocks lock-id)
)

(define-read-only (is-unlocked (lock-id uint))
  (match (map-get? timelocks lock-id)
    lock (and 
      (>= stacks-block-height (get unlock-block lock))
      (not (get executed lock))
      (not (get cancelled lock))
    )
    false
  )
)

(define-read-only (get-time-remaining (lock-id uint))
  (match (map-get? timelocks lock-id)
    lock 
    (if (>= stacks-block-height (get unlock-block lock))
      u0
      (- (get unlock-block lock) stacks-block-height)
    )
    u0
  )
)

(define-read-only (get-stats)
  {
    total-locks: (var-get lock-count),
    total-locked: (var-get total-locked)
  }
)

(define-public (create-timelock (beneficiary principal) (amount uint) (unlock-delay uint) (memo (optional (string-utf8 128))))
  (let (
    (lock-id (var-get lock-count))
  )
    (map-set timelocks lock-id {
      creator: tx-sender,
      beneficiary: beneficiary,
      amount: amount,
      unlock-block: (+ stacks-block-height unlock-delay),
      created-at: stacks-block-height,
      executed: false,
      cancelled: false,
      memo: memo
    })
    
    (var-set lock-count (+ lock-id u1))
    (var-set total-locked (+ (var-get total-locked) amount))
    
    (ok { lock-id: lock-id, unlock-block: (+ stacks-block-height unlock-delay) })
  )
)

(define-public (execute-timelock (lock-id uint))
  (match (map-get? timelocks lock-id)
    lock
    (begin
      (asserts! (>= stacks-block-height (get unlock-block lock)) err-not-ready)
      (asserts! (not (get executed lock)) err-already-executed)
      (asserts! (not (get cancelled lock)) err-cancelled)
      (asserts! (is-eq tx-sender (get beneficiary lock)) err-not-owner)
      
      (map-set timelocks lock-id (merge lock { executed: true }))
      (var-set total-locked (- (var-get total-locked) (get amount lock)))
      
      (ok { lock-id: lock-id, amount: (get amount lock), beneficiary: (get beneficiary lock) })
    )
    err-not-found
  )
)

(define-public (cancel-timelock (lock-id uint))
  (match (map-get? timelocks lock-id)
    lock
    (begin
      (asserts! (is-eq tx-sender (get creator lock)) err-not-owner)
      (asserts! (not (get executed lock)) err-already-executed)
      (asserts! (not (get cancelled lock)) err-cancelled)
      
      (map-set timelocks lock-id (merge lock { cancelled: true }))
      (var-set total-locked (- (var-get total-locked) (get amount lock)))
      
      (ok { lock-id: lock-id, refunded: true })
    )
    err-not-found
  )
)

(define-public (extend-timelock (lock-id uint) (additional-blocks uint))
  (match (map-get? timelocks lock-id)
    lock
    (begin
      (asserts! (is-eq tx-sender (get creator lock)) err-not-owner)
      (asserts! (not (get executed lock)) err-already-executed)
      (asserts! (not (get cancelled lock)) err-cancelled)
      
      (map-set timelocks lock-id 
        (merge lock { unlock-block: (+ (get unlock-block lock) additional-blocks) })
      )
      
      (ok { lock-id: lock-id, new-unlock-block: (+ (get unlock-block lock) additional-blocks) })
    )
    err-not-found
  )
)

Functions (8)

FunctionAccessArgs
get-timelockread-onlylock-id: uint
is-unlockedread-onlylock-id: uint
get-time-remainingread-onlylock-id: uint
get-statsread-only
create-timelockpublicbeneficiary: principal, amount: uint, unlock-delay: uint, memo: (optional (string-utf8 128
execute-timelockpubliclock-id: uint
cancel-timelockpubliclock-id: uint
extend-timelockpubliclock-id: uint, additional-blocks: uint