Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

Setting Up a Quadratic Voting System for Content Prioritization

This guide provides a step-by-step implementation for a quadratic voting system using Solidity smart contracts and proof-of-personhood verification to create a democratic content feed.
Chainscore Š 2026
introduction
INTRODUCTION

Setting Up a Quadratic Voting System for Content Prioritization

Quadratic voting is a governance mechanism that allows participants to express the intensity of their preferences, making it ideal for prioritizing features, proposals, or content in a decentralized community.

Quadratic voting (QV) is a collective decision-making process where participants allocate a budget of voice credits to vote on multiple options. The key innovation is that the cost of a vote increases quadratically with the number of votes cast for a single option. For example, casting 1 vote costs 1 credit, 2 votes cost 4 credits, and 3 votes cost 9 credits. This pricing structure makes it expensive to concentrate all influence on one choice, encouraging voters to distribute their support across the issues they care about most. This system is particularly effective for content prioritization, as it surfaces consensus on what the community values highly without letting a few wealthy voters dominate.

To implement a QV system, you need a few core components: a voting contract to manage the logic, a token or credit system for allocating voting power, and a front-end interface for user interaction. The smart contract must calculate the quadratic cost of votes, enforce budget limits, and tally results. A common approach is to use a commit-reveal scheme to prevent strategic voting based on early results. For on-chain governance, platforms like Snapshot with custom strategies or frameworks like OpenZeppelin Governor can be adapted. The first step is to define the voting parameters: the total credit budget per voter, the list of content proposals, and the voting period.

Here is a simplified example of the core quadratic cost calculation in a Solidity smart contract. The function ensures a user cannot exceed their budget and calculates the credits spent based on the square of their votes.

solidity
function castVotes(uint256[] memory proposalIds, uint256[] memory votes) public {
    uint256 totalCost = 0;
    for (uint i = 0; i < proposalIds.length; i++) {
        totalCost += votes[i] * votes[i]; // Quadratic cost
    }
    require(totalCost <= credits[msg.sender], "Insufficient credits");
    credits[msg.sender] -= totalCost;
    // ... Record votes for each proposal
}

This logic ensures that spreading 10 credits across several proposals (e.g., 1+1+3 votes) yields more total influence than spending them all on one (only 3 votes for 9 credits).

For content prioritization, each piece of proposed content—like a new research topic, feature request, or grant proposal—becomes a voting option. Community members signal not just what they want, but how much they want it. After the vote, projects are ranked by their total quadratic voting score. This outcome is more nuanced than simple plurality voting because it mitigates tyranny of the majority and reduces the impact of sybil attacks (where one entity creates many accounts), as the quadratic cost makes such attacks prohibitively expensive. Platforms like Gitcoin Grants have successfully used QV to allocate millions in community funding, demonstrating its practical utility.

When setting up your system, key design decisions include: choosing between a native token or a non-transferable credit system for votes, determining if votes are weighted by reputation or equal for all, and deciding on a vote aggregation method (e.g., sum of squares). Security is paramount; the contract must be audited to prevent manipulation of credit balances or vote counts. For many teams, using an existing audited library like Compound's Bravo governor or a no-code tool like Collab.Land for token-gated voting is a safer starting point than building from scratch.

The final step is integrating the voting mechanism into your community's workflow. This could involve automating the creation of votes from forum posts, displaying real-time results on a dashboard, and executing on-chain actions based on outcomes. The true power of quadratic voting for content prioritization lies in its ability to create a meritocratic signal of community desire, leading to better-aligned roadmaps and more engaged contributors. By implementing QV, you move beyond simple polls to a dynamic system where the strength of collective preference directly shapes your project's direction.

prerequisites
SETUP GUIDE

Prerequisites

Before building a quadratic voting system, you need the right tools and foundational knowledge. This guide covers the essential software, accounts, and concepts required to follow the implementation tutorial.

