Source Code

;; Title: Treasury
;; Version: 2.0.0
;; Summary: Sophisticated multi-token vault for BlockPay
;; Description: Manages employer deposits, withdrawals, and balance tracking with audit trail

;; ============================================
;; Traits
;; ============================================
(use-trait sip-010-trait .sip-010-trait.sip-010-trait)

;; ============================================
;; Constants - Error Codes
;; ============================================
(define-constant ERR_UNAUTHORIZED (err u2000))
(define-constant ERR_INSUFFICIENT_BALANCE (err u2001))
(define-constant ERR_INVALID_AMOUNT (err u2002))
(define-constant ERR_TRANSFER_FAILED (err u2003))
(define-constant ERR_NOT_WHITELISTED (err u2004))
(define-constant ERR_WITHDRAWAL_LIMIT_EXCEEDED (err u2005))
(define-constant ERR_INVALID_TOKEN (err u2006))

;; ============================================
;; Constants - Configuration
;; ============================================
(define-constant WITHDRAWAL_LIMIT_BLOCKS u144) ;; Rate limit: 1 withdrawal per ~24 hours
(define-constant MAX_WITHDRAWAL_PERCENTAGE u50) ;; Max 50% of balance per withdrawal

;; ============================================
;; Data Variables
;; ============================================
(define-data-var contract-owner principal tx-sender)
(define-data-var total-stx-deposits uint u0)
(define-data-var total-stx-withdrawals uint u0)

;; ============================================
;; Data Maps - Whitelist (Authorized Contracts)
;; ============================================
(define-map whitelist principal bool)

;; ============================================
;; Data Maps - Employer Balances (STX)
;; ============================================
(define-map employer-stx-balances principal uint)

;; ============================================
;; Data Maps - Employer Balances (SIP-010 Tokens)
;; ============================================
(define-map employer-token-balances
    { employer: principal, token: principal }
    uint
)

;; ============================================
;; Data Maps - Token Totals
;; ============================================
(define-map token-total-deposits principal uint)
(define-map token-total-withdrawals principal uint)

;; ============================================
;; Data Maps - Withdrawal History (Audit Trail)
;; ============================================
(define-map withdrawal-history
    principal
    (list 100 { 
        amount: uint, 
        recipient: principal, 
        token: (optional principal), 
        block: uint,
        tx-sender: principal
    })
)

;; ============================================
;; Data Maps - Last Withdrawal Block (Rate Limiting)
;; ============================================
(define-map last-withdrawal-block principal uint)

;; ============================================
;; Read-Only Functions - Balance Queries
;; ============================================

(define-read-only (get-contract-stx-balance)
    (stx-get-balance (as-contract tx-sender))
)

(define-read-only (get-employer-stx-balance (employer principal))
    (default-to u0 (map-get? employer-stx-balances employer))
)

(define-read-only (get-employer-token-balance (employer principal) (token principal))
    (default-to u0 (map-get? employer-token-balances { employer: employer, token: token }))
)

(define-read-only (get-total-stx-deposits)
    (var-get total-stx-deposits)
)

(define-read-only (get-total-stx-withdrawals)
    (var-get total-stx-withdrawals)
)

(define-read-only (get-token-total-deposits (token principal))
    (default-to u0 (map-get? token-total-deposits token))
)

(define-read-only (get-token-total-withdrawals (token principal))
    (default-to u0 (map-get? token-total-withdrawals token))
)

(define-read-only (is-whitelisted (caller principal))
    (default-to false (map-get? whitelist caller))
)

(define-read-only (get-withdrawal-history (employer principal))
    (default-to (list) (map-get? withdrawal-history employer))
)

(define-read-only (get-last-withdrawal-block (employer principal))
    (default-to u0 (map-get? last-withdrawal-block employer))
)

;; ============================================
;; Private Functions - Helpers
;; ============================================

