Source Code

;; Multi-Signature Wallet
;; Requires multiple signatures to execute transactions

(define-constant err-not-owner (err u100))
(define-constant err-insufficient-confirmations (err u101))
(define-constant err-tx-not-found (err u102))
(define-constant err-already-confirmed (err u103))
(define-constant err-already-executed (err u104))

(define-data-var required-confirmations uint u2)
(define-data-var tx-nonce uint u0)

(define-map owners principal bool)
(define-map transactions
    uint
    {
        to: principal,
        amount: uint,
        confirmations: uint,
        executed: bool
    }
)
(define-map confirmations {tx-id: uint, owner: principal} bool)

;; Initialize owners
(map-set owners tx-sender true)

;; Add owner (requires execution through multisig)
(define-public (add-owner (new-owner principal))
    (begin
        (asserts! (default-to false (map-get? owners tx-sender)) err-not-owner)
        (ok (map-set owners new-owner true))
    )
)

;; Submit a transaction
(define-public (submit-transaction (to principal) (amount uint))
    (let
        (
            (tx-id (var-get tx-nonce))
        )
        (asserts! (default-to false (map-get? owners tx-sender)) err-not-owner)
        
        (map-set transactions tx-id {
            to: to,
            amount: amount,
            confirmations: u1,
            executed: false
        })
        
        (map-set confirmations {tx-id: tx-id, owner: tx-sender} true)
        (var-set tx-nonce (+ tx-id u1))
        (ok tx-id)
    )
)

;; Confirm a transaction
(define-public (confirm-transaction (tx-id uint))
    (let
        (
            (tx (unwrap! (map-get? transactions tx-id) err-tx-not-found))
            (confirmation-key {tx-id: tx-id, owner: tx-sender})
        )
        (asserts! (default-to false (map-get? owners tx-sender)) err-not-owner)
        (asserts! (is-none (map-get? confirmations confirmation-key)) err-already-confirmed)
        (asserts! (not (get executed tx)) err-already-executed)
        
        (map-set confirmations confirmation-key true)
        (map-set transactions tx-id (merge tx {
            confirmations: (+ (get confirmations tx) u1)
        }))
        
        ;; Try to execute if threshold met
        (try! (execute-if-confirmed tx-id))
        (ok true)
    )
)

;; Execute transaction if enough confirmations
(define-private (execute-if-confirmed (tx-id uint))
    (let
        (
            (tx (unwrap! (map-get? transactions tx-id) err-tx-not-found))
        )
        (if (and 
                (>= (get confirmations tx) (var-get required-confirmations))
                (not (get executed tx)))
            (begin
                (try! (as-contract (stx-transfer? (get amount tx) tx-sender (get to tx))))
                (map-set transactions tx-id (merge tx {executed: true}))
                (ok true)
            )
            (ok false)
        )
    )
)

;; Revoke confirmation
(define-public (revoke-confirmation (tx-id uint))
    (let
        (
            (tx (unwrap! (map-get? transactions tx-id) err-tx-not-found))
            (confirmation-key {tx-id: tx-id, owner: tx-sender})
        )
        (asserts! (default-to false (map-get? owners tx-sender)) err-not-owner)
        (asserts! (not (get executed tx)) err-already-executed)
        (asserts! (is-some (map-get? confirmations confirmation-key)) err-already-confirmed)
        
        (map-delete confirmations confirmation-key)
        (map-set transactions tx-id (merge tx {
            confirmations: (- (get confirmations tx) u1)
        }))
        (ok true)
    )
)

;; Read-only functions
(define-read-only (get-transaction (tx-id uint))
    (map-get? transactions tx-id)
)

(define-read-only (is-owner (account principal))
    (default-to false (map-get? owners account))
)

(define-read-only (has-confirmed (tx-id uint) (owner principal))
    (is-some (map-get? confirmations {tx-id: tx-id, owner: owner}))
)

Functions (8)

FunctionAccessArgs
is-ownerread-onlyaccount: principal
add-ownerpublicnew-owner: principal
submit-transactionpublicto: principal, amount: uint
confirm-transactionpublictx-id: uint
execute-if-confirmedprivatetx-id: uint
revoke-confirmationpublictx-id: uint
get-transactionread-onlytx-id: uint
has-confirmedread-onlytx-id: uint, owner: principal