To build a quadratic voting system, you'll need a basic development environment and access to blockchain infrastructure. Install Node.js (version 18 or later) and a package manager like npm or yarn. You will also need a code editor such as Visual Studio Code. For interacting with a blockchain, you must set up a crypto wallet; MetaMask is the most common choice for Ethereum Virtual Machine (EVM) chains. Ensure you have a small amount of testnet ETH (e.g., from a Sepolia faucet) to pay for transaction gas fees during development and testing.

A foundational understanding of smart contract development is required. You should be comfortable with Solidity, the primary language for Ethereum smart contracts, including concepts like functions, modifiers, and state variables. Familiarity with the Hardhat or Foundry development frameworks is highly recommended, as they provide testing environments and deployment scripts. Knowledge of OpenZeppelin Contracts is beneficial for leveraging secure, audited base contracts like Ownable and ERC20. These tools form the backbone of secure and testable on-chain logic.

Quadratic voting's core mechanic requires calculating a square root on-chain, which is computationally expensive in Solidity. You will need to understand or implement a square root function. A common approach is to use a fixed-point math library, such as ABDKMathQuad or PRBMath, which provide efficient and precise methods for these calculations. Alternatively, you can use a pre-compiled contract on certain chains or approximate the calculation. Deciding on this mathematical implementation is a critical prerequisite that affects the gas cost and accuracy of your voting system.

Your application will need a way for users to connect their wallets and interact with the smart contract. You should have experience with a frontend framework like React, Vue, or Svelte. You will use a library such as ethers.js or viem to create a connection between the browser and the blockchain, call contract functions, and listen for events. Understanding how to manage user state, transaction signing, and error handling in this context is essential for creating a functional dApp interface.

Finally, consider the data and identity layer. A pure on-chain system requires voters to hold a specific token or NFT to participate. For more complex systems, you may need to integrate with identity verification protocols or decentralized credential systems like World ID or Ethereum Attestation Service (EAS) to prevent Sybil attacks—where a single user creates multiple identities to manipulate votes. Deciding on your anti-Sybil mechanism is a key design choice that must be made before writing your first line of code.

key-concepts-text
GOVERNANCE

How Quadratic Voting Works

Quadratic Voting (QV) is a collective decision-making mechanism designed to more accurately reflect the intensity of voter preferences, making it ideal for content prioritization and funding allocation in DAOs and community projects.

Quadratic Voting (QV) is a governance mechanism where participants allocate a budget of voice credits across multiple proposals. The key innovation is the quadratic cost function: the cost to cast n votes for a single option is n². This means one vote costs 1 credit, four votes cost 16 credits, and nine votes cost 81 credits. This exponential cost structure makes it expensive for any single participant to dominate a decision, forcing voters to spread their influence more broadly or pay a steep premium for strong preferences.

Setting up a QV system for content prioritization involves several core components. First, you need a voting contract that manages credit allocation and tracks votes. Second, a sybil resistance mechanism like proof-of-personhood (e.g., BrightID, Worldcoin) or token-gating is essential to prevent users from creating multiple identities to game the system. Finally, you need a clear proposal framework where community members can submit content ideas or tasks to be voted on. Platforms like Snapshot with QV plugins or custom-built solutions using libraries like MACI (Minimal Anti-Collusion Infrastructure) are common starting points.

Here is a simplified conceptual structure for a QV smart contract function written in Solidity. It calculates the cost of votes and checks a user's remaining credit balance.

solidity
function castVote(uint256 proposalId, uint256 voteCount) public {
    uint256 cost = voteCount * voteCount; // Quadratic cost: n²
    require(userCredits[msg.sender] >= cost, "Insufficient credits");
    userCredits[msg.sender] -= cost;
    votes[proposalId] += voteCount;
}

This function enforces the core rule: casting 5 votes costs 25 credits, not 5. In practice, you would add checks for sybil resistance and proposal validity.

