;; TipStream Multisig
;; Multi-signature admin operations for secure platform management
;; Requires multiple signers to approve transactions before execution
(define-constant contract-owner tx-sender)
(define-constant err-not-signer (err u1100))
(define-constant err-already-signed (err u1101))
(define-constant err-not-found (err u1102))
(define-constant err-already-executed (err u1103))
(define-constant err-threshold-not-met (err u1104))
(define-constant err-invalid-params (err u1105))
(define-constant err-owner-only (err u1106))
(define-data-var signer-count uint u0)
(define-data-var required-signatures uint u2)
(define-data-var tx-nonce uint u0)
;; Authorized signers
(define-map signers principal bool)
;; Multi-sig transaction proposals
(define-map multisig-txs
{ tx-id: uint }
{
proposer: principal,
description: (string-utf8 200),
signatures: uint,
executed: bool,
action-type: (string-ascii 20),
action-value: uint
}
)
;; Track individual signatures per transaction
(define-map tx-signatures
{ tx-id: uint, signer: principal }
bool
)
;; ---------- Signer Management ----------
(define-public (add-signer (signer principal))
(begin
(asserts! (is-eq tx-sender contract-owner) err-owner-only)
(asserts! (not (is-signer signer)) err-invalid-params)
(map-set signers signer true)
(var-set signer-count (+ (var-get signer-count) u1))
(ok true)
)
)
(define-public (remove-signer (signer principal))
(begin
(asserts! (is-eq tx-sender contract-owner) err-owner-only)
(asserts! (is-signer signer) err-invalid-params)
(map-set signers signer false)
(var-set signer-count (- (var-get signer-count) u1))
(ok true)
)
)
(define-public (set-required-signatures (required uint))
(begin
(asserts! (is-eq tx-sender contract-owner) err-owner-only)
(asserts! (> required u0) err-invalid-params)
(asserts! (<= required (var-get signer-count)) err-invalid-params)
(var-set required-signatures required)
(ok true)
)
)
;; ---------- Transaction Lifecycle ----------
;; Propose a new multi-sig transaction (proposer auto-signs)
(define-public (propose-tx (description (string-utf8 200)) (action-type (string-ascii 20)) (action-value uint))
(let
(
(tx-id (var-get tx-nonce))
)
(asserts! (is-signer tx-sender) err-not-signer)
(map-set multisig-txs
{ tx-id: tx-id }
{
proposer: tx-sender,
description: description,
signatures: u1,
executed: false,
action-type: action-type,
action-value: action-value
}
)
(map-set tx-signatures { tx-id: tx-id, signer: tx-sender } true)
(var-set tx-nonce (+ tx-id u1))
(ok tx-id)
)
)
;; Sign a proposed transaction
(define-public (sign-tx (tx-id uint))
(let
(
(tx-data (unwrap! (map-get? multisig-txs { tx-id: tx-id }) err-not-found))
)
(asserts! (is-signer tx-sender) err-not-signer)
(asserts! (not (get executed tx-data)) err-already-executed)
(asserts! (not (has-signed tx-id tx-sender)) err-already-signed)
(map-set tx-signatures { tx-id: tx-id, signer: tx-sender } true)
(map-set multisig-txs
{ tx-id: tx-id }
(merge tx-data { signatures: (+ (get signatures tx-data) u1) })
)
(ok true)
)
)
;; Execute a transaction once signature threshold is met
(define-public (execute-tx (tx-id uint))
(let
(
(tx-data (unwrap! (map-get? multisig-txs { tx-id: tx-id }) err-not-found))
)
(asserts! (is-signer tx-sender) err-not-signer)
(asserts! (not (get executed tx-data)) err-already-executed)
(asserts! (>= (get signatures tx-data) (var-get required-signatures)) err-threshold-not-met)
(map-set multisig-txs
{ tx-id: tx-id }
(merge tx-data { executed: true })
)
(ok true)
)
)
;; ---------- Read-Only Queries ----------
(define-read-only (get-tx (tx-id uint))
(map-get? multisig-txs { tx-id: tx-id })
)
(define-read-only (get-tx-count)
(var-get tx-nonce)
)
(define-read-only (is-signer (who principal))
(default-to false (map-get? signers who))
)
(define-read-only (get-required-signatures)
(var-get required-signatures)
)
(define-read-only (get-signer-count)
(var-get signer-count)
)
(define-read-only (has-signed (tx-id uint) (signer principal))
(default-to false (map-get? tx-signatures { tx-id: tx-id, signer: signer }))
)