feat(dao): scaffold aldabra-dao crate (Phase 1 reads)

Adds a 4th workspace crate `aldabra-dao` for native Agora-on-Cardano
DAO interaction. Multi-DAO from day one — DaoConfig per DAO at
\$ALDABRA_DATA/daos/<name>.json + .active selector. Sulkta DAO and any
community Agora deployment (Bob's DAO, Alice's DAO) are first-class.

Phase 0 type port complete:
- StakeDatum, StakeRedeemer, ProposalAction, ProposalLock, Credential
- ProposalDatum, ProposalRedeemer, ProposalStatus, ProposalThresholds,
  ProposalTimingConfig, ProposalVotes
- GovernorDatum, GovernorRedeemer
- All Constr indices verified against Agora source makeIsDataIndexed
  + EnumIsData declarations (Stake/Proposal/Governor/Action/Status all
  cross-referenced)
- Round-trip tests for every type

Phase 1 read surface (this commit):
- DaoStore: DaoConfig load/save/list/remove + active-DAO selector with
  first-register-becomes-active UX. 8 unit tests.
- DaoReader trait + KoiosDaoReader impl for get_governor + list_stakes.
  list_proposals stubbed pending Phase 4 proposal-script-address discovery.
- Stake address sharing handled: list_stakes filters on gov_token_policy
  (the shared MLabs stakes addr serves many DAOs).

Stubs for upcoming phases:
- agora/treasury.rs (Phase 4 — treasury spend helpers)
- agora/authority_token.rs (Phase 4 — GAT mint/burn)
- agora/reference_scripts.rs (Phase 2/3 — independent script-hash discovery
  per Sulkta's choice 2026-05-05; computed locally, never trust MLabs registry)
- builder/mod.rs (per-operation Plutus tx builders, populated phase-by-phase)

Spec doc + decisions: internal notes in workspace.

Effects map (`ProposalDatum.effects`) kept as raw PlutusData for round-trip
integrity until Phase 4 (proposal create) needs typed access.

ExUnits strategy locked: Koios tx_evaluate from day one (no hardcoded values).
Wired up in Phase 2 alongside reference-script discovery.
This commit is contained in:
Sulkta 2026-05-05 13:40:12 -07:00
parent 0a658cc4de
commit 50658ea51b
15 changed files with 2018 additions and 3 deletions

View file

@ -0,0 +1,79 @@
# aldabra-dao — Agora-on-Cardano DAO interaction.
#
# This crate is a community-publishable, multi-DAO client for any
# Agora deployment. Bob's DAO and Alice's DAO are both first-class —
# nothing is hardcoded to any single DAO.
#
# Layout:
# config — per-DAO config files at $ALDABRA_DATA/daos/<name>.json
# + .active selector. Loaded fresh on every tool call so
# add/remove/switch take effect without daemon restart.
# agora — Plutarch type ports (StakeDatum, ProposalDatum, etc) with
# PlutusData encode/decode. One module per Agora module.
# reader — Read-only Koios-backed state queries for governor /
# stakes / proposals UTxOs. Decodes datums into typed Rust.
# builder — Plutus tx assembly per operation (stake_create,
# proposal_vote, etc). Each operation is its own file
# for readability.
# error — Crate-internal error type.
#
# Boundary rules:
# - We depend on aldabra-core for crypto / signing / address ops only.
# - We depend on aldabra-chain for raw Koios queries.
# - We do NOT touch keys directly; signing is delegated to aldabra-core.
# - We do NOT do MCP. The dao_* MCP tools live in aldabra-mcp.
#
# Why a separate crate (not just a module under aldabra-core):
# - DAO ops are a separate auditable surface from the core wallet.
# - Community users can depend on aldabra-dao without pulling in the
# full MCP binary.
# - Plutus DAO tx assembly is enough code that mixing it with raw
# wallet sends would bloat aldabra-core past the auditability threshold.
[package]
name = "aldabra-dao"
version.workspace = true
edition.workspace = true
license-file.workspace = true
repository.workspace = true
authors.workspace = true
[dependencies]
aldabra-core = { path = "../aldabra-core" }
aldabra-chain = { path = "../aldabra-chain" }
# Pallas — PlutusData encode/decode + tx building + addresses.
pallas-primitives = { workspace = true }
pallas-codec = { workspace = true }
pallas-crypto = { workspace = true }
pallas-addresses = { workspace = true }
pallas-txbuilder = { workspace = true }
pallas-traverse = { workspace = true }
# Async + I/O for chain reads.
tokio = { workspace = true }
async-trait = "0.1"
reqwest = { workspace = true }
# Serde for DaoConfig persistence + Koios JSON.
serde = { workspace = true }
serde_json = { workspace = true }
# Bech32 for parsing addresses we don't get pre-decoded.
bech32 = "0.9"
# Hex for handling token names + script hashes.
hex = "0.4"
# Errors.
thiserror = { workspace = true }
# Logging.
tracing = { workspace = true }
[dev-dependencies]
# DaoStore tests use a temp dir as the data root.
tempfile = "3"
# `from_slice` for round-trip CBOR tests in agora module.
pallas-codec = { workspace = true }