Community & governance
Design proposals, voting and execution around participants and permissions.
← All implementation patternsConnect community budget proposals, voting and execution
A facility community proposes allocating 100 budget-token units to an activity coordinator. Members vote on executable recipient and amount data as well as the proposal text, then execute it after approval and a waiting period.
Keep the proposal and execution aligned, with a record of votes, approval and the eventual allocation.
Architecture and responsibilities
Proposal and voting interface
Collect target, amount and dates and display a readable view of the execution data.
Web UI / WalletVoting power and rules
Configure voting tokens, delegation, thresholds and quorum.
IVotes / Weight strategyProposal lifecycle
Manage proposal IDs, voting windows, results and state.
GenericGovernorHelperDelayed execution
Timelock calls the target contract after the delay.
TimelockController / Target contract
Example data model
Map application records to the data managed by the chain or APIs. Field names and values below illustrate the application model for this architecture.
| Field and example | Stored in | Purpose and mapping |
|---|---|---|
proposalIduint256 as string | Chain and application DB | Proposal ID returned by Governor. Store it as a string rather than converting to a JavaScript number. |
targets / values / calldatastoken / 0 / transfer(...) | Chain | Matching indices define one call. values contains native-currency amounts. |
descriptionCommunity activity grant | Chain and application DB | Text used in proposal ID calculation. Keep it associated with the executable data. |
startTs / endTsUNIX seconds | Chain | Voting start and end timestamps. Do not pass browser milliseconds directly. |
vote / supportproposalId / 1 | Chain | 0 is Against, 1 For and 2 Abstain. Voting power follows the configured strategy. |
executionHash / operationId0x… / timelock ID | Chain and application DB | Distinguish proposal ID, Timelock operation ID and execution transaction hash. |
Processing flow
Prepare governance and the budget
Connect Governor, Timelock, an IVotes token and a weight strategy. The executing Timelock must hold the budget tokens and any required permissions.
GenericGovernor / TimelockController / IVotesInclude the exact action in the proposal
Encode the recipient and 100 units as an ERC-20 transfer, then propose it with a voting window and description. Display the token, recipient and display amount, not only encoded bytes.
Interface.encodeFunctionData → governor.proposeCollect votes
Eligible members vote while the proposal is Active. For ERC20Votes configurations, check delegation and historical voting power; current holdings alone may not establish voting power.
state(proposalId) / castVote(proposalId, 1)Queue a successful proposal
Queue after voting ends successfully. Approval, Queued and Executed are distinct states. Enable actions based on state, including defeated or canceled outcomes.
queue(proposalId) → Timelock delayExecute after the delay
Once the Timelock operation is Ready, an authorized executor calls execute. After confirmation, check the recipient’s balance and execution event and update the proposal view.
Timelock.isOperationReady / governor.execute(proposalId)
Before using the code
- Deploy Governor, Timelock, voting token and weight strategy, then configure roles for proposals, queueing and execution.
- Timelock must hold the budget tokens. Transfers must satisfy transferability, pause and allowlist conditions.
- Use UNIX seconds based on the target chain’s clock. The end must follow the start and respect the allowed voting window.
npm install --save-exact @hazbase/kit@0.9.0 ethers@6.16.0The TypeScript functions below are called from your application with the appropriate connections, permissions and data. Integrate the UI, persistence and recovery using the architecture above.
Propose a 100-unit allocation
Pass units: "100" to encode the allocation using the budget token’s decimals. This function creates the proposal; voting, queueing and execution are separate operations at later states.
import { GenericGovernorHelper, FlexibleTokenHelper } from '@hazbase/kit';
import { Interface, parseUnits } from 'ethers';
type Signer = Parameters<typeof GenericGovernorHelper.deploy>[1];
export async function proposeCommunityGrant(input: {
governorAddress: string;
budgetTokenAddress: string;
recipient: string;
units: string;
description: string;
startTs: bigint;
endTs: bigint;
proposer: Signer;
}) {
const governor = GenericGovernorHelper.attach(input.governorAddress, input.proposer);
const token = FlexibleTokenHelper.attach(input.budgetTokenAddress, input.proposer);
const amount = parseUnits(input.units, await token.decimals());
if (amount <= 0n) throw new Error('Grant must be positive.');
const erc20 = new Interface(['function transfer(address to, uint256 amount)']);
// At execution, Timelock must hold the budget tokens being transferred.
const proposalId = await governor.propose({
proposer: await input.proposer.getAddress(),
targets: [input.budgetTokenAddress],
values: [0n],
calldatas: [erc20.encodeFunctionData('transfer', [input.recipient, amount])],
description: input.description,
startTs: input.startTs,
endTs: input.endTs,
});
// Voting, queueing and execution are separate actions at later states.
return { proposalId: proposalId.toString(), state: await governor.state(proposalId) };
}
Result and application integration
Store proposalId as a string and use state to track progress. Creating the proposal does not transfer tokens; allocation happens on execute after successful voting and the delay.
Implementation considerations
Description and action mismatch
Decode targets, values and calldatas for the user instead of relying on the description alone. Make recipient and token mismatches visible before submission.
Who holds the budget
Tokens in the proposer’s wallet do not fund a Timelock transfer. Check the executor’s balance and target-contract permissions.
Execution failure after approval
Insufficient funds, a pause or transfer restrictions can cause execution to fail after approval. Distinguish approved from executed and inspect operation state before retrying.
Extending the architecture
Other governed actions
Other actions, such as contract configuration changes, can be proposed when Timelock is authorized to execute them. Display the required permissions and effects for each function.
Different voting-power models
Use IVotes and a weight strategy for membership- or role-based voting. Define thresholds, quorum and reference time as well as the difference from balance-based voting.