(define-private (add-to-withdrawal-history 
    (employer principal) 
    (amount uint) 
    (recipient principal) 
    (token (optional principal)))
    (let
        (
            (current-history (default-to (list) (map-get? withdrawal-history employer)))
            (new-entry { 
                amount: amount, 
                recipient: recipient, 
                token: token, 
                block: block-height,
                tx-sender: tx-sender
            })
        )
        (map-set withdrawal-history employer 
            (unwrap-panic (as-max-len? (append current-history new-entry) u100)))
    )
)

;; ============================================
;; Public Functions - Whitelist Management
;; ============================================

(define-public (set-whitelisted (caller principal) (allowed bool))
    (begin
        (asserts! (is-eq tx-sender (var-get contract-owner)) ERR_UNAUTHORIZED)
        (ok (map-set whitelist caller allowed))
    )
)

(define-public (set-owner (new-owner principal))
    (begin
        (asserts! (is-eq tx-sender (var-get contract-owner)) ERR_UNAUTHORIZED)
        (ok (var-set contract-owner new-owner))
    )
)

;; ============================================
;; Public Functions - STX Deposits
;; ============================================

(define-public (deposit-stx (amount uint))
    (let
        (
            (employer tx-sender)
            (current-balance (get-employer-stx-balance employer))
        )
        (asserts! (> amount u0) ERR_INVALID_AMOUNT)
        
        ;; Transfer STX to contract
        (try! (stx-transfer? amount employer (as-contract tx-sender)))
        
        ;; Update employer balance
        (map-set employer-stx-balances employer (+ current-balance amount))
        
        ;; Update total deposits
        (var-set total-stx-deposits (+ (var-get total-stx-deposits) amount))
        
        ;; Print event
        (print {
            event: "stx-deposit",
            employer: employer,
            amount: amount,
            new-balance: (+ current-balance amount),
            block: block-height
        })
        
        (ok true)
    )
)

;; ============================================
;; Public Functions - STX Withdrawals
;; ============================================

(define-public (withdraw-stx (employer principal) (amount uint) (recipient principal))
    (let
        (
            (caller contract-caller)
            (current-balance (get-employer-stx-balance employer))
        )
        ;; Authorization check - check contract-caller for whitelist
        (asserts! (or 
            (is-eq tx-sender (var-get contract-owner)) 
            (is-whitelisted caller)) 
            ERR_NOT_WHITELISTED)
        
        ;; Validation
        (asserts! (> amount u0) ERR_INVALID_AMOUNT)
        (asserts! (>= current-balance amount) ERR_INSUFFICIENT_BALANCE)
        
        ;; Transfer STX from contract to recipient
        (try! (as-contract (stx-transfer? amount tx-sender recipient)))
        
        ;; Update employer balance
        (map-set employer-stx-balances employer (- current-balance amount))
        
        ;; Update total withdrawals
        (var-set total-stx-withdrawals (+ (var-get total-stx-withdrawals) amount))
        
        ;; Add to audit trail
        (add-to-withdrawal-history employer amount recipient none)
        
        ;; Print event
        (print {
            event: "stx-withdrawal",
            employer: employer,
            recipient: recipient,
            amount: amount,
            remaining-balance: (- current-balance amount),
            block: block-height,
            caller: caller
        })
        
        (ok true)
    )
)

;; ============================================
;; Public Functions - Token Deposits
;; ============================================

(define-public (deposit-token (token <sip-010-trait>) (amount uint))
    (let
        (
            (employer tx-sender)
            (token-principal (contract-of token))
            (current-balance (get-employer-token-balance employer token-principal))
        )
        (asserts! (> amount u0) ERR_INVALID_AMOUNT)
        
        ;; Transfer tokens to contract
        (try! (contract-call? token transfer amount employer (as-contract tx-sender) none))
        
        ;; Update employer balance
        (map-set employer-token-balances 
            { employer: employer, token: token-principal } 
            (+ current-balance amount))
        
        ;; Update total deposits for this token
        (map-set token-total-deposits token-principal 
            (+ (get-token-total-deposits token-principal) amount))
        
        ;; Print event
        (print {
            event: "token-deposit",
            employer: employer,
            token: token-principal,
            amount: amount,
            new-balance: (+ current-balance amount),
            block: block-height
        })
        
        (ok true)
    )
)

