Skip to content

governance API

Proposal system implementing 1M1V (One Member = One Vote) governance with ICRC-7 vote-pass NFTs.

Candid file: governance/src/governance.didStandard: ICRC-7 (for vote-pass NFTs)

Types

ProposalCategory

candid
type ProposalCategory = variant {
  Constitutional;       // Bylaws, membership rules
  Operational;          // Day-to-day management
  Treasury;             // Spending, allocations
  SoftwareDevelopment;  // Features, infrastructure
};

ProposalStatus

candid
type ProposalStatus = variant {
  Draft;     // In review period (24h before voting)
  Active;    // Voting period active
  Approved;  // Passed
  Rejected;  // Failed
  Executed;  // Successfully executed
  Failed;    // Execution failed
};

VoteChoice

candid
type VoteChoice = variant {
  Yes;
  No;
  Abstain;
};

Proposal

candid
type Proposal = record {
  id: nat64;
  category: ProposalCategory;
  title: text;
  description: text;
  proposer: principal;
  created_at: nat64;
  voting_start_at: nat64;
  voting_end_at: nat64;
  status: ProposalStatus;
  yes_votes: nat64;
  no_votes: nat64;
  abstain_votes: nat64;
  quorum_threshold: nat64;
  approval_threshold: nat8;  // Percentage (0-100)
  execution_data: opt blob;
};

Vote

candid
type Vote = record {
  proposal_id: nat64;
  voter: principal;
  choice: VoteChoice;
  voted_at: nat64;
  vote_pass_nft_id: opt nat64;
};

Proposal Management

create_proposal

Create a new governance proposal.

candid
"create_proposal": (CreateProposalArgs) -> (variant { Ok: nat64; Err: text });

type CreateProposalArgs = record {
  category: ProposalCategory;
  title: text;
  description: text;
  voting_period_hours: nat64;  // 24-168 hours
  execution_data: opt blob;
};

Requirements:

  • Caller must be active member
  • Title max 200 characters
  • Description max 10,000 characters
  • Voting period 24-168 hours

TypeScript Example:

typescript
const result = await governanceActor.create_proposal({
  category: { Treasury: null },
  title: 'Allocate funds for community event',
  description: 'Proposal to allocate 1000 DOM for Q1 community meetup...',
  voting_period_hours: 72n,
  execution_data: [],
});

if ('Ok' in result) {
  console.log('Created proposal ID:', result.Ok);
}

get_proposal (query)

candid
"get_proposal": (proposal_id: nat64) -> (opt Proposal) query;

list_proposals (query)

candid
"list_proposals": (filter: opt ProposalFilter, pagination: opt Pagination) -> (vec Proposal) query;

type ProposalFilter = record {
  status: opt ProposalStatus;
  category: opt ProposalCategory;
  proposer: opt principal;
};

type Pagination = record {
  offset: nat64;
  limit: nat64;
};

Voting

cast_vote

Cast a vote on an active proposal.

candid
"cast_vote": (proposal_id: nat64, choice: VoteChoice) -> (variant { Ok; Err: text });

Requirements:

  • Caller must be active member
  • Proposal must be in Active status
  • Cannot vote twice on same proposal

1M1V Rule: Each vote increments the counter by exactly 1, regardless of token holdings.

Side Effect: Mints a vote-pass NFT for the voter.

has_voted (query)

candid
"has_voted": (proposal_id: nat64, voter: principal) -> (bool) query;

get_vote (query)

candid
"get_vote": (proposal_id: nat64, voter: principal) -> (opt Vote) query;

Proposal Finalization

finalize

Finalize a proposal after voting ends.

candid
"finalize": (proposal_id: nat64) -> (yes_votes: nat64, no_votes: nat64, abstain_votes: nat64);

Actions:

  1. Calculates final vote counts
  2. Sets status to Approved or Rejected
  3. Burns all vote-pass NFTs for this proposal

execute_proposal

Execute an approved proposal.

candid
"execute_proposal": (proposal_id: nat64) -> (variant { Ok; Err: text });

Status transitions: ApprovedExecuted (success) or Failed (error)

ICRC-7 Vote-Pass NFT Methods

Vote-pass NFTs are Soul-Bound Tokens minted when voting, burned when proposal finalizes.

icrc7_name (query)

candid
"icrc7_name": () -> (text) query;

Returns: "Hello World DAO Vote Pass"

icrc7_symbol (query)

candid
"icrc7_symbol": () -> (text) query;

Returns: "HWDVP"

icrc7_total_supply (query)

candid
"icrc7_total_supply": () -> (nat64) query;

Active vote passes (not yet burned).

icrc7_balance_of (query)

candid
"icrc7_balance_of": (principal) -> (nat64) query;

icrc7_tokens_of (query)

candid
"icrc7_tokens_of": (principal) -> (vec nat64) query;

icrc7_transfer

Always returns NonTransferable - Vote passes cannot be transferred.

candid
"icrc7_transfer": (from: principal, to: principal, token_id: nat64, memo: opt blob, created_at_time: opt nat64)
  -> (variant { Ok: nat64; Err: TransferError });

Audit Trail

get_burn_history (query)

Get burned vote passes for a proposal.

candid
"get_burn_history": (proposal_id: nat64) -> (vec BurnRecord) query;

type BurnRecord = record {
  token_id: nat64;
  proposal_id: nat64;
  voter: principal;
  burned_at: nat64;
};

get_all_burn_history (query)

candid
"get_all_burn_history": (offset: opt nat64, limit: opt nat64) -> (vec BurnRecord) query;

Category Statistics

get_proposal_count_by_category (query)

candid
"get_proposal_count_by_category": () -> (vec record { ProposalCategory; nat64 }) query;

get_active_proposals_by_category (query)

candid
"get_active_proposals_by_category": (category: ProposalCategory) -> (vec Proposal) query;

Threshold Configuration (Controller Only)

set_default_quorum_percentage

candid
"set_default_quorum_percentage": (category: ProposalCategory, percentage: nat8) -> (variant { Ok; Err: text });

set_default_approval_percentage

candid
"set_default_approval_percentage": (category: ProposalCategory, percentage: nat8) -> (variant { Ok; Err: text });

Default Thresholds:

CategoryQuorumApproval
Constitutional50%67%
Operational25%50%
Treasury33%50%
SoftwareDevelopment25%50%

Error Messages

ErrorCauseResolution
Not a memberCaller not active memberComplete signup
Already votedDuplicate vote attemptCannot vote twice
Voting not activeProposal not in Active statusWait for voting period
Proposal not foundInvalid proposal IDCheck proposal exists
Proposal not approvedTrying to execute non-approvedProposal must pass first

Hello World Co-Op DAO