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
4 changes: 4 additions & 0 deletions DAO/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

settings/Mainnet.toml
settings/Testnet.toml
history.txt
4 changes: 4 additions & 0 deletions DAO/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

{
"deno.enable": true,
}
18 changes: 18 additions & 0 deletions DAO/.vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

{
"version": "2.0.0",
"tasks": [
{
"label": "check contracts",
"group": "test",
"type": "shell",
"command": "clarinet check"
},
{
"label": "test contracts",
"group": "test",
"type": "shell",
"command": "clarinet test"
}
]
}
11 changes: 11 additions & 0 deletions DAO/Clarinet.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[project]
name = "DAO"
authors = []
description = ""
telemetry = true
requirements = []
analysis = ["check_checker"]
costs_version = 2
[contracts.treasury]
path = "contracts/treasury.clar"
depends_on = []
221 changes: 221 additions & 0 deletions DAO/contracts/treasury.clar
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
;; Bitcoin Treasury DAO Smart Contract
;; A decentralized autonomous organization for managing Bitcoin treasury

;; Constants
(define-constant CONTRACT_OWNER tx-sender)
(define-constant ERR_UNAUTHORIZED (err u100))
(define-constant ERR_INSUFFICIENT_BALANCE (err u101))
(define-constant ERR_PROPOSAL_NOT_FOUND (err u102))
(define-constant ERR_ALREADY_VOTED (err u103))
(define-constant ERR_PROPOSAL_EXPIRED (err u104))
(define-constant ERR_PROPOSAL_NOT_PASSED (err u105))
(define-constant ERR_ALREADY_EXECUTED (err u106))
(define-constant ERR_INVALID_AMOUNT (err u107))
(define-constant ERR_MEMBER_EXISTS (err u108))
(define-constant ERR_NOT_MEMBER (err u109))
(define-constant ERR_INVALID_QUORUM (err u110))

;; Data Variables
(define-data-var proposal-counter uint u0)
(define-data-var quorum-percentage uint u51)
(define-data-var voting-period uint u1440)
(define-data-var total-members uint u0)

;; Data Maps
(define-map members principal bool)
(define-map member-voting-power principal uint)
(define-map proposals
uint
{
proposer: principal,
recipient: principal,
amount: uint,
description: (string-ascii 256),
yes-votes: uint,
no-votes: uint,
executed: bool,
created-at: uint,
expires-at: uint
}
)
(define-map votes {proposal-id: uint, voter: principal} bool)
(define-map member-proposals principal (list 50 uint))

;; Read-only functions
(define-read-only (get-proposal (proposal-id uint))
(map-get? proposals proposal-id)
)

(define-read-only (get-member-status (account principal))
(default-to false (map-get? members account))
)

(define-read-only (get-voting-power (account principal))
(default-to u0 (map-get? member-voting-power account))
)

(define-read-only (has-voted (proposal-id uint) (voter principal))
(default-to false (map-get? votes {proposal-id: proposal-id, voter: voter}))
)

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

(define-read-only (get-quorum)
(var-get quorum-percentage)
)

(define-read-only (get-voting-period)
(var-get voting-period)
)

(define-read-only (get-total-members)
(var-get total-members)
)

(define-read-only (calculate-quorum-votes)
(let ((total-power (var-get total-members)))
(/ (* total-power (var-get quorum-percentage)) u100)
)
)

(define-read-only (is-proposal-passed (proposal-id uint))
(match (get-proposal proposal-id)
proposal
(let (
(total-votes (+ (get yes-votes proposal) (get no-votes proposal)))
(required-quorum (calculate-quorum-votes))
)
(and
(>= total-votes required-quorum)
(> (get yes-votes proposal) (get no-votes proposal))
)
)
false
)
)

;; Public functions
(define-public (add-member (new-member principal) (voting-power uint))
(begin
(asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_UNAUTHORIZED)
(asserts! (not (get-member-status new-member)) ERR_MEMBER_EXISTS)
(asserts! (> voting-power u0) ERR_INVALID_AMOUNT)
(map-set members new-member true)
(map-set member-voting-power new-member voting-power)
(var-set total-members (+ (var-get total-members) u1))
(ok true)
)
)

(define-public (remove-member (member principal))
(begin
(asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_UNAUTHORIZED)
(asserts! (get-member-status member) ERR_NOT_MEMBER)
(map-delete members member)
(map-delete member-voting-power member)
(var-set total-members (- (var-get total-members) u1))
(ok true)
)
)