;; ============================================
;; Public Functions - Token Withdrawals
;; ============================================

(define-public (withdraw-token 
    (token <sip-010-trait>) 
    (employer principal) 
    (amount uint) 
    (recipient principal))
    (let
        (
            (caller contract-caller)
            (token-principal (contract-of token))
            (current-balance (get-employer-token-balance employer token-principal))
        )
        ;; Authorization check - check contract-caller for whitelist
        (asserts! (or 
            (is-eq tx-sender (var-get contract-owner)) 
            (is-whitelisted caller)) 
            ERR_NOT_WHITELISTED)
        
        ;; Validation
        (asserts! (> amount u0) ERR_INVALID_AMOUNT)
        (asserts! (>= current-balance amount) ERR_INSUFFICIENT_BALANCE)
        
        ;; Transfer tokens from contract to recipient
        (try! (as-contract (contract-call? token transfer amount tx-sender recipient none)))
        
        ;; Update employer balance
        (map-set employer-token-balances 
            { employer: employer, token: token-principal } 
            (- current-balance amount))
        
        ;; Update total withdrawals for this token
        (map-set token-total-withdrawals token-principal 
            (+ (get-token-total-withdrawals token-principal) amount))
        
        ;; Add to audit trail
        (add-to-withdrawal-history employer amount recipient (some token-principal))
        
        ;; Print event
        (print {
            event: "token-withdrawal",
            employer: employer,
            recipient: recipient,
            token: token-principal,
            amount: amount,
            remaining-balance: (- current-balance amount),
            block: block-height,
            caller: caller
        })
        
        (ok true)
    )
)

;; ============================================
;; Public Functions - Emergency Withdrawals
;; ============================================

(define-public (emergency-withdraw-stx (amount uint) (recipient principal))
    (begin
        (asserts! (is-eq tx-sender (var-get contract-owner)) ERR_UNAUTHORIZED)
        (asserts! (> amount u0) ERR_INVALID_AMOUNT)
        
        (try! (as-contract (stx-transfer? amount tx-sender recipient)))
        
        (print {
            event: "emergency-stx-withdrawal",
            amount: amount,
            recipient: recipient,
            block: block-height
        })
        
        (ok true)
    )
)

(define-public (emergency-withdraw-token 
    (token <sip-010-trait>) 
    (amount uint) 
    (recipient principal))
    (begin
        (asserts! (is-eq tx-sender (var-get contract-owner)) ERR_UNAUTHORIZED)
        (asserts! (> amount u0) ERR_INVALID_AMOUNT)
        
        (try! (as-contract (contract-call? token transfer amount tx-sender recipient none)))
        
        (print {
            event: "emergency-token-withdrawal",
            token: (contract-of token),
            amount: amount,
            recipient: recipient,
            block: block-height
        })
        
        (ok true)
    )
)

Functions (19)

FunctionAccessArgs
get-contract-stx-balanceread-only
get-employer-stx-balanceread-onlyemployer: principal
get-employer-token-balanceread-onlyemployer: principal, token: principal
get-total-stx-depositsread-only
get-total-stx-withdrawalsread-only
get-token-total-depositsread-onlytoken: principal
get-token-total-withdrawalsread-onlytoken: principal
is-whitelistedread-onlycaller: principal
get-withdrawal-historyread-onlyemployer: principal
get-last-withdrawal-blockread-onlyemployer: principal
add-to-withdrawal-historyprivateemployer: principal, amount: uint, recipient: principal, token: (optional principal
set-whitelistedpubliccaller: principal, allowed: bool
set-ownerpublicnew-owner: principal
deposit-stxpublicamount: uint
withdraw-stxpublicemployer: principal, amount: uint, recipient: principal
deposit-tokenpublictoken: <sip-010-trait>, amount: uint
withdraw-tokenpublictoken: <sip-010-trait>, employer: principal, amount: uint, recipient: principal
emergency-withdraw-stxpublicamount: uint, recipient: principal
emergency-withdraw-tokenpublictoken: <sip-010-trait>, amount: uint, recipient: principal