Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Clarinet.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,35 @@ epoch = "3.0"
path = "contracts/manifesto.clar"
epoch = "3.0"

# Pegged DAO v2 contracts (no guardian council - reputation-governed)
[contracts.token-pegged]
path = "contracts/pegged/token-pegged.clar"
epoch = "3.0"

[contracts.dao-pegged]
path = "contracts/pegged/dao-pegged.clar"
epoch = "3.0"

[contracts.reputation-registry]
path = "contracts/pegged/reputation-registry.clar"
epoch = "3.0"

[contracts.auto-micro-payout]
path = "contracts/pegged/auto-micro-payout.clar"
epoch = "3.0"

[contracts.treasury-proposals]
path = "contracts/pegged/treasury-proposals.clar"
epoch = "3.0"

[contracts.upgrade-to-free-floating]
path = "contracts/pegged/upgrade-to-free-floating.clar"
epoch = "3.0"

[contracts.init-pegged-dao]
path = "contracts/proposals/init-pegged-dao.clar"
epoch = "3.0"

# Core contracts (independent multisig)
[contracts.dao-run-cost]
path = "contracts/core/dao-run-cost.clar"
Expand Down
214 changes: 214 additions & 0 deletions contracts/pegged/auto-micro-payout.clar
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
;; title: auto-micro-payout
;; version: 2.0.0
;; summary: Automatic micro-payouts for verified agent work (v2 - no guardians).
;; description: Pays 100-500 sats from treasury for verified work such as
;; check-ins and proofs. Verifies work against on-chain registries before paying.
;; No vote required. Rate-limited per agent per epoch.
;; v2: Removed guardian-approved work type. Only on-chain verified work qualifies.

;; TRAITS
(impl-trait .dao-traits.extension)

;; CONSTANTS
(define-constant SELF (as-contract tx-sender))
(define-constant MIN_PAYOUT u100) ;; 100 sats minimum
(define-constant MAX_PAYOUT u500) ;; 500 sats maximum
(define-constant MAX_PAYOUTS_PER_EPOCH u10) ;; max 10 payouts per agent per epoch
(define-constant EPOCH_LENGTH u4320) ;; ~30 days in blocks

;; Error codes (6200 range)
(define-constant ERR_NOT_AUTHORIZED (err u6200))
(define-constant ERR_INVALID_AMOUNT (err u6201))
(define-constant ERR_RATE_LIMITED (err u6202))
(define-constant ERR_INVALID_WORK_TYPE (err u6203))
(define-constant ERR_ALREADY_CLAIMED (err u6204))
(define-constant ERR_PAUSED (err u6205))
(define-constant ERR_WORK_NOT_VERIFIED (err u6206))

;; Work type constants (only on-chain verifiable types)
(define-constant WORK_TYPE_CHECKIN u1)
(define-constant WORK_TYPE_PROOF u2)

;; DATA VARS
(define-data-var paused bool false)
(define-data-var total-paid uint u0)
(define-data-var total-payouts uint u0)

;; DATA MAPS

;; Track payouts per agent per epoch
(define-map AgentEpochPayouts
{ agent: principal, epoch: uint }
uint
)

;; Track individual work claims to prevent double-payment
(define-map WorkClaims
{ agent: principal, work-type: uint, work-id: uint }
bool
)

;; Configurable payout amounts per work type
(define-map PayoutAmounts uint uint)

;; ============================================================
;; EXTENSION CALLBACK
;; ============================================================

(define-public (callback (sender principal) (memo (buff 34)))
(ok true)
)

;; ============================================================
;; INITIALIZATION
;; ============================================================

;; Set default payout amounts (called via init proposal)
(define-public (set-payout-amount (work-type uint) (amount uint))
(begin
(try! (is-dao-or-extension))
(asserts! (and (>= amount MIN_PAYOUT) (<= amount MAX_PAYOUT)) ERR_INVALID_AMOUNT)
(asserts! (and (>= work-type u1) (<= work-type u2)) ERR_INVALID_WORK_TYPE)
(map-set PayoutAmounts work-type amount)
(ok true)
)
)

;; ============================================================
;; CLAIM PAYOUT FOR VERIFIED WORK
;; ============================================================