For content prioritization, QV helps surface projects with broad, moderate support over those with intense support from a small faction. Imagine a developer DAO with 100 credit tokens per member. A niche technical proposal might get 5 votes each from 5 developers (cost: 5²5=125 credits). A broadly useful documentation update might get 2 votes each from 20 developers (cost: 2²20=80 credits). Despite the smaller per-person intensity for the docs, the broader support makes it the winner, efficiently allocating community resources to higher-impact work.

The main challenges in implementing QV are collusion and sybil attacks. Participants could coordinate to pool credits and direct them to a single proposal, bypassing the quadratic cost. Solutions include using MACI for private voting to make coordination harder, or implementing pairwise matching funds where a sponsor matches contributions in a way that preserves quadratic scaling. Regular vote rounds with clear timelines and a user-friendly interface are also critical for adoption and legitimate outcomes.

To deploy a QV system, follow these steps: 1) Choose a platform (Snapshot with qv-plugin, build with MACI-cli). 2) Define voting parameters: credit amount per voter, proposal submission process, and voting period. 3) Integrate sybil resistance. 4) Test thoroughly on a testnet with simulated proposals. 5) Run a pilot round with a small grant pool before full deployment. Resources include the CLR.fund documentation for quadratic funding implementations and EthResearch posts on MACI for advanced, collusion-resistant setups.

system-components
QUADRATIC VOTING

System Architecture Components

A modular breakdown of the core components needed to build a secure and efficient on-chain quadratic voting system for content or proposal prioritization.

04

Frontend & User Interface

The UI must clearly communicate the quadratic cost. A good interface shows a voter's remaining voice credit budget and updates the cost in real-time as they adjust sliders for different proposals. Integration with wallets (MetaMask, WalletConnect) is essential for signing votes.

  • Libraries: React/Vue with ethers.js or viem.
  • Critical Display: Cost = (Votes)^2 | Remaining Credits: 45
  • Features: Proposal browsing, vote history, and real-time result graphs.
step-1-smart-contract
SOLIDITY DEVELOPMENT

Step 1: Write the Core Voting Smart Contract

This guide walks through building the foundational smart contract for a Quadratic Voting system, which uses a square root calculation to allocate voting power.

A Quadratic Voting (QV) system allows participants to express the intensity of their preferences by allocating a budget of voice credits. The key mechanic is that the cost of votes increases quadratically: casting n votes for a single proposal costs n² voice credits. This design, popularized by projects like Gitcoin Grants, mitigates whale dominance and funds projects more proportionally to their community support. Our contract will manage proposal creation, track user credit balances, and enforce the quadratic cost function.

We'll use Solidity and the OpenZeppelin libraries for security and gas efficiency. Start by importing necessary contracts and defining the core data structures. We need a Proposal struct to store content metadata and vote tallies, and a mapping to track each user's remaining voice credits. The constructor will initialize the contract owner and set the total credit allocation per user, often distributed via an ERC-20 token or an airdrop.

The core function is vote(uint256 proposalId, uint256 voteAmount). It must: 1) Check the user has sufficient credits, 2) Calculate the cost as voteAmount², 3) Deduct the cost from the user's balance, and 4) Update the proposal's total vote count. Use SafeMath or Solidity 0.8's built-in checked math to prevent overflows. Implement a getCost(uint256 votes) view function so users can simulate costs before transacting.

For accurate results, the contract must calculate the square root of a user's total spent credits to determine their voting power impact. Since Solidity lacks a native sqrt function for integers, we implement one. A common and gas-efficient method is the Babylonian method (also known as Heron's method), which iteratively approximates the square root. We'll include a sqrt(uint256 x) internal pure function that handles this calculation.

Finally, add essential view functions: getProposal(uint256 id) to return proposal details, getUserCredits(address user) to check balances, and getResults(uint256 id) to compute the quadratic sum of votes (Σ √votes) for each proposal. Emit clear events like VoteCast and ProposalCreated for off-chain indexing. Thorough testing with a framework like Foundry or Hardhat is critical before deployment to a testnet.

