Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

v0 source pure

v0 - Unaudited: This is an initial version that has not yet been formally audited. A fully audited version will be pu...

Readme View source

v0 - Unaudited This is an initial version of this package that has not yet been formally audited. A fully audited version will be published as a subsequent release. Use in production at your own risk.

commondao

Governance primitives following the Common DAO Spec (docs/CONSTITUTION.md, Appendix): a CommonDAO is a Council (a set of addresses with equal voting power), a proposal lifecycle, and an optional sub-DAO tree.

CommonDAO
├── council:            *addrset.Set — who may vote
├── kinds:              registered ProposalKind factories — what may be
│                       proposed (name → New(readonly dao, args))
├── active proposals:   active + early passed, each with an electorate
│                       snapshot and voting record
├── finished proposals: dismissed / executed / failed / withdrawn
├── treasury:           a derived address + frozen flag (funds moved by
│                       the hosting realm, never by this package)
└── children:           sub-DAOs (each a CommonDAO with a parent pointer)

Quick start

 1import "gno.land/p/nt/commondao/v0"
 2
 3// A proposal kind names one proposal type and builds its definitions.
 4type textKind struct{}
 5
 6func (textKind) Name() string { return "text" }
 7func (textKind) New(dao commondao.ReadonlyCommonDAO, args any) (commondao.ProposalDefinition, error) {
 8    text, ok := args.(string) // validate args, build the definition
 9    if !ok || text == "" {
10        return nil, errors.New("a proposal text is required")
11    }
12    return textDefinition{text}, nil
13}
14
15var dao = commondao.New(
16    commondao.WithName("My DAO"),
17    commondao.WithCouncilMember(founder),
18    commondao.WithProposalKind(textKind{}), // a type is proposable iff registered
19)
20
21// Propose looks the kind up in the DAO's registry and calls its New
22// factory with a readonly view of the host DAO and args to build the
23// frozen definition. The council snapshot taken at Propose is the
24// proposal's electorate. (The package does not gate who proposes —
25// hosting realms do.)
26p, _ := dao.Propose(founder, "text", "hello world")
27
28// Electorate members vote; default rule proposals can be decided the
29// moment the outcome is settled.
30dao.Vote(founder, p.ID(), commondao.ChoiceYes, "")
31
32// Execute runs passed proposals (early passed ones immediately, active
33// ones once their voting deadline passes). The host mints a DAO-scoped
34// sub-identity and passes it as the executor's value-movement authority.
35dao.Execute(p.ID(), sub)

Voting rules (the constitutional defaults)

Every proposal definition returns a Threshold(); proposals are decided by TallyDefault with integer math over the proposal's electorate snapshot E (the council at Propose time):

D = |E| - abstains                 // the tally denominator
supermajority:   pass ⇔ D > 0 && 3*yes >= 2*D   ("two thirds or more")
simple majority: pass ⇔ D > 0 && 2*yes > D       ("more than half")
dismiss (both):       ⇔ 2*no > D
undecided at deadline ⇒ dismissed

Abstaining shrinks the denominator (deference); not voting counts against passage (silence is opposition). Votes are re-evaluated after every ballot — including changed votes — so a proposal passes or is dismissed the moment the outcome is mathematically settled and an early-passed proposal may be executed before its deadline.

Vote choices are fixed at YES/NO/ABSTAIN.

Council changes

UpdateCouncil(add, remove) applies idempotent set operations: the final set is (council ∪ add) \ remove, duplicate adds and absent removes are no-ops (so concurrently passed updates merge in execution order), full replacement in one call is legal, and an update that would empty a non-empty council returns ErrEmptyCouncil — executors propagate the error to fail the proposal cleanly.

Proposal kinds

Proposal types are registered on the DAO, not passed per proposal: a ProposalKind couples a registry name with a New(dao ReadonlyCommonDAO, args) factory, and Propose(creator, kind, args) accepts exactly the kinds registered (WithProposalKind at construction; RegisterKind/DeregisterKind afterwards, typically from a governance proposal executor). The registry is read only at Propose: deregistering a kind blocks new proposals but never touches in-flight ones, whose definitions were frozen at creation. HasKind/KindNames expose the registry, also on the readonly view.

New receives only a ReadonlyCommonDAO, so a kind — including an externally-authored or user-registered one — cannot mutate the host DAO (or its tree) at Propose time, before the vote. A kind that must mutate state on execution takes the target *CommonDAO through args, which only a trusted caller can populate (an external proposer cannot obtain a *CommonDAO), captures it in the definition, and mutates in its Executor — which runs only after the vote passes.

The ExecutionKind concrete kind

The package ships exactly one concrete kind, /p/-typed so any realm can seed it with WithProposalKind(ExecutionKind{}) or register it later with RegisterKind:

  • ExecutionKind ("execution") runs an arbitrary ExecFunc supplied by the proposer (ExecutionArgs{Title, Body, Fn}) on approval, under a default policy (7-day voting period, supermajority threshold) and no check on the closure beyond a non-nil Fn. The Fn closure is frozen at Propose (vote-integrity), so it must be authored in a persistent realm — a closure created by a maketx run script does not persist to Execute and cannot run.

    Because it applies no policy to the closure, a realm with treasury constraints (e.g. a freeze flag) should not catalog ExecutionKind directly: it should author its own execution kind whose definition wraps the closure with a Validable check enforcing those constraints, so arbitrary execution cannot bypass them. The reference realm does this to keep a frozen DAO from draining its own treasury via an execution proposal.

A registered foreign-realm kind runs under its defining realm's authority — registering one is a governance trust grant, not a sandbox.

