DAO Grants System Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
DAO Grants System Development
Medium
~1-2 weeks
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Proposal and Voting System Development

If a DAO already exists and voting system needs refinement or building from scratch — need clarity on what we're building: full on-chain Governor, lightweight off-chain mechanism, or hybrid. Choice depends on active participant count, treasury size, and decentralization requirements.

Here — detailed on proposal mechanics and voting, including patterns not obvious from documentation.

Proposal Lifecycle

Creation and Snapshot

Proposal is created by calling governor.propose(). At creation, proposalSnapshot — block number where voting power will be counted — is fixed. This is critical: if snapshot matches current block, attacker can buy tokens in same block and vote.

Therefore votingDelay — blocks/seconds between proposal creation and voting start — must be nonzero. Compound uses 1 day (6570 blocks on Ethereum mainnet); Aave uses 1 day.

// GovernorSettings parameters
uint48 public constant VOTING_DELAY = 1 days;    // delay before voting starts
uint32 public constant VOTING_PERIOD = 7 days;   // voting duration
uint256 public constant PROPOSAL_THRESHOLD = 100_000e18; // min tokens to create

Voting: Simple vs Weighted

GovernorCountingSimple — standard: FOR, AGAINST, ABSTAIN. Proposal passes if: (1) quorum reached (votes not less than threshold), (2) FOR > AGAINST.

Fractional voting — more advanced: delegate can distribute voting power fractionally among options. Useful when delegate wants expressing constituent positions where they disagree. Implemented via custom GovernorCountingFractional (fork by a16z exists).

Quadratic voting — voting power = sqrt(tokens). Equalizes influence of large and small holders. Hard implementing on-chain honestly due to Sybil: address with 10000 tokens can split into 100 addresses with 100 tokens each, getting 10x more influence. Requires identity verification (Worldcoin, Gitcoin Passport) for Sybil resistance.

Quorum and Calculation

Quorum is minimum votes (FOR + AGAINST + ABSTAIN) for validity. GovernorVotesQuorumFraction counts quorum as percentage of total supply at snapshot moment.

Problem: if vesting gradually unlocks tokens, total supply grows — quorum in absolute numbers too. With high supply growth early proposals passed with lower quorum. getPastTotalSupply(proposalSnapshot) solves this — quorum counted from supply at snapshot, not current.

function quorum(uint256 timepoint) public view override returns (uint256) {
    return token.getPastTotalSupply(timepoint) * quorumNumerator(timepoint) / quorumDenominator();
}

Delegation Mechanism

Fluid Delegation

ERC20Votes allows changing delegate anytime. Change takes effect immediately for future votes but not retroactively — for open proposals snapshot is already fixed.

This creates interesting dynamics: before important voting, active participants aggressively collect delegations. Delegate companies (Gauntlet, a16z governance team, Blockchain@UMich) publicly declare positions on each issue, attracting passive holders.

Subdelegation

Standard ERC20Votes doesn't support subdelegation: if A delegated B, B can't further delegate to C (B uses own tokens + delegated together but can't subdelegation). Compound v3 Governor introduced partial delegation and subdelegation via separate mechanism.

For complex governance with delegate hierarchies — need custom extension atop ERC20Votes.

Proposal Types and Mechanics

Single-action Proposals

Simplest: one action — change parameter. Example: change percentage rate in lending protocol:

targets = [address(lendingPool)];
values = [0];
calldatas = [abi.encodeWithSelector(ILendingPool.setInterestRate.selector, newRate)];

Multi-action Proposals (Batched)

Governor supports arrays of targets/values/calldatas — all actions execute atomically. Useful for related changes: update contract implementation AND update parameters in one proposal. If any action reverts — all reverts.

Proposal Cancellation

Creator can cancel proposal before voting starts. Protects from errors (wrong calldata). After voting starts — only Guardian with CANCELLER role in TimelockController.

function cancel(
    address[] memory targets,
    uint256[] memory values,
    bytes[] memory calldatas,
    bytes32 descriptionHash
) public returns (uint256) {
    uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
    require(
        _msgSender() == proposalProposer(proposalId),
        "Only proposer can cancel"
    );
    return _cancel(targets, values, calldatas, descriptionHash);
}

Prevent Late Quorum Extension

Classic attack: large holder waits until voting's last minutes when outcome seems decided, then swings result with one vote. Opponents have no time to react.

GovernorPreventLateQuorum — extension extending voting period if quorum reached in last N blocks before deadline:

function _castVote(...) internal override returns (uint256) {
    uint256 result = super._castVote(...);
    
    uint256 deadline = proposalDeadline(proposalId);
    if (deadline - block.number < voteExtension && _quorumReached(proposalId)) {
        // extend deadline
        _extendedDeadlines[proposalId] = block.number + voteExtension;
        emit ProposalExtended(proposalId, block.number + voteExtension);
    }
    
    return result;
}

Compound Governance uses analogous mechanism. Important for fairness, especially early stage with few active participants.

Frontend and UX

Tally and Boardroom as Ready Solutions

Tally.xyz and Boardroom.io — ready governance UIs. Support OpenZeppelin Governor and Governor Bravo. Free for public DAOs. Provide: proposal lists, voting interface, delegate directory, treasury view.

Minus: branding and customization limited. For embedded governance in own dApp — need custom UI.

Custom Governance UI

Key frontend data:

  • Proposal list: ProposalCreated events via The Graph subgraph
  • User voting power: token.getVotes(address) for current, token.getPastVotes(address, blockNumber) for snapshot
  • Delegate info: Tally API or own indexer

wagmi hooks for voting:

const { writeContract } = useWriteContract()

function castVote(proposalId: bigint, support: 0 | 1 | 2) {
  writeContract({
    address: GOVERNOR_ADDRESS,
    abi: governorAbi,
    functionName: 'castVoteWithReason',
    args: [proposalId, support, 'My reasoning here'],
  })
}

Timeline and Scope

Basic proposal and voting system on OpenZeppelin Governor — 1-2 weeks contract development plus 1 week testing. Custom voting mechanics (fractional, quadratic) — +1-2 weeks. Frontend with full governance UI — 3-4 weeks.

Audit mandatory if Governor manages real funds. Typical governance contract audit scope — 1-2 weeks from qualified team.