step-2-sybil-prevention
SYBIL RESISTANCE

Step 2: Integrate Proof-of-Personhood

This step secures your quadratic voting system against Sybil attacks by integrating a decentralized proof-of-personhood protocol to verify unique human participants.

A quadratic voting (QV) system is inherently vulnerable to Sybil attacks, where a single entity creates multiple fake identities to manipulate outcomes. The cost of a vote increases quadratically (cost = credits²), but without identity verification, an attacker can simply split a large stake across many accounts to pay linear costs for quadratic influence. To prevent this, you must integrate a proof-of-personhood (PoP) protocol like Worldcoin, BrightID, or Proof of Humanity. These systems use biometrics, social graph analysis, or trusted attestations to issue a unique, non-transferable identity credential to each real human.

The integration involves checking for a valid PoP credential before allowing a user to purchase voting credits or cast votes. In a smart contract, this is typically a permission check. For example, using Worldcoin's Semaphore protocol for anonymity or its IDKit for verification. Your contract's mintCredits or castVote function would require a zero-knowledge proof that the caller holds a verified World ID, without revealing which specific ID. This ensures one-person-one-voice at the protocol level, making Sybil attacks economically impractical.

Here is a simplified conceptual outline for a Solidity function integrating PoP:

solidity
// Pseudocode integrating a proof-of-personhood verifier
function castVote(uint256 proposalId, uint256 voteCredits, bytes memory proof) public {
    // 1. Verify the proof-of-personhood
    require(poppVerifier.verifyProof(msg.sender, proof), "Invalid or missing PoP");
    
    // 2. Ensure user hasn't voted on this proposal
    require(!hasVoted[msg.sender][proposalId], "Already voted");
    
    // 3. Apply quadratic cost: cost = voteCredits²
    uint256 cost = voteCredits * voteCredits;
    require(creditBalance[msg.sender] >= cost, "Insufficient credits");
    
    // 4. Deduct credits and record vote
    creditBalance[msg.sender] -= cost;
    votes[proposalId] += voteCredits; // Tally
    hasVoted[msg.sender][proposalId] = true;
}

This structure guarantees that each vote's quadratic cost is enforced per unique human, not per wallet address.

For implementation, you must choose a PoP provider and integrate their verification contract. Worldcoin offers the WorldID.sol contract for on-chain verification. BrightID uses a social graph and requires users to register their address in a verified context. Consider the trade-offs: Worldcoin provides global scale with biometric verification, while BrightID offers privacy but requires an existing social connection. Your choice affects user onboarding friction and decentralization. Always query the provider's latest contract addresses from their official documentation, such as the Worldcoin Developer Portal.

After integration, test the system thoroughly. Use testnet versions of the PoP verifier to simulate attacks. Attempt to vote from two different addresses controlled by the same test identity; the system should reject the second attempt. This step transforms your quadratic voting mechanism from a theoretical model into a Sybil-resistant governance primitive. The combined system ensures that influence scales with the square of a participant's commitment (credits²) but is fundamentally allocated per human, creating a more equitable and secure method for community prioritization.

step-3-feed-algorithm
IMPLEMENTATION

Step 3: Build the Content Ranking Algorithm

This section details the implementation of a quadratic voting mechanism to prioritize content based on community sentiment, moving beyond simple like/dislike counts.

A quadratic voting system allows users to allocate a budget of voice credits across multiple content items. The key principle is that the cost of additional votes for a single item increases quadratically. For example, casting 1 vote costs 1 credit, 2 votes cost 4 credits, and 3 votes cost 9 credits. This design strongly discourages a single user from dominating the ranking of one piece of content, promoting a more balanced representation of the community's preferences. It's a mechanism used in governance platforms like Gitcoin Grants to fund public goods.

To implement this, you need a smart contract that manages user credit balances and vote tallies. The core function calculates the cost of a vote batch using the formula: cost = (new_total_votes² - current_total_votes²). Here is a simplified Solidity example for the voting logic:

