Source Code

;; title: vault-timelock
;; version: 1.0.0
;; summary: Security timelock for critical operations
;; description: 24-48 hour delays on sensitive vault operations - Clarity 4

;; Constants
(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-UNAUTHORIZED (err u400))
(define-constant ERR-OPERATION-NOT-FOUND (err u401))
(define-constant ERR-TIMELOCK-NOT-EXPIRED (err u402))
(define-constant ERR-OPERATION-EXPIRED (err u403))
(define-constant ERR-OPERATION-EXECUTED (err u404))
(define-constant ERR-OPERATION-CANCELLED (err u405))

;; Timelock Delays (in seconds)
(define-constant DELAY-SHORT u86400)   ;; 24 hours
(define-constant DELAY-MEDIUM u172800) ;; 48 hours
(define-constant DELAY-LONG u259200)   ;; 72 hours

;; Grace Period
(define-constant GRACE-PERIOD u604800) ;; 7 days to execute after timelock

;; Operation Types
(define-constant OP-FEE-CHANGE u1)
(define-constant OP-ADMIN-CHANGE u2)
(define-constant OP-PARAMETER-CHANGE u3)
(define-constant OP-UPGRADE u4)

;; Data Variables
(define-data-var next-operation-id uint u1)
(define-data-var timelock-paused bool false)

;; Data Maps - Using stacks-block-time for Clarity 4
(define-map timelocked-operations uint {
  operation-type: uint,
  initiator: principal,
  target-contract: principal,
  operation-data: (buff 256),
  delay-required: uint,
  scheduled-at: uint,  ;; Clarity 4: Unix timestamp
  executable-at: uint, ;; Clarity 4: Unix timestamp
  expires-at: uint,    ;; Clarity 4: Unix timestamp
  executed: bool,
  cancelled: bool
})

(define-map operation-metadata uint {
  title: (string-ascii 100),
  description: (string-ascii 300),
  impact-level: uint  ;; 1=low, 2=medium, 3=high
})

;; Public Functions

;; Schedule operation with timelock
(define-public (schedule-operation
  (operation-type uint)
  (target-contract principal)
  (operation-data (buff 256))
  (title (string-ascii 100))
  (description (string-ascii 300)))
  (let (
    (operation-id (var-get next-operation-id))
    (delay (get-required-delay operation-type))
    (impact (get-impact-level operation-type))
  )
    (asserts! (not (var-get timelock-paused)) ERR-UNAUTHORIZED)

    ;; Schedule operation
    (map-set timelocked-operations operation-id {
      operation-type: operation-type,
      initiator: tx-sender,
      target-contract: target-contract,
      operation-data: operation-data,
      delay-required: delay,
      scheduled-at: stacks-block-time,
      executable-at: (+ stacks-block-time delay),
      expires-at: (+ stacks-block-time (+ delay GRACE-PERIOD)),
      executed: false,
      cancelled: false
    })

    ;; Store metadata
    (map-set operation-metadata operation-id {
      title: title,
      description: description,
      impact-level: impact
    })

    ;; Increment ID
    (var-set next-operation-id (+ operation-id u1))

    ;; Emit event
    (print {
      event: "operation-scheduled",
      operation-id: operation-id,
      initiator: tx-sender,
      operation-type: operation-type,
      executable-at: (+ stacks-block-time delay),
      timestamp: stacks-block-time
    })

    (ok operation-id)
  )
)

;; Execute operation after timelock
(define-public (execute-operation (operation-id uint))
  (let (
    (operation (unwrap! (map-get? timelocked-operations operation-id) ERR-OPERATION-NOT-FOUND))
  )
    (asserts! (not (get cancelled operation)) ERR-OPERATION-CANCELLED)
    (asserts! (not (get executed operation)) ERR-OPERATION-EXECUTED)
    (asserts! (>= stacks-block-time (get executable-at operation)) ERR-TIMELOCK-NOT-EXPIRED)
    (asserts! (< stacks-block-time (get expires-at operation)) ERR-OPERATION-EXPIRED)

    ;; Mark as executed
    (map-set timelocked-operations operation-id
      (merge operation { executed: true }))

    ;; Emit event
    (print {
      event: "operation-executed",
      operation-id: operation-id,
      executor: tx-sender,
      operation-type: (get operation-type operation),
      timestamp: stacks-block-time
    })

    ;; In production, this would call the actual operation
    ;; For now, just return success
    (ok true)
  )
)