;; Claim payout for a verified check-in
;; work-id = the check-in index from checkin-registry
(define-public (claim-checkin-payout (checkin-index uint))
(let
(
(agent tx-sender)
(current-epoch (get-current-epoch))
(epoch-payouts (get-agent-epoch-payouts agent current-epoch))
(payout-amount (get-payout-for-type WORK_TYPE_CHECKIN))
;; Verify the check-in exists on-chain for this agent
(checkin-data (contract-call? .checkin-registry get-checkin agent checkin-index))
)
(asserts! (not (var-get paused)) ERR_PAUSED)
(asserts! (> payout-amount u0) ERR_INVALID_AMOUNT)
(asserts! (< epoch-payouts MAX_PAYOUTS_PER_EPOCH) ERR_RATE_LIMITED)
;; Verify check-in actually exists for this agent
(asserts! (is-some checkin-data) ERR_WORK_NOT_VERIFIED)
;; Prevent double-claims
(asserts!
(map-insert WorkClaims { agent: agent, work-type: WORK_TYPE_CHECKIN, work-id: checkin-index } true)
ERR_ALREADY_CLAIMED
)
;; Update counters and pay
(map-set AgentEpochPayouts { agent: agent, epoch: current-epoch } (+ epoch-payouts u1))
(var-set total-paid (+ (var-get total-paid) payout-amount))
(var-set total-payouts (+ (var-get total-payouts) u1))
;; Hardcoded sBTC
(try! (contract-call? .dao-treasury withdraw-ft .mock-sbtc payout-amount agent))
(print {
notification: "auto-micro-payout/claim-checkin",
payload: { agent: agent, checkin-index: checkin-index, amount: payout-amount, epoch: current-epoch }
})
(ok payout-amount)
)
)

;; Claim payout for a verified proof submission
;; work-id = the proof index from proof-registry
(define-public (claim-proof-payout (proof-index uint))
(let
(
(agent tx-sender)
(current-epoch (get-current-epoch))
(epoch-payouts (get-agent-epoch-payouts agent current-epoch))
(payout-amount (get-payout-for-type WORK_TYPE_PROOF))
;; Verify the proof exists on-chain for this agent
(proof-data (contract-call? .proof-registry get-proof agent proof-index))
)
(asserts! (not (var-get paused)) ERR_PAUSED)
(asserts! (> payout-amount u0) ERR_INVALID_AMOUNT)
(asserts! (< epoch-payouts MAX_PAYOUTS_PER_EPOCH) ERR_RATE_LIMITED)
;; Verify proof actually exists for this agent
(asserts! (is-some proof-data) ERR_WORK_NOT_VERIFIED)
;; Prevent double-claims
(asserts!
(map-insert WorkClaims { agent: agent, work-type: WORK_TYPE_PROOF, work-id: proof-index } true)
ERR_ALREADY_CLAIMED
)
;; Update counters and pay
(map-set AgentEpochPayouts { agent: agent, epoch: current-epoch } (+ epoch-payouts u1))
(var-set total-paid (+ (var-get total-paid) payout-amount))
(var-set total-payouts (+ (var-get total-payouts) u1))
(try! (contract-call? .dao-treasury withdraw-ft .mock-sbtc payout-amount agent))
(print {
notification: "auto-micro-payout/claim-proof",
payload: { agent: agent, proof-index: proof-index, amount: payout-amount, epoch: current-epoch }
})
(ok payout-amount)
)
)

;; ============================================================
;; DAO GOVERNANCE
;; ============================================================

(define-public (set-paused (is-paused bool))
(begin
(try! (is-dao-or-extension))
(var-set paused is-paused)
(ok true)
)
)

;; ============================================================
;; READ-ONLY FUNCTIONS
;; ============================================================

(define-read-only (get-current-epoch)
(/ stacks-block-height EPOCH_LENGTH)
)

(define-read-only (get-agent-epoch-payouts (agent principal) (epoch uint))
(default-to u0 (map-get? AgentEpochPayouts { agent: agent, epoch: epoch }))
)

(define-read-only (get-payout-for-type (work-type uint))
(default-to u0 (map-get? PayoutAmounts work-type))
)

(define-read-only (has-claimed (agent principal) (work-type uint) (work-id uint))
(is-some (map-get? WorkClaims { agent: agent, work-type: work-type, work-id: work-id }))
)

