Source Code

;; StackSusu Emergency v7
;; Emergency withdrawal handling

(define-constant CONTRACT-OWNER tx-sender)

;; Error constants
(define-constant ERR-NOT-AUTHORIZED (err u4000))
(define-constant ERR-CIRCLE-NOT-FOUND (err u4001))
(define-constant ERR-NOT-MEMBER (err u4002))
(define-constant ERR-ALREADY-REQUESTED (err u4003))
(define-constant ERR-NO-REQUEST (err u4004))
(define-constant ERR-COOLDOWN (err u4005))
(define-constant ERR-PAUSED (err u4006))

;; Emergency request status
(define-constant STATUS-PENDING u0)
(define-constant STATUS-APPROVED u1)
(define-constant STATUS-DENIED u2)
(define-constant STATUS-PROCESSED u3)

;; Configuration
(define-constant EMERGENCY-COOLDOWN u144) ;; ~1 day

;; Emergency requests
(define-map emergency-requests
  { circle-id: uint, member: principal }
  {
    reason: (string-ascii 200),
    amount-requested: uint,
    status: uint,
    created-at: uint,
    processed-at: uint
  }
)

;; Stats
(define-data-var total-emergency-requests uint u0)
(define-data-var total-emergency-payouts uint u0)


;; ============================================
;; Emergency Functions
;; ============================================

(define-public (request-emergency-payout (circle-id uint) (reason (string-ascii 200)))
  (let
    (
      (member tx-sender)
      (member-balance (unwrap! (contract-call? .stacksusu-escrow-v7 get-member-balance circle-id member) ERR-CIRCLE-NOT-FOUND))
      (existing-request (map-get? emergency-requests { circle-id: circle-id, member: member }))
    )
    (asserts! (not (contract-call? .stacksusu-admin-v7 is-paused)) ERR-PAUSED)
    (asserts! (> member-balance u0) ERR-NOT-MEMBER)
    
    ;; Check for existing pending request
    (match existing-request
      req (asserts! (or 
            (is-eq (get status req) STATUS-PROCESSED)
            (is-eq (get status req) STATUS-DENIED)
            (> (- block-height (get created-at req)) EMERGENCY-COOLDOWN))
          ERR-ALREADY-REQUESTED)
      true
    )
    
    ;; Create request
    (map-set emergency-requests { circle-id: circle-id, member: member } {
      reason: reason,
      amount-requested: member-balance,
      status: STATUS-PENDING,
      created-at: block-height,
      processed-at: u0
    })
    
    (var-set total-emergency-requests (+ (var-get total-emergency-requests) u1))
    
    (ok true)
  )
)

(define-public (process-emergency-request (circle-id uint) (member principal) (approve bool))
  (let
    (
      (request (unwrap! (map-get? emergency-requests { circle-id: circle-id, member: member }) ERR-NO-REQUEST))
    )
    ;; Only contract owner can process
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (asserts! (is-eq (get status request) STATUS-PENDING) ERR-ALREADY-REQUESTED)
    
    (if approve
      (begin
        ;; Process withdrawal through escrow
        (try! (as-contract (contract-call? .stacksusu-escrow-v7 emergency-withdraw circle-id (get amount-requested request))))
        
        (map-set emergency-requests { circle-id: circle-id, member: member }
          (merge request { status: STATUS-PROCESSED, processed-at: block-height }))
        
        (var-set total-emergency-payouts (+ (var-get total-emergency-payouts) (get amount-requested request)))
        (ok true)
      )
      (begin
        (map-set emergency-requests { circle-id: circle-id, member: member }
          (merge request { status: STATUS-DENIED, processed-at: block-height }))
        (ok false)
      )
    )
  )
)

;; Self-service emergency withdraw (no approval needed, higher fee)
(define-public (self-emergency-withdraw (circle-id uint))
  (let
    (
      (member tx-sender)
      (member-balance (unwrap! (contract-call? .stacksusu-escrow-v7 get-member-balance circle-id member) ERR-CIRCLE-NOT-FOUND))
    )
    (asserts! (not (contract-call? .stacksusu-admin-v7 is-paused)) ERR-PAUSED)
    (asserts! (> member-balance u0) ERR-NOT-MEMBER)
    
    ;; Direct withdraw from escrow (applies emergency fee)
    (try! (contract-call? .stacksusu-escrow-v7 emergency-withdraw circle-id member-balance))
    
    (var-set total-emergency-payouts (+ (var-get total-emergency-payouts) member-balance))
    
    (ok member-balance)
  )
)


;; ============================================
;; Read Functions
;; ============================================

(define-read-only (get-emergency-request (circle-id uint) (member principal))
  (map-get? emergency-requests { circle-id: circle-id, member: member })
)

(define-read-only (get-emergency-stats)
  {
    total-requests: (var-get total-emergency-requests),
    total-payouts: (var-get total-emergency-payouts)
  }
)

(define-read-only (has-pending-request (circle-id uint) (member principal))
  (match (map-get? emergency-requests { circle-id: circle-id, member: member })
    req (is-eq (get status req) STATUS-PENDING)
    false
  )
)

Functions (6)

FunctionAccessArgs
request-emergency-payoutpubliccircle-id: uint, reason: (string-ascii 200
process-emergency-requestpubliccircle-id: uint, member: principal, approve: bool
self-emergency-withdrawpubliccircle-id: uint
get-emergency-requestread-onlycircle-id: uint, member: principal
get-emergency-statsread-only
has-pending-requestread-onlycircle-id: uint, member: principal