The package ships no governance meta-kinds. RegisterKind / DeregisterKind are plain registry primitives with no reserved names: any registered kind can be removed. Managing a DAO's kind set through governance — and keeping a managing kind un-removable so a DAO can always recover — is the consuming realm's policy, built on these primitives (see the reference realm's manage-kinds kind).

Extending commondao in your own realm

The package is mostly mechanism: it ships the ExecutionKind concrete kind (with a default voting policy) and the registry primitives, and leaves the rest of governance policy — which kinds a DAO accepts, how it manages them, and any per-kind constraints such as a treasury freeze — to the consuming realm. To add your own proposal type:

  1. Author a ProposalKindName() plus New(dao ReadonlyCommonDAO, args any) (ProposalDefinition, error). Make the definition Executable if it mutates on approval. If its executor moves funds from a DAO other than the host, have the host realm consume a Funded-style contract (FundingDAOID() uint64): minting a DAO sub needs the host's cur, so it is host-consumed, not package-dispatched — define it in your realm.
  2. Seed it — the owning realm holds the handle, so no proposal is needed: commondao.New(WithProposalKind(YourKind{}), …) at construction, or dao.RegisterKind(YourKind{}) directly.
  3. Author a typed, CLI-friendly wrapper CreateYourProposal(cur realm, daoID uint64, …params…) that council-gates the caller, builds the args, and calls Propose.
  4. Optionally add a governance toggle — a manage-kinds-style kind whose executor calls RegisterKind/DeregisterKind, kept itself un-deregisterable, if the council should manage kinds at runtime.

Trust boundary: New gets only a ReadonlyCommonDAO; the mutable *CommonDAO reaches a definition only via args your trusted wrapper populates; the executor gets the DAO's terminal, RealmSend-only sub. See the reference realm for a worked example.

Proposal lifecycle

Propose (kind-gated as above; capped via SetMaxActiveProposals; CapExempt definitions such as council updates bypass the cap, bounded to one active proposal per creator) → Vote (electorate-gated, deadline-gated, rejects non-active proposals) → Execute (early-passed: immediately, still validating; active: after the deadline, dismissing undecided proposals) or Withdraw (active, zero votes). Dissolve dismisses every in-flight proposal and soft deletes the DAO; deleted DAOs reject proposals, votes, and executions.

Treasury

The package stores a treasury address (WithAddress, Address()) and a frozen flag (SetTreasuryFrozen, IsTreasuryFrozen) but never moves funds — hosting realms derive the address (typically a realm sub-identity via chain.DerivePkgSubAddr) and enforce the frozen flag. Execute runs the executor with the DAO-scoped sub-identity the host passes as its value-movement authority: a fund-moving definition builds its banker from that sub, so value moves are structurally bounded to that one DAO address. Which DAO's sub the host mints is the host's decision — minting a sub needs the host realm's cur, so the package cannot make it — typically the proposal's own DAO, but a fund-moving definition may direct the host to a different DAO (e.g. clawback sweeps the target, not the host). See the reference realm's treasury proposals and its host-side Funded contract for the constitutional pattern.

Realm boundaries

A *CommonDAO is a mutable handle for the realm that owns it:

  1. Do not ACCEPT a *CommonDAO from an untrusted caller.
  2. Do not RETURN a *CommonDAO — return dao.Readonly(), a ReadonlyCommonDAO view whose whole reachable graph is read-only (ReadonlyProposal flattens Title()/Body() and never exposes the ProposalDefinition, whose executor would otherwise be callable under your realm's authority).
  3. Do not TRUST a readonly view received from an untrusted caller — it is a live handle over the sender's data.

See gno.land/r/nt/commondao/v0 for the reference realm hosting many DAOs with invitations, council governance, treasuries, and rendering.

Overview

v0 - Unaudited: This is an initial version that has not yet been formally audited. A fully audited version will be published as a subsequent release. Use in production at your own risk.

Package commondao provides governance primitives following the Common DAO Spec (docs/CONSTITUTION.md, Appendix): a CommonDAO is a Council (a set of addresses with equal voting power), a proposal lifecycle decided by the constitution's default voting rules, and an optional sub-DAO tree.

Proposal types are registered per DAO: a ProposalKind couples a registry name with a definition factory New(dao ReadonlyCommonDAO, args), and Propose(creator, kind, args) accepts exactly the kinds registered on the DAO. New receives only a readonly view, so it cannot mutate the DAO before the vote; a kind that must mutate state on execution captures its target *CommonDAO from args (populated only by trusted callers) and mutates in its executor. RegisterKind / DeregisterKind / HasKind / KindNames and the WithProposalKind option are the registry primitives; the package ships one concrete kind, ExecutionKind (arbitrary execution), and no governance meta-kinds — managing a DAO's kind set through governance is the consuming realm's job.

Proposals snapshot the council as their electorate at creation and are decided the moment the outcome is mathematically settled: with integer math over D = |electorate| - abstains, a supermajority (3*yes >= 2*D) passes, a NO majority (2*no > D) dismisses, and proposals still undecided at their voting deadline are dismissed.

A DAO may carry a treasury address and frozen flag; the package only stores them - hosting realms derive the address and move the funds.

A *CommonDAO is a mutable handle for the realm that owns it: never accept one from, or return one to, an untrusted realm - readonly views (CommonDAO.Readonly) are the only safe handles to cross a realm boundary. See the package README for details.

Extending commondao in your own realm

This package is mostly mechanism: it ships the ExecutionKind concrete kind (with a default voting policy) and the registry primitives, and leaves the rest of governance policy — which kinds a DAO accepts, how it manages them, and any per-kind constraints such as a treasury freeze — to the consuming realm. To add a proposal type of your own:

  • Author a ProposalKind: a type with Name() string and New(dao ReadonlyCommonDAO, args any) (ProposalDefinition, error). Make the definition Executable (Executor() returns an ExecFunc) if it mutates state on approval. If its executor moves funds from a DAO other than the proposal's host, have the HOST realm consume a Funded-style contract (FundingDAOID() uint64) — minting a DAO sub needs the host's cur, so this contract is host-consumed, not package-dispatched; define it in your realm, as the reference realm does.
  • Apply your own policy. ExecutionKind runs the closure as-is (no check beyond a non-nil Fn), so if your realm has treasury constraints (e.g. a freeze flag) do NOT catalog ExecutionKind directly: author your own execution kind whose definition wraps the closure with a Validable check (Validate() error) enforcing those constraints, so arbitrary execution cannot bypass them. The reference realm does this so a frozen DAO cannot drain its own treasury through an execution proposal.
  • Seed it. The owning realm holds the DAO handle, so no proposal is needed: pass commondao.New(WithProposalKind(YourKind{}), …) at construction, or call dao.RegisterKind(YourKind{}) directly.
  • Author a typed, CLI-friendly wrapper CreateYourProposal(cur realm, daoID uint64, …params…): council-gate the caller, build the args struct, and call Propose. This is the only public entry, so the args-capture trust boundary holds.
  • Optionally add a runtime governance toggle. If the council should register/deregister kinds by vote (rather than only at construction), author a manage-kinds-style ProposalKind whose executor calls RegisterKind/DeregisterKind, and keep that managing kind itself un-deregisterable so the DAO can always recover.

Trust boundary: New receives only a ReadonlyCommonDAO, so a kind — even an externally authored one — cannot mutate the host at Propose time. The mutable *CommonDAO reaches a definition only through args, which your trusted wrapper populates (an external proposer cannot obtain one). On execution the host passes the DAO's terminal, RealmSend-only sub, so a fund-moving executor is bounded to that one DAO address. See the reference realm gno.land/r/nt/commondao/v0 for a full worked example.

Constants 5

const DefaultMaxActiveProposals

1const DefaultMaxActiveProposals = 32
source

DefaultMaxActiveProposals is the default cap for simultaneously active proposals per DAO. Every active proposal stores a council snapshot, so the cap bounds storage. It is applied at construction; a hosting realm may override it per DAO via SetMaxActiveProposals (the reference realm keeps this default).

const ThresholdSupermajority, ThresholdSimpleMajority

 1const (
 2	// ThresholdSupermajority passes with "two thirds or more" of the
 3	// tally denominator. The default for Council decisions.
 4	ThresholdSupermajority Threshold = iota
 5
 6	// ThresholdSimpleMajority passes with "more than half" of the
 7	// tally denominator. The Constitution assigns it to specific
 8	// decisions, e.g. sub-DAO creation.
 9	ThresholdSimpleMajority
10)
source

Thresholds for the constitution's default Council voting rules.

const ChoiceYes, ChoiceNo, ChoiceAbstain

1const (
2	ChoiceYes     VoteChoice = "YES"
3	ChoiceNo      VoteChoice = "NO"
4	ChoiceAbstain VoteChoice = "ABSTAIN"
5)
source

Vote choices, fixed by the Common DAO Spec's default voting rules.

Variables 3

var ErrCouncilUpdateOverlap, ErrDAOIsDeleted, ErrEmptyCouncil, ErrExecutionNotAllowed, ErrInvalidVoteChoice, ErrMaxActiveProposals, ErrMaxCapExemptProposals, ErrNotElectorateMember, ErrOverflow, ErrProposalKindExists, ErrProposalKindNotFound, ErrProposalKindRequired, ErrProposalNotFound, ErrVotingDeadlineNotMet, ErrVotingDeadlinePassed, ErrWithdrawalNotAllowed

 1var (
 2	ErrCouncilUpdateOverlap  = errors.New("council update adds and removes the same address")
 3	ErrDAOIsDeleted          = errors.New("DAO is deleted")
 4	ErrEmptyCouncil          = errors.New("council update would remove every council member")
 5	ErrExecutionNotAllowed   = errors.New("proposal must be active or passed to be executed")
 6	ErrInvalidVoteChoice     = errors.New("invalid vote choice")
 7	ErrMaxActiveProposals    = errors.New("max number of active proposals reached")
 8	ErrMaxCapExemptProposals = errors.New("creator already has an active cap exempt proposal")
 9	ErrNotElectorateMember   = errors.New("account is not a member of the proposal's electorate")
10	ErrOverflow              = errors.New("next ID overflows uint64")
11	ErrProposalKindExists    = errors.New("proposal kind already registered")
12	ErrProposalKindNotFound  = errors.New("proposal kind not found")
13	ErrProposalKindRequired  = errors.New("proposal kind is required")
14	ErrProposalNotFound      = errors.New("proposal not found")
15	ErrVotingDeadlineNotMet  = errors.New("voting deadline not met")
16	ErrVotingDeadlinePassed  = errors.New("voting deadline has passed")
17	ErrWithdrawalNotAllowed  = errors.New("withdrawal not allowed for proposals with votes")
18)
source

Functions 11

func New

1func New(options ...Option) *CommonDAO
source

New creates a new common DAO.

func WithAddress

1func WithAddress(addr address) Option
source

WithAddress assigns a treasury address to the DAO. Hosting realms derive it, typically as a realm sub-identity address (chain.DerivePkgSubAddr) so each DAO owns a distinct account.

func WithCouncilMember

1func WithCouncilMember(addr address) Option
source

WithCouncilMember assigns a council member to the DAO.

func WithDescription

1func WithDescription(description string) Option
source

WithDescription assigns a description to the DAO.

func WithID

1func WithID(id uint64) Option
source

WithID assigns a unique identifier to the DAO.

func WithName

1func WithName(name string) Option
source

WithName assigns a name to the DAO.

func WithParent

1func WithParent(p *CommonDAO) Option
source

WithParent assigns a parent DAO and registers the DAO as one of the parent's children, keeping both sides of the tree wired in one step.

func WithProposalKind

1func WithProposalKind(k ProposalKind) Option
source

WithProposalKind registers a proposal kind on the DAO. It panics when the kind is nil, has an empty name, or its name is already registered.

The package ships one concrete kind, ExecutionKind; seed it with WithProposalKind(ExecutionKind{}).

func WithPurpose

1func WithPurpose(purpose string) Option
source

WithPurpose assigns a purpose to the DAO. Purpose and description together form the DAO's Charter (docs/CONSTITUTION.md :1485).

func TallyDefault

1func TallyDefault(r ReadonlyVotingRecord, electorate *addrset.ReadonlySet, t Threshold) Outcome
source

TallyDefault applies the constitution's default Council voting rules over a proposal's electorate.

Only votes cast by electorate members are counted. The tally denominator D is the electorate size minus the number of ABSTAIN votes: abstaining shrinks the denominator (deference), while not voting counts against passage (silence is opposition). With integer math:

Example
1D = |electorate| - abstains
2supermajority:   passed    ⇔ D > 0 && 3*yes >= 2*D
3simple majority: passed    ⇔ D > 0 && 2*yes > D
4both:            dismissed ⇔ 2*no > D

Passing is checked before dismissal; within one electorate both can never hold at once (yes+no <= D makes each pair contradictory). When D is zero or negative (an empty electorate, or every member abstained) the outcome stays pending: nothing can pass with zero YES votes.

func NewVote

1func NewVote(addr address, choice VoteChoice, reason string) (Vote, error)
source

NewVote creates a vote, validating the address and choice. It exists so external code can build records to independently re-verify a tally with TallyDefault; votes reach a DAO's own records only through CommonDAO.Vote.

Types 22

type CapExempt

interface
1type CapExempt interface {
2	// CapExempt marks the definition as exempt.
3	CapExempt()
4}
source

CapExempt defines an interface for proposal definitions that are not counted against the DAO's active proposals cap. Exempt definitions are instead bounded to one active proposal per creator, so that proposals which remove members (and therefore must never be blockable by a full cap) stay bounded.

type CommonDAO

struct
 1type CommonDAO struct {
 2	id                 uint64
 3	name               string
 4	description        string
 5	purpose            string
 6	addr               address // derived treasury address, empty when unset
 7	parent             *CommonDAO
 8	children           list.IList
 9	council            *addrset.Set
10	genID              seqid.ID
11	kinds              *bptree.BPTree // proposal kind name -> ProposalKind
12	activeProposals    *proposalStorage
13	finishedProposals  *proposalStorage
14	deleted            bool // Soft delete
15	treasuryFrozen     bool
16	maxActiveProposals int
17	proposing          bool // re-entrancy latch around a kind's New in Propose
18	executing          bool // re-entrancy latch around Execute
19}
source

CommonDAO defines a DAO.

Security

A *CommonDAO is a mutable handle: its exported mutators (UpdateCouncil, Dissolve, Propose, Vote, Execute, Withdraw, SetTreasuryFrozen, SetMaxActiveProposals, RegisterKind, DeregisterKind) are meant for the realm that owns the DAO. Three rules apply at realm boundaries:

  1. Do not ACCEPT a *CommonDAO from an external/untrusted caller.
  2. Do not RETURN a *CommonDAO from any function callable by untrusted realms — return dao.Readonly() (a ReadonlyCommonDAO view) instead.
  3. Do not TRUST a readonly view received from an untrusted caller: it is a live handle over the sender's data.

Methods on CommonDAO

func ActiveProposalsSize

method on CommonDAO
1func (dao CommonDAO) ActiveProposalsSize() int
source

ActiveProposalsSize returns the number of active proposals, including early passed proposals that were not executed yet.

func Address

method on CommonDAO
1func (dao CommonDAO) Address() address
source

Address returns the DAO's treasury address, assigned at creation with WithAddress. The package never derives or uses the address itself: hosting realms derive it (e.g. from a realm sub-identity) and operate its funds through their own banker. Empty when unset.

func ChildrenCount

method on CommonDAO
1func (dao CommonDAO) ChildrenCount() int
source

ChildrenCount returns the number of direct children DAOs.

func Council

method on CommonDAO
1func (dao CommonDAO) Council() *addrset.ReadonlySet
source

Council returns a read only view of the DAO council.

The council is the set of addresses entitled to vote. It changes only through UpdateCouncil (normally called by a council update proposal executor) or constructor options.

func DeregisterKind

method on CommonDAO
1func (dao *CommonDAO) DeregisterKind(name string) error
source

DeregisterKind removes a proposal kind by name.

Deregistering only blocks new proposals: the registry is read at Propose time only, so in-flight proposals of the kind keep their frozen definition and still vote and execute.

This is a plain registry primitive with no reserved names: any registered kind can be removed. A consuming realm that must keep a kind un-removable (e.g. a governance kind that manages the kind set) enforces that as its own policy, not through this package.

func Description

method on CommonDAO
1func (dao CommonDAO) Description() string
source

Description returns DAO's description.

func Dissolve

method on CommonDAO
1func (dao *CommonDAO) Dissolve(reason string)
source

Dissolve soft deletes the DAO after dismissing every in-flight proposal (both still-active and passed-but-unexecuted ones). Dissolution is terminal: a deleted DAO rejects proposals, votes and executions, so nothing may remain pending.

func Execute

method on CommonDAO
1func (dao *CommonDAO) Execute(proposalID uint64, sub realm) error
source

Execute executes a proposal.

Proposals that already passed (decided early by the default Council rules) execute immediately. Active proposals are tallied once their voting deadline passes and are dismissed unless passed.

sub is the DAO-scoped sub-identity that the host mints and passes into the executor as its value-movement authority (see ExecFunc). The executor is non-crossing, so it is called directly. Execute itself is not a crossing function (sub sits in a non-first parameter slot) because /p/ production code cannot declare crossing functions.

func FinishedProposalsSize

method on CommonDAO
1func (dao CommonDAO) FinishedProposalsSize() int
source

FinishedProposalsSize returns the number of finished proposals.

func GetProposal

method on CommonDAO
1func (dao CommonDAO) GetProposal(proposalID uint64) *Proposal
source

GetProposal returns a proposal or nil when proposal is not found.

func HasKind

method on CommonDAO
1func (dao CommonDAO) HasKind(name string) bool
source

HasKind checks if a proposal kind is registered.

func ID

method on CommonDAO
1func (dao CommonDAO) ID() uint64
source

ID returns DAO's unique identifier.

func IsDeleted

method on CommonDAO
1func (dao CommonDAO) IsDeleted() bool
source

IsDeleted returns true when DAO has been soft deleted.

func IsTreasuryFrozen

method on CommonDAO
1func (dao CommonDAO) IsTreasuryFrozen() bool
source

IsTreasuryFrozen checks if the DAO's treasury is frozen. The package stores the flag only; hosting realms enforce it when moving funds.

func IterateActiveProposals

method on CommonDAO
1func (dao CommonDAO) IterateActiveProposals(offset, count int, reverse bool, fn func(*Proposal) bool) bool
source

IterateActiveProposals iterates active proposals ordered by ID.

func IterateChildren

method on CommonDAO
1func (dao CommonDAO) IterateChildren(fn func(*CommonDAO) bool) (stopped bool)
source

IterateChildren iterates the direct children DAOs.

func IterateFinishedProposals

method on CommonDAO
1func (dao CommonDAO) IterateFinishedProposals(offset, count int, reverse bool, fn func(*Proposal) bool) bool
source

IterateFinishedProposals iterates finished proposals ordered by ID.

func KindNames

method on CommonDAO
1func (dao CommonDAO) KindNames() []string
source

KindNames returns the names of the registered proposal kinds, sorted.

func MaxActiveProposals

method on CommonDAO
1func (dao CommonDAO) MaxActiveProposals() int
source

MaxActiveProposals returns the cap for simultaneously active proposals.

func Name

method on CommonDAO
1func (dao CommonDAO) Name() string
source

Name returns DAO's name.

func Parent

method on CommonDAO
1func (dao CommonDAO) Parent() *CommonDAO
source

Parent returns the parent DAO. Null can be returned when DAO has no parent assigned.

func Propose

method on CommonDAO
1func (dao *CommonDAO) Propose(creator address, kind string, args any) (*Proposal, error)
source

Propose creates a new DAO proposal.

Proposals are created through registered proposal kinds: the kind is looked up by name in the DAO's registry and its New factory builds the proposal definition from args. The registry is read only here and the definition is frozen once the proposal is created, so deregistering a kind later never touches in-flight proposals.

The proposal's electorate is the council snapshot taken now: members added later vote on the next proposal; members removed later remain in the electorate, where their silence counts against passage.

The number of simultaneously active proposals is capped. Definitions implementing CapExempt (e.g. council updates, which must never be blockable by a full cap) are exempt but bounded to one active proposal per creator.

func Purpose

method on CommonDAO
1func (dao CommonDAO) Purpose() string
source

Purpose returns the DAO's purpose. Together with the description it forms the DAO's Charter (docs/CONSTITUTION.md :1485).

func Readonly

method on CommonDAO
1func (dao *CommonDAO) Readonly() ReadonlyCommonDAO
source

Readonly returns a read only view of the DAO.

func RegisterKind

method on CommonDAO
1func (dao *CommonDAO) RegisterKind(k ProposalKind) error
source

RegisterKind registers a proposal kind by its name.

Registered kinds are the only way to create proposals: Propose looks kinds up by name and calls their New factory, so a proposal type is proposable iff its kind is registered. Like SetTreasuryFrozen, this mutator is meant for the realm that owns the DAO (typically called at DAO creation and by governance proposal executors).

func SetMaxActiveProposals

method on CommonDAO
1func (dao *CommonDAO) SetMaxActiveProposals(max int)
source

SetMaxActiveProposals changes the cap for simultaneously active proposals. Values below one are ignored: a DAO must always be able to propose.

func SetTreasuryFrozen

method on CommonDAO
1func (dao *CommonDAO) SetTreasuryFrozen(frozen bool)
source

SetTreasuryFrozen freezes or unfreezes the DAO's treasury.

func UpdateCouncil

method on CommonDAO
1func (dao *CommonDAO) UpdateCouncil(add, remove []address) error
source

UpdateCouncil adds and removes council members as idempotent set operations: adding an existing member or removing an absent one is a no-op, so concurrently passed council updates merge deterministically in execution order, and a full council replacement in a single update is legal.

The final set is (council ∪ add) \ remove. An update that adds and removes the same address is rejected, and an update whose final set would empty a non-empty council returns ErrEmptyCouncil: executors must propagate the error (failing the proposal) instead of panicking, which would revert the transaction and leave the proposal stuck.

func Vote

method on CommonDAO
1func (dao *CommonDAO) Vote(member address, proposalID uint64, c VoteChoice, reason string) error
source

Vote submits a new vote for a proposal.

Votes are only allowed to members of the proposal's electorate while the proposal is active and within the voting period. A member may change their vote by voting again.

Proposals are re-evaluated after every recorded vote: a YES tally at the definition's threshold decides the proposal immediately, and a simple majority of NO dismisses it immediately.

func Withdraw

method on CommonDAO
1func (dao *CommonDAO) Withdraw(proposalID uint64) error
source

Withdraw withdraws a proposal that has no votes. Only active proposals without votes can be withdrawn, and once withdrawn they are considered finished.

type ExecFunc

func
1type ExecFunc func(int, realm) error
source

ExecFunc defines a type for functions that execute proposals.

The leading int makes ExecFunc non-crossing: the host calls it directly (no cross), so the executor holds no realm cur of its own — only the realm argument, a DAO-scoped sub-identity the host mints and passes. Fund-moving executors send through that sub (e.g. banker RealmSend), which is terminal and bounded to one DAO address; executors that move no funds ignore it. The int is unused.

Authority note: the sub is a least-authority DEFAULT, not a sandbox. An executor is trusted realm code; because the sub is the executor's only current realm value, it could regain the host realm's primary authority via an explicit cross(sub) into a crossing function. That is a visible, auditable call the reference realm's executors never make, so their blast radius is one treasury — but a realm that runs untrusted or user-registered executors gets no such guarantee. See ADR pr6012_commondao_exec_scope.

The sharper hazard for such a realm is not cross(sub) but the banker: an executor can mint banker.NewBanker(BankerTypeRealmSend, sub) and simply RETAIN it. Authorization happens at construction only, and the banker holds no realm reference, so it persists across transactions even though the sub itself cannot — a permanent, unrevocable capability over that DAO's address, spendable later with no proposal. It also bypasses any check the host performs before spending (a frozen flag, a pause switch), because it reaches the bank keeper without re-entering host code. Passing the sub to an executor whose code the DAO has not vetted is therefore an irrevocable grant of that DAO's treasury, not a scoped loan of it.

type Executable

interface
1type Executable interface {
2	// Executor returns a function to execute the proposal.
3	Executor() ExecFunc
4}
source

Executable defines an interface for proposal definitions that modify state on approval. Once proposals are executed they are archived and considered finished.

type ExecutionArgs

struct
1type ExecutionArgs struct {
2	Title string
3	Body  string
4	Fn    ExecFunc
5}
source

ExecutionArgs are the args for the execution kind (ExecutionKind): a title, a body, and the ExecFunc to run on approval. The closure must be authored in a persistent realm so it survives Propose→Execute; a closure created by a `maketx run` script does not persist and cannot be executed later.

type ExecutionKind

struct
1type ExecutionKind struct{}
source

ExecutionKind is the package's one concrete proposal kind: a stateless, reusable arbitrary-execution kind that runs an ExecFunc supplied by the proposer on approval. It is /p/-typed so any realm can register it with WithProposalKind(ExecutionKind{}) or RegisterKind without defining its own execution kind.

The executor moves value only through the DAO-scoped sub the host passes (see ExecFunc): the host mints and passes that sub, so the executor receives whichever DAO's sub the host decides (its own DAO's by default). The closure is frozen at Propose (vote-integrity: the exact code is fixed before the vote).

This kind applies NO policy check to the closure beyond a non-nil Fn: it runs the arbitrary code as-is. A realm that has treasury constraints (e.g. a freeze flag) should NOT catalog this kind directly; instead it should author its own execution kind whose definition wraps the closure with a Validable check that enforces those constraints (blocking execution while frozen, etc.), so arbitrary execution cannot bypass them. The reference realm gno.land/r/nt/commondao/v0 does exactly this.

Methods on ExecutionKind

func Name

method on ExecutionKind
1func (ExecutionKind) Name() string
source

Name returns the execution kind's registry name.

func New

method on ExecutionKind
1func (ExecutionKind) New(_ ReadonlyCommonDAO, args any) (ProposalDefinition, error)
source

New validates ExecutionArgs and builds an execution definition. It is a pure factory: it receives only a readonly view and captures no mutable handle.

type Option

func
1type Option func(*CommonDAO)
source

Option configures the CommonDAO.

type Outcome

ident
1type Outcome int
source

Outcome defines a type for default tally outcomes.

type Proposal

struct
 1type Proposal struct {
 2	id             uint64
 3	status         ProposalStatus
 4	definition     ProposalDefinition
 5	creator        address
 6	record         *VotingRecord
 7	electorate     *addrset.Set // council snapshot taken at Propose
 8	statusReason   string
 9	votingDeadline time.Time
10	createdAt      time.Time
11}
source

Proposal defines a DAO proposal.

Methods on Proposal

func CreatedAt

method on Proposal
1func (p Proposal) CreatedAt() time.Time
source

CreatedAt returns the time that proposal was created.

func Creator

method on Proposal
1func (p Proposal) Creator() address
source

Creator returns the address of the account that created the proposal.

func Definition

method on Proposal
1func (p Proposal) Definition() ProposalDefinition
source

Definition returns the proposal definition. Proposal definitions define proposal content and behavior.

func Electorate

method on Proposal
1func (p Proposal) Electorate() *addrset.ReadonlySet
source

Electorate returns the proposal's electorate: a read only view of the council snapshot taken when the proposal was created. Members added to the council afterwards vote on the next proposal; members removed or resigned afterwards remain in the electorate (their silence counts against passage).

func ExpectedOutcome

method on Proposal
1func (p Proposal) ExpectedOutcome() Outcome
source

ExpectedOutcome returns the outcome the proposal would have if it were decided with the votes submitted so far. Useful for rendering.

func HasVotingDeadlinePassed

method on Proposal
1func (p Proposal) HasVotingDeadlinePassed() bool
source

HasVotingDeadlinePassed checks if the voting deadline has been met.

func ID

method on Proposal
1func (p Proposal) ID() uint64
source

ID returns the unique proposal identifier.

func Readonly

method on Proposal
1func (p *Proposal) Readonly() ReadonlyProposal
source

Readonly returns a read only view of the proposal.

func Status

method on Proposal
1func (p Proposal) Status() ProposalStatus
source

Status returns the current proposal status.

func StatusReason

method on Proposal
1func (p Proposal) StatusReason() string
source

StatusReason returns an optional reason that led to the current proposal status. Reason is mostly useful when a proposal fails.

func Validate

method on Proposal
1func (p Proposal) Validate() error
source

Validate validates that a proposal is valid for the current state. Validation is done when the proposal can still be executed (status is active or passed) and when the definition supports validation.

func VotingDeadline

method on Proposal
1func (p Proposal) VotingDeadline() time.Time
source

VotingDeadline returns the deadline after which no more votes should be allowed.

func VotingRecord

method on Proposal
1func (p Proposal) VotingRecord() ReadonlyVotingRecord
source

VotingRecord returns a read only record with the votes submitted for the proposal. Votes are recorded through CommonDAO.Vote only.

type ProposalDefinition

interface
 1type ProposalDefinition interface {
 2	// Title returns the proposal title.
 3	Title() string
 4
 5	// Body returns proposal's body.
 6	// It usually contains description or values that are specific to the proposal,
 7	// like a description of the proposal's motivation or the list of values that
 8	// would be applied when the proposal is approved.
 9	Body() string
10
11	// VotingPeriod returns the period where votes are allowed after proposal creation.
12	// It is used to calculate the voting deadline from the proposal's creation date.
13	VotingPeriod() time.Duration
14
15	// Threshold returns the tally threshold for passing the proposal.
16	// Proposals are decided by the constitution's default Council voting
17	// rules: re-evaluated after every recorded vote, they can pass or be
18	// dismissed before their voting deadline.
19	//
20	// Threshold is read on every Vote (for early passage) AND again in
21	// the post-deadline re-tally inside Execute. Return a CONSTANT value:
22	// a threshold that loosens over a proposal's lifetime can let the
23	// deadline re-tally pass with fewer YES votes than voters faced when
24	// they cast under the stricter earlier value. A changing threshold is
25	// honored, but the definition author owns that consequence.
26	Threshold() Threshold
27}
source

ProposalDefinition defines an interface for custom proposal definitions. These definitions define proposal content and behavior, essentially allowing the definition of different proposal types.

type ProposalKind

interface
 1type ProposalKind interface {
 2	// Name returns the kind name used as registry key, e.g. "treasury-spend".
 3	Name() string
 4
 5	// New validates args and builds the proposal definition. Propose
 6	// passes a ReadonlyCommonDAO view of the host DAO, so New is a
 7	// pure factory that cannot mutate the host or its tree before the
 8	// vote; proposal targets and parameters come via args. A kind that
 9	// must mutate state on execution receives the target *CommonDAO
10	// through args (which only trusted callers can populate), captures
11	// it, and mutates in its Executor. The returned definition's
12	// instance data is frozen at Propose like any proposal definition.
13	New(dao ReadonlyCommonDAO, args any) (ProposalDefinition, error)
14}
source

ProposalKind defines an interface for proposal kinds: named factories for proposal definitions, registered per DAO. A kind is both the registry key (Name) and the factory (New) for one proposal type, and a DAO accepts proposals of exactly the kinds registered on it (CommonDAO.RegisterKind).

type ProposalStatus

ident
1type ProposalStatus string
source

ProposalStatus defines a type for different proposal states.

type ReadonlyCommonDAO

struct
1type ReadonlyCommonDAO struct {
2	dao *CommonDAO
3}
source

ReadonlyCommonDAO is a read only view of a CommonDAO. It exposes only read side methods, holds the *CommonDAO in an unexported field, and every reachable value is itself readonly or a copy — so cross-realm holders cannot mutate the DAO through it. Views are live handles, not snapshots.

This is the only safe handle to a DAO across a realm boundary: hosting realms must never return the *CommonDAO itself.

Methods on ReadonlyCommonDAO

func ActiveProposalsSize

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) ActiveProposalsSize() int
source

