Community & governance https://docs.hazbase.com/en/patterns/governance/ IMPLEMENTATION PATTERNS Community & governance Design proposals, voting and execution around participants and permissions. ← All implementation patterns EXAMPLE Connect 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 0 1 Proposal and voting interface Collect target, amount and dates and display a readable view of the execution data. Web UI / Wallet 0 2 Voting power and rules Configure voting tokens, delegation, thresholds and quorum. IVotes / Weight strategy 0 3 Proposal lifecycle Manage proposal IDs, voting windows, results and state. GenericGovernorHelper 0 4 Delayed 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 proposalId uint256 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 / calldatas token / 0 / transfer(...) Chain Matching indices define one call. values contains native-currency amounts. description Community activity grant Chain and application DB Text used in proposal ID calculation. Keep it associated with the executable data. startTs / endTs UNIX seconds Chain Voting start and end timestamps. Do not pass browser milliseconds directly. vote / support proposalId / 1 Chain 0 is Against, 1 For and 2 Abstain. Voting power follows the configured strategy. executionHash / operationId 0x… / timelock ID Chain and application DB Distinguish proposal ID, Timelock operation ID and execution transaction hash. Processing flow 1 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 / IVotes 2 Include 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.propose 3 Collect 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) 4 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 delay 5 Execute 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. Install npm install --save-exact @hazbase/kit@0.9.0 ethers@6.16.0 The 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. pattern-governance.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 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 ) } ; } Download the example 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. Related API and SDK reference GenericGovernorHelper TimelockControllerHelper FlexibleTokenHelper / IVotes Start with the sample application Deployment and operations