(define-read-only (get-stats)
{
total-paid: (var-get total-paid),
total-payouts: (var-get total-payouts),
paused: (var-get paused),
current-epoch: (get-current-epoch)
}
)

(define-read-only (get-remaining-payouts (agent principal))
(let ((used (get-agent-epoch-payouts agent (get-current-epoch))))
(if (>= used MAX_PAYOUTS_PER_EPOCH)
u0
(- MAX_PAYOUTS_PER_EPOCH used)
)
)
)

;; ============================================================
;; PRIVATE FUNCTIONS
;; ============================================================

(define-private (is-dao-or-extension)
(ok (asserts!
(or
(is-eq tx-sender .base-dao)
(contract-call? .base-dao is-extension contract-caller)
)
ERR_NOT_AUTHORIZED
))
)
132 changes: 132 additions & 0 deletions contracts/pegged/dao-pegged.clar
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
;; title: dao-pegged
;; version: 2.0.0
;; summary: Main orchestrator for pegged agent DAOs (v2 - no guardians).
;; description: A simplified DAO entry point that wraps base-dao with
;; agent-friendly deploy and configuration. Manages the lifecycle from
;; Phase 1 (pegged, reputation-governed) to Phase 2 (free-floating, token-weighted).
;; v2: Removed guardian council references. Phase 1 governed by reputation-weighted proposals.

;; TRAITS
(impl-trait .dao-traits.extension)

;; CONSTANTS
(define-constant SELF (as-contract tx-sender))
(define-constant DEPLOYER tx-sender)

;; Error codes (6400 range)
(define-constant ERR_NOT_AUTHORIZED (err u6400))
(define-constant ERR_ALREADY_INITIALIZED (err u6401))

;; DATA VARS
(define-data-var dao-name (string-ascii 64) "Agent DAO")
(define-data-var phase uint u1) ;; 1 = pegged, 2 = free-floating
(define-data-var initialized bool false)
(define-data-var deployer-principal principal DEPLOYER)

;; ============================================================
;; EXTENSION CALLBACK
;; ============================================================

(define-public (callback (sender principal) (memo (buff 34)))
(ok true)
)

;; ============================================================
;; INITIALIZATION (called by init proposal)
;; ============================================================

;; Set DAO metadata during construction
(define-public (set-dao-name (name (string-ascii 64)))
(begin
(try! (is-dao-or-extension))
(var-set dao-name name)
(print {
notification: "dao-pegged/set-name",
payload: { name: name }
})
(ok true)
)
)

;; Mark as initialized
(define-public (mark-initialized)
(begin
(try! (is-dao-or-extension))
(asserts! (not (var-get initialized)) ERR_ALREADY_INITIALIZED)
(var-set initialized true)
(print {
notification: "dao-pegged/initialized",
payload: {
name: (var-get dao-name),
phase: (var-get phase),
deployer: (var-get deployer-principal)
}
})
(ok true)
)
)

;; ============================================================
;; PHASE MANAGEMENT
;; ============================================================

;; Advance to Phase 2 (called by upgrade-to-free-floating on successful vote)
(define-public (set-phase (new-phase uint))
(begin
(try! (is-dao-or-extension))
(asserts! (or (is-eq new-phase u1) (is-eq new-phase u2)) ERR_NOT_AUTHORIZED)
(var-set phase new-phase)
(print {
notification: "dao-pegged/phase-change",
payload: { phase: new-phase }
})
(ok true)
)
)

;; ============================================================
;; READ-ONLY FUNCTIONS
;; ============================================================

(define-read-only (get-dao-name)
(var-get dao-name)
)

(define-read-only (get-phase)
(var-get phase)
)

(define-read-only (is-phase-1)
(is-eq (var-get phase) u1)
)

(define-read-only (is-phase-2)
(is-eq (var-get phase) u2)
)

(define-read-only (is-initialized)
(var-get initialized)
)

(define-read-only (get-dao-info)
{
name: (var-get dao-name),
phase: (var-get phase),
initialized: (var-get initialized),
deployer: (var-get deployer-principal)
}
)

;; ============================================================
;; PRIVATE FUNCTIONS
;; ============================================================

(define-private (is-dao-or-extension)
(ok (asserts!
(or
(is-eq tx-sender .base-dao)
(contract-call? .base-dao is-extension contract-caller)
)
ERR_NOT_AUTHORIZED
))
)
Loading