ActiveProposalsSize returns the number of active proposals.

func Address

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) Address() address
source

Address returns the DAO's treasury address, or empty when unset.

func ChildrenCount

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) ChildrenCount() int
source

ChildrenCount returns the number of direct children DAOs.

func Council

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) Council() *addrset.ReadonlySet
source

Council returns a read only view of the DAO council.

func Description

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) Description() string
source

Description returns DAO's description.

func FinishedProposalsSize

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) FinishedProposalsSize() int
source

FinishedProposalsSize returns the number of finished proposals.

func GetProposal

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) GetProposal(proposalID uint64) (_ ReadonlyProposal, found bool)
source

GetProposal returns a read only view of a proposal when it exists.

func HasKind

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) HasKind(name string) bool
source

HasKind checks if a proposal kind is registered on the DAO.

func ID

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) ID() uint64
source

ID returns DAO's unique identifier.

func IsDeleted

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) IsDeleted() bool
source

IsDeleted returns true when DAO has been soft deleted.

func IsTreasuryFrozen

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) IsTreasuryFrozen() bool
source

IsTreasuryFrozen checks if the DAO's treasury is frozen.

func IterateActiveProposals

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) IterateActiveProposals(offset, count int, reverse bool, fn func(ReadonlyProposal) bool) bool
source