;; Cancel scheduled operation (only initiator or admin)
(define-public (cancel-operation (operation-id uint))
  (let (
    (operation (unwrap! (map-get? timelocked-operations operation-id) ERR-OPERATION-NOT-FOUND))
  )
    (asserts! (or
      (is-eq tx-sender (get initiator operation))
      (is-eq tx-sender CONTRACT-OWNER))
      ERR-UNAUTHORIZED)
    (asserts! (not (get executed operation)) ERR-OPERATION-EXECUTED)
    (asserts! (not (get cancelled operation)) ERR-OPERATION-CANCELLED)

    ;; Mark as cancelled
    (map-set timelocked-operations operation-id
      (merge operation { cancelled: true }))

    ;; Emit event
    (print {
      event: "operation-cancelled",
      operation-id: operation-id,
      cancelled-by: tx-sender,
      timestamp: stacks-block-time
    })

    (ok true)
  )
)

;; Admin Functions

(define-public (pause-timelock)
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-UNAUTHORIZED)
    (var-set timelock-paused true)
    (print {
      event: "timelock-paused",
      timestamp: stacks-block-time
    })
    (ok true)
  )
)

(define-public (resume-timelock)
  (begin
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-UNAUTHORIZED)
    (var-set timelock-paused false)
    (print {
      event: "timelock-resumed",
      timestamp: stacks-block-time
    })
    (ok true)
  )
)

;; Read-Only Functions

(define-read-only (get-operation (operation-id uint))
  (map-get? timelocked-operations operation-id)
)

(define-read-only (get-operation-metadata (operation-id uint))
  (map-get? operation-metadata operation-id)
)

(define-read-only (is-executable (operation-id uint))
  (match (map-get? timelocked-operations operation-id)
    operation (and
      (not (get executed operation))
      (not (get cancelled operation))
      (>= stacks-block-time (get executable-at operation))
      (< stacks-block-time (get expires-at operation)))
    false
  )
)

(define-read-only (is-expired (operation-id uint))
  (match (map-get? timelocked-operations operation-id)
    operation (and
      (not (get executed operation))
      (>= stacks-block-time (get expires-at operation)))
    false
  )
)

(define-read-only (get-time-remaining (operation-id uint))
  (match (map-get? timelocked-operations operation-id)
    operation (if (< stacks-block-time (get executable-at operation))
      (some (- (get executable-at operation) stacks-block-time))
      none)
    none
  )
)

(define-read-only (is-timelock-paused)
  (var-get timelock-paused)
)

;; Private Functions

(define-private (get-required-delay (operation-type uint))
  (if (is-eq operation-type OP-FEE-CHANGE)
    DELAY-SHORT
    (if (is-eq operation-type OP-PARAMETER-CHANGE)
      DELAY-MEDIUM
      (if (or (is-eq operation-type OP-ADMIN-CHANGE) (is-eq operation-type OP-UPGRADE))
        DELAY-LONG
        DELAY-MEDIUM)))
)

(define-private (get-impact-level (operation-type uint))
  (if (is-eq operation-type OP-FEE-CHANGE)
    u1  ;; Low impact
    (if (is-eq operation-type OP-PARAMETER-CHANGE)
      u2  ;; Medium impact
      u3  ;; High impact (admin change or upgrade)
    ))
)

;; Emergency override (use with extreme caution)
(define-public (emergency-execute (operation-id uint))
  (let (
    (operation (unwrap! (map-get? timelocked-operations operation-id) ERR-OPERATION-NOT-FOUND))
  )
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-UNAUTHORIZED)
    (asserts! (not (get executed operation)) ERR-OPERATION-EXECUTED)
    (asserts! (not (get cancelled operation)) ERR-OPERATION-CANCELLED)

    ;; Mark as executed (bypassing timelock)
    (map-set timelocked-operations operation-id
      (merge operation { executed: true }))

    ;; Emit warning event
    (print {
      event: "emergency-execution",
      operation-id: operation-id,
      executor: tx-sender,
      WARNING: "TIMELOCK BYPASSED",
      timestamp: stacks-block-time
    })

    (ok true)
  )
)

Functions (14)

FunctionAccessArgs
schedule-operationpublicoperation-type: uint, target-contract: principal, operation-data: (buff 256
execute-operationpublicoperation-id: uint
cancel-operationpublicoperation-id: uint
pause-timelockpublic
resume-timelockpublic
get-operationread-onlyoperation-id: uint
get-operation-metadataread-onlyoperation-id: uint
is-executableread-onlyoperation-id: uint
is-expiredread-onlyoperation-id: uint
get-time-remainingread-onlyoperation-id: uint
is-timelock-pausedread-only
get-required-delayprivateoperation-type: uint
get-impact-levelprivateoperation-type: uint
emergency-executepublicoperation-id: uint