(define-public (deposit (amount uint))
(begin
(asserts! (> amount u0) ERR_INVALID_AMOUNT)
(try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
(ok true)
)
)

(define-public (create-proposal (recipient principal) (amount uint) (description (string-ascii 256)))
(let (
(proposal-id (+ (var-get proposal-counter) u1))
(current-block block-height)
(expires-at (+ current-block (var-get voting-period)))
)
(asserts! (get-member-status tx-sender) ERR_NOT_MEMBER)
(asserts! (> amount u0) ERR_INVALID_AMOUNT)
(asserts! (<= amount (get-treasury-balance)) ERR_INSUFFICIENT_BALANCE)
(map-set proposals proposal-id {
proposer: tx-sender,
recipient: recipient,
amount: amount,
description: description,
yes-votes: u0,
no-votes: u0,
executed: false,
created-at: current-block,
expires-at: expires-at
})
(var-set proposal-counter proposal-id)
(ok proposal-id)
)
)

(define-public (vote (proposal-id uint) (support bool))
(let (
(proposal (unwrap! (get-proposal proposal-id) ERR_PROPOSAL_NOT_FOUND))
(voter-power (get-voting-power tx-sender))
)
(asserts! (get-member-status tx-sender) ERR_NOT_MEMBER)
(asserts! (not (has-voted proposal-id tx-sender)) ERR_ALREADY_VOTED)
(asserts! (<= block-height (get expires-at proposal)) ERR_PROPOSAL_EXPIRED)
(map-set votes {proposal-id: proposal-id, voter: tx-sender} true)
(if support
(map-set proposals proposal-id (merge proposal {yes-votes: (+ (get yes-votes proposal) voter-power)}))
(map-set proposals proposal-id (merge proposal {no-votes: (+ (get no-votes proposal) voter-power)}))
)
(ok true)
)
)

(define-public (execute-proposal (proposal-id uint))
(let (
(proposal (unwrap! (get-proposal proposal-id) ERR_PROPOSAL_NOT_FOUND))
)
(asserts! (get-member-status tx-sender) ERR_NOT_MEMBER)
(asserts! (> block-height (get expires-at proposal)) ERR_PROPOSAL_EXPIRED)
(asserts! (not (get executed proposal)) ERR_ALREADY_EXECUTED)
(asserts! (is-proposal-passed proposal-id) ERR_PROPOSAL_NOT_PASSED)
(try! (as-contract (stx-transfer? (get amount proposal) tx-sender (get recipient proposal))))
(map-set proposals proposal-id (merge proposal {executed: true}))
(ok true)
)
)

(define-public (update-quorum (new-quorum uint))
(begin
(asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_UNAUTHORIZED)
(asserts! (and (> new-quorum u0) (<= new-quorum u100)) ERR_INVALID_QUORUM)
(var-set quorum-percentage new-quorum)
(ok true)
)
)

(define-public (update-voting-period (new-period uint))
(begin
(asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_UNAUTHORIZED)
(asserts! (> new-period u0) ERR_INVALID_AMOUNT)
(var-set voting-period new-period)
(ok true)
)
)

;; Emergency withdraw (only owner)
(define-public (emergency-withdraw (amount uint) (recipient principal))
(begin
(asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_UNAUTHORIZED)
(asserts! (> amount u0) ERR_INVALID_AMOUNT)
(asserts! (<= amount (get-treasury-balance)) ERR_INSUFFICIENT_BALANCE)
(try! (as-contract (stx-transfer? amount tx-sender recipient)))
(ok true)
)
)

;; Initialize contract with initial member
(begin
(map-set members CONTRACT_OWNER true)
(map-set member-voting-power CONTRACT_OWNER u1)
(var-set total-members u1)
)
127 changes: 127 additions & 0 deletions DAO/settings/Devnet.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
[network]
name = "devnet"
deployment_fee_rate = 10

[accounts.deployer]
mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw"
balance = 100_000_000_000_000
# secret_key: 753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601
# stx_address: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM
# btc_address: mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH

[accounts.wallet_1]
mnemonic = "sell invite acquire kitten bamboo drastic jelly vivid peace spawn twice guilt pave pen trash pretty park cube fragile unaware remain midnight betray rebuild"
balance = 100_000_000_000_000
# secret_key: 7287ba251d44a4d3fd9276c88ce34c5c52a038955511cccaf77e61068649c17801
# stx_address: ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5
# btc_address: mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC

[accounts.wallet_2]
mnemonic = "hold excess usual excess ring elephant install account glad dry fragile donkey gaze humble truck breeze nation gasp vacuum limb head keep delay hospital"
balance = 100_000_000_000_000
# secret_key: 530d9f61984c888536871c6573073bdfc0058896dc1adfe9a6a10dfacadc209101
# stx_address: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG
# btc_address: muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG

[accounts.wallet_3]
mnemonic = "cycle puppy glare enroll cost improve round trend wrist mushroom scorpion tower claim oppose clever elephant dinosaur eight problem before frozen dune wagon high"
balance = 100_000_000_000_000
# secret_key: d655b2523bcd65e34889725c73064feb17ceb796831c0e111ba1a552b0f31b3901
# stx_address: ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC
# btc_address: mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7

[accounts.wallet_4]
mnemonic = "board list obtain sugar hour worth raven scout denial thunder horse logic fury scorpion fold genuine phrase wealth news aim below celery when cabin"
balance = 100_000_000_000_000
# secret_key: f9d7206a47f14d2870c163ebab4bf3e70d18f5d14ce1031f3902fbbc894fe4c701
# stx_address: ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND
# btc_address: mg1C76bNTutiCDV3t9nWhZs3Dc8LzUufj8

[accounts.wallet_5]
mnemonic = "hurry aunt blame peanut heavy update captain human rice crime juice adult scale device promote vast project quiz unit note reform update climb purchase"
balance = 100_000_000_000_000
# secret_key: 3eccc5dac8056590432db6a35d52b9896876a3d5cbdea53b72400bc9c2099fe801
# stx_address: ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB
# btc_address: mweN5WVqadScHdA81aATSdcVr4B6dNokqx

[accounts.wallet_6]
mnemonic = "area desk dutch sign gold cricket dawn toward giggle vibrant indoor bench warfare wagon number tiny universe sand talk dilemma pottery bone trap buddy"
balance = 100_000_000_000_000
# secret_key: 7036b29cb5e235e5fd9b09ae3e8eec4404e44906814d5d01cbca968a60ed4bfb01
# stx_address: ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0
# btc_address: mzxXgV6e4BZSsz8zVHm3TmqbECt7mbuErt

[accounts.wallet_7]
mnemonic = "prevent gallery kind limb income control noise together echo rival record wedding sense uncover school version force bleak nuclear include danger skirt enact arrow"
balance = 100_000_000_000_000
# secret_key: b463f0df6c05d2f156393eee73f8016c5372caa0e9e29a901bb7171d90dc4f1401
# stx_address: ST3PF13W7Z0RRM42A8VZRVFQ75SV1K26RXEP8YGKJ
# btc_address: n37mwmru2oaVosgfuvzBwgV2ysCQRrLko7

[accounts.wallet_8]
mnemonic = "female adjust gallery certain visit token during great side clown fitness like hurt clip knife warm bench start reunion globe detail dream depend fortune"
balance = 100_000_000_000_000
# secret_key: 6a1a754ba863d7bab14adbbc3f8ebb090af9e871ace621d3e5ab634e1422885e01
# stx_address: ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP
# btc_address: n2v875jbJ4RjBnTjgbfikDfnwsDV5iUByw

[accounts.wallet_9]
mnemonic = "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform"
balance = 100_000_000_000_000
# secret_key: de433bdfa14ec43aa1098d5be594c8ffb20a31485ff9de2923b2689471c401b801
# stx_address: STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6
# btc_address: mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d

[devnet]
disable_bitcoin_explorer = true
# disable_stacks_explorer = true
# disable_stacks_api = true
# working_dir = "tmp/devnet"
# stacks_node_events_observers = ["host.docker.internal:8002"]
# miner_mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw"
# miner_derivation_path = "m/44'/5757'/0'/0/0"
# orchestrator_port = 20445
# bitcoin_node_p2p_port = 18444
# bitcoin_node_rpc_port = 18443
# bitcoin_node_username = "devnet"
# bitcoin_node_password = "devnet"
# bitcoin_controller_port = 18442
# bitcoin_controller_block_time = 30_000
# stacks_node_rpc_port = 20443
# stacks_node_p2p_port = 20444
# stacks_api_port = 3999
# stacks_api_events_port = 3700
# bitcoin_explorer_port = 8001
# stacks_explorer_port = 8000
# postgres_port = 5432
# postgres_username = "postgres"
# postgres_password = "postgres"
# postgres_database = "postgres"
# bitcoin_node_image_url = "quay.io/hirosystems/bitcoind:devnet"
# stacks_node_image_url = "localhost:5000/stacks-node:devnet"
# stacks_api_image_url = "blockstack/stacks-blockchain-api:latest"
# stacks_explorer_image_url = "blockstack/explorer:latest"
# bitcoin_explorer_image_url = "quay.io/hirosystems/bitcoin-explorer:devnet"
# postgres_image_url = "postgres:alpine"

# Send some stacking orders
[[devnet.pox_stacking_orders]]
start_at_cycle = 3
duration = 12
wallet = "wallet_1"
slots = 2
btc_address = "mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC"

[[devnet.pox_stacking_orders]]
start_at_cycle = 3
duration = 12
wallet = "wallet_2"
slots = 1
btc_address = "muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG"

[[devnet.pox_stacking_orders]]
start_at_cycle = 3
duration = 12
wallet = "wallet_3"
slots = 1
btc_address = "mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7"
Loading