IterateActiveProposals iterates read only views of the active proposals. The callback can return true to stop iteration.

func IterateChildren

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) IterateChildren(fn func(ReadonlyCommonDAO) bool) (stopped bool)
source

IterateChildren iterates the direct children DAOs. The callback can return true to stop iteration.

func IterateFinishedProposals

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) IterateFinishedProposals(offset, count int, reverse bool, fn func(ReadonlyProposal) bool) bool
source

IterateFinishedProposals iterates read only views of the finished proposals. The callback can return true to stop iteration.

func KindNames

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) KindNames() []string
source

KindNames returns the names of the registered proposal kinds, sorted.

func Name

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) Name() string
source

Name returns DAO's name.

func Parent

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) Parent() (_ ReadonlyCommonDAO, found bool)
source

Parent returns a read only view of the parent DAO when there is one.

func Purpose

method on ReadonlyCommonDAO
1func (r ReadonlyCommonDAO) Purpose() string
source

Purpose returns DAO's purpose (part of the Charter).

type ReadonlyProposal

struct
1type ReadonlyProposal struct {
2	p *Proposal
3}
source

ReadonlyProposal is a read only view of a Proposal. Proposal content is exposed flattened (Title, Body): the view never exposes the underlying ProposalDefinition, whose Executor would otherwise be callable by any holder with the hosting realm's authority.