solidity
function vote(uint256 contentId, uint256 voteAmount) public {
    uint256 currentVotes = userVotes[msg.sender][contentId];
    uint256 newVotes = currentVotes + voteAmount;
    uint256 cost = (newVotes * newVotes) - (currentVotes * currentVotes);
    
    require(credits[msg.sender] >= cost, "Insufficient credits");
    credits[msg.sender] -= cost;
    userVotes[msg.sender][contentId] = newVotes;
    totalVotes[contentId] += voteAmount;
}

This ensures the cost is always based on the square of the total votes cast by that user for that item.

The final ranking score for each content item is the sum of the square roots of all votes it received: score = Σ √(votes_from_user_i). This formula aggregates the intensity of support from many users rather than the raw vote count. An item with votes distributed as [4, 1, 1] (sqrt sum: 2+1+1=4) will rank similarly to an item with votes [16] (sqrt sum: 4), even though the raw totals are 6 and 16 respectively. This effectively flattens the influence of concentrated voting power. You can compute this off-chain via a subgraph from The Graph or an indexer that queries the totalVotes mapping.

For a production system, you must integrate Sybil resistance. Simply distributing credits via a wallet connection is insufficient. Common approaches include:

  • Proof-of-Personhood: Integrating with services like Worldcoin or BrightID to issue credits per unique human.
  • Stake-based: Requiring a stake of protocol tokens (e.g., ERC-20 tokens) that can be slashed for malicious behavior.
  • Reputation-based: Allocating credits based on past constructive participation within the platform. The choice depends on your application's threat model and desired decentralization.

Finally, the ranking algorithm must be gas-efficient. Batching votes (e.g., allowing users to vote on multiple items in one transaction) and using EIP-712 signed messages for off-chain vote aggregation can significantly reduce user costs. The on-chain contract would then only need to verify the signature and update state once per batch. The ranked list of content IDs, sorted by the quadratic score, can be served via an API from your indexer, ready for display in the frontend application.

step-4-frontend-integration
QUADRATIC VOTING SYSTEM

Develop the Frontend Interface

Build a React-based interface to interact with your smart contract, enabling users to submit proposals, allocate voice credits, and view results.

The frontend serves as the user's gateway to the quadratic voting protocol. For this guide, we'll use a React application with TypeScript, Vite for tooling, and wagmi/viem for blockchain interactions. Start by initializing a new project: npm create vite@latest frontend -- --template react-ts. Then, install the essential Web3 libraries: npm install wagmi viem @tanstack/react-query. Configure the Wagmi client in your main application file to connect to your local Hardhat node or a testnet like Sepolia, specifying your deployed QuadraticVoting contract address and ABI.

The core UI consists of three main components: a proposal submission form, a voting interface, and a results dashboard. The submission form should capture a proposal's title and description, calling the contract's createProposal function. The voting interface is the most critical; it must display all active proposals and allow users to allocate their voice credits using a quadratic formula. Implement a slider or input field where a user's vote cost is calculated as cost = votes². For example, casting 3 votes costs 9 credits. The UI must update the user's remaining credit balance in real-time.

To manage state and credit calculations efficiently, use React hooks like useState and useEffect. A key function will be calculateCost(voiceCredits, desiredVotes), which ensures the user cannot exceed their budget. When a user confirms their votes, the frontend should call the contract's vote function, passing the proposalId and the votePower (the square root of the spent credits). Use Wagmi's useWriteContract hook for the transaction and useWaitForTransactionReceipt to confirm it. Display transaction status and errors clearly to the user.

Finally, the results dashboard should fetch and display data from the contract's view functions. Use useReadContract to call getProposal and getAllProposals, displaying each proposal's id, title, totalVotes, and fundsAllocated. Since votes are stored as the square root of the actual voice credits, you must calculate the displayed total by summing the square of each vote power: totalVoiceCredits = Σ(votePower²). This dashboard provides transparency and allows the community to see how collective funding is being allocated based on the quadratic voting mechanism.