Methods on ReadonlyProposal

func Body

method on ReadonlyProposal
1func (r ReadonlyProposal) Body() string
source

Body returns the proposal definition's body.

func CreatedAt

method on ReadonlyProposal
1func (r ReadonlyProposal) CreatedAt() time.Time
source

CreatedAt returns the time that proposal was created.

func Creator

method on ReadonlyProposal
1func (r ReadonlyProposal) Creator() address
source

Creator returns the address of the account that created the proposal.

func Electorate

method on ReadonlyProposal
1func (r ReadonlyProposal) Electorate() *addrset.ReadonlySet
source

Electorate returns a read only view of the proposal's electorate.

func ID

method on ReadonlyProposal
1func (r ReadonlyProposal) ID() uint64
source

ID returns the unique proposal identifier.

func Status

method on ReadonlyProposal
1func (r ReadonlyProposal) Status() ProposalStatus
source

Status returns the current proposal status.

func StatusReason

method on ReadonlyProposal
1func (r ReadonlyProposal) StatusReason() string
source

StatusReason returns an optional reason that led to the current proposal status.

func Title

method on ReadonlyProposal
1func (r ReadonlyProposal) Title() string
source

Title returns the proposal definition's title.

func VotingDeadline

method on ReadonlyProposal
1func (r ReadonlyProposal) VotingDeadline() time.Time
source

VotingDeadline returns the deadline after which no more votes are allowed.

func VotingRecord

method on ReadonlyProposal
1func (r ReadonlyProposal) VotingRecord() ReadonlyVotingRecord
source

VotingRecord returns a read only record with the submitted votes.

type ReadonlyVotingRecord

struct
1type ReadonlyVotingRecord struct {
2	votes bptree.BPTree // string(address) -> Vote
3	count bptree.BPTree // string(choice) -> int
4}
source

ReadonlyVotingRecord defines a read only voting record. The copy captures the live record's tree roots by value, so a held value can go stale in surprising ways: fetch it, read it, and re-fetch rather than holding it across votes.

Methods on ReadonlyVotingRecord

func GetVote

method on ReadonlyVotingRecord
1func (r ReadonlyVotingRecord) GetVote(user address) (_ Vote, found bool)
source

GetVote returns a vote.

func HasVoted

method on ReadonlyVotingRecord
1func (r ReadonlyVotingRecord) HasVoted(user address) bool
source

HasVoted checks if an account already voted.

func Iterate

method on ReadonlyVotingRecord
1func (r ReadonlyVotingRecord) Iterate(offset, count int, reverse bool, fn VoteIterFn) bool
source

Iterate iterates voting record votes.

func IterateVotesCount