MECHANISM COMPARISON

Quadratic Voting vs. Other Mechanisms

A feature comparison of Quadratic Voting against common governance and prioritization systems.

Feature / MetricQuadratic Voting (QV)Simple Plurality (1p1v)Weighted Voting (Token-based)Conviction Voting

Cost to Influence Outcome

Quadratic (e.g., 4 votes = 16 credits)

Linear (1 vote = 1 credit)

Linear (1 token = 1 vote)

Linear (time-weighted stake)

Resistance to Sybil Attacks

Preference Intensity Capture

Small Holder Influence

High

Low

Very Low

Medium

Typical Gas Cost per Vote

Medium-High

Low

Low

Very High

Implementation Complexity

High (requires credit system)

Low

Low

High (requires time-locking)

Best For

Prioritizing community projects

Simple yes/no decisions

Capital-weighted governance

Continuous funding allocation

QUADRATIC VOTING

Frequently Asked Questions

Common technical questions and troubleshooting for developers implementing quadratic voting (QV) on-chain for content prioritization or governance.

Quadratic voting (QV) is a collective decision-making mechanism where participants allocate a budget of voice credits to vote on proposals. The key on-chain innovation is that the cost of votes increases quadratically. Casting n votes for a single proposal costs n² voice credits.

This is implemented using a commit-reveal scheme or a zk-SNARK to preserve privacy before tallying. Smart contracts, like those used by Gitcoin Grants or clr.fund, manage the credit distribution, voting, and final tally. The quadratic cost curve is enforced in the contract logic, ensuring a single user cannot dominate by spending all credits on one option, promoting more equitable fund allocation or content ranking.

conclusion
IMPLEMENTATION SUMMARY

Conclusion and Next Steps

This guide has walked through building a functional quadratic voting system for content prioritization. The next steps involve deploying the system, integrating it with a frontend, and considering advanced governance features.

You have now implemented the core components of a quadratic voting (QV) system using smart contracts on a blockchain like Ethereum or a Layer 2. The system allows users to allocate voice credits to proposals, with the cost of votes scaling quadratically (cost = votes²). This mechanism effectively surfaces community preferences by making it expensive to concentrate large amounts of voting power on a single option. The key contracts you've built or reviewed likely include a QuadraticVoting contract to manage proposals and voting, a VoiceCredit token or accounting system, and potentially a timelock or execution module for finalizing winning proposals.

To move from a local test environment to a live system, your next steps are deployment and integration. First, deploy your contracts to a testnet (e.g., Sepolia or Goerli) for final validation. Use a tool like Hardhat or Foundry for scripting the deployment. After testing, deploy to your chosen production network. You will then need to build or connect a frontend dApp. This interface should allow users to connect their wallet (using WalletConnect or similar), view active proposals, allocate their voice credits, and see real-time vote tallies. Frameworks like Next.js with wagmi and viem are excellent choices for this.

Consider enhancing your system with advanced features to increase its robustness and fairness. Implementing a Gitcoin Passport or similar sybil-resistance mechanism can help ensure one-person-one-voice credit allocation. Adding a timelock contract for executing winning proposals adds a security delay for community review. You might also explore batching vote transactions using solutions like EIP-4337 (Account Abstraction) to reduce user friction and gas costs. For ongoing governance, integrate with Snapshot for off-chain signaling or Tally for on-chain delegation and analytics.

Finally, monitor and iterate. Use subgraphs on The Graph to index and query voting data for transparent analytics. Solidity libraries like OpenZeppelin's Governor provide a standardized foundation you can extend. The complete code for this guide and further resources are available in the Chainscore Labs GitHub repository. By implementing quadratic voting, you create a more democratic and nuanced method for your community to prioritize work, allocate funds, or make collective decisions.