method on ReadonlyVotingRecord
1func (r ReadonlyVotingRecord) IterateVotesCount(fn VotesCountIterFn) bool
source

IterateVotesCount iterates voted choices with the amount of votes submitted for each.

func Size

method on ReadonlyVotingRecord
1func (r ReadonlyVotingRecord) Size() int
source

Size returns the total number of votes that record contains.

func VoteCount

method on ReadonlyVotingRecord
1func (r ReadonlyVotingRecord) VoteCount(c VoteChoice) int
source

VoteCount returns the number of votes for a single voting choice.

type Threshold

ident
1type Threshold int
source

Threshold defines a type for the default tally thresholds.

type Validable

interface
1type Validable interface {
2	// Validate validates that the proposal is valid for the current state.
3	Validate() error
4}
source

Validable defines an interface for proposal definitions that require state validation. Validation is done before execution and normally also during proposal rendering.

type Vote

struct
1type Vote struct {
2	addr   address
3	choice VoteChoice
4	reason string
5}
source

Vote defines a single vote. Its fields are unexported so instances cannot be forged or reshaped outside the package: votes enter a record only through CommonDAO.Vote's gates.

Methods on Vote

func Address

method on Vote
1func (v Vote) Address() address
source

Address returns the address of the account that submitted the vote.

func Choice

method on Vote
1func (v Vote) Choice() VoteChoice
source

Choice returns the voted choice.

func Reason

method on Vote
1func (v Vote) Reason() string
source

Reason returns the optional reason for the vote.

type VoteChoice

ident
1type VoteChoice string
source

VoteChoice defines a type for proposal vote choices.

type VoteIterFn

func
1type VoteIterFn func(Vote) (stop bool)
source

VoteIterFn defines a callback to iterate votes.

type VotesCountIterFn

func
1type VotesCountIterFn func(_ VoteChoice, voteCount int) (stop bool)
source

VotesCountIterFn defines a callback to iterate voted choices.

type VotingRecord

struct
1type VotingRecord struct {
2	ReadonlyVotingRecord
3}
source

VotingRecord stores accounts that voted and vote choices.

Methods on VotingRecord

func AddVote

method on VotingRecord
1func (r *VotingRecord) AddVote(vote Vote) (updated bool)
source

AddVote adds a vote to the voting record. If a vote for the same user already exists is overwritten.

func Readonly

method on VotingRecord
1func (r VotingRecord) Readonly() ReadonlyVotingRecord
source

Readonly returns a read only voting record.

Imports 6

Source Files 10