Reputation-Weighted Voting 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
Reputation-Weighted Voting System Development
Complex
~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

Developing reputation-weighted voting system

Token-weighted voting has a fundamental flaw: buying voting power with money. A wealthy participant doesn't need to understand the subject — they just override everyone. Reputation-weighted voting solves this differently: voting weight is determined by history of participation, quality of past decisions, and protocol contribution, not wallet balance.

This is significantly more complex to implement. You need to solve three non-trivial problems: how to measure reputation without manipulation, how to store and update it on-chain efficiently, and how to prevent reputation accumulation through sybil attacks.

Reputation models: where weight comes from

Reputation can be built from multiple sources simultaneously.

On-chain activity: frequency and quality of votes, timeliness of participation, following decisions (voted with majority vs contrarian). The last is debatable — sometimes the dissenters are right.

Contribution-based: merged PRs into protocol, written proposals, organized community calls, written documentation. Entering contributions is harder to automate, requires subjective evaluation.

Peer review: other participants rate contributions, reputation builds socially. This is the SourceCred model — graph distribution of reputation through mutual ratings.

Outcome-based: retroactive evaluation of decisions. If a proposal the participant voted for brought measurable benefit to the protocol — their reputation weight grows. Requires a metrics oracle.

Soulbound tokens as reputation carrier

EIP-5114 (Soulbound tokens) — non-transferable NFTs tied to an address. Ideal reputation carrier: can't be bought and sold, can't delegate someone else's reputation.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract ReputationToken {
    struct ReputationData {
        uint256 baseScore;           // accumulated reputation
        uint256 participationCount;  // number of votes
        uint256 proposalsCreated;    // created proposals
        uint256 proposalsPassed;     // passed proposals
        uint256 lastActivityBlock;
        uint256 decayFactor;         // decay multiplier (1000 = 1.0)
        bool exists;
    }
    
    mapping(address => ReputationData) public reputation;
    mapping(address => bool) public trustedIssuers;  // who can issue reputation
    
    uint256 public constant DECAY_PERIOD = 180 days;
    uint256 public constant DECAY_RATE = 50;    // 5% per period
    uint256 public constant BASE_VOTE_SCORE = 10;
    uint256 public constant PROPOSAL_BONUS = 100;
    uint256 public constant PASSED_PROPOSAL_BONUS = 500;
    
    event ReputationEarned(address indexed participant, uint256 amount, string reason);
    event ReputationDecayed(address indexed participant, uint256 newScore);
    
    modifier onlyIssuer() {
        require(trustedIssuers[msg.sender], "Not a trusted issuer");
        _;
    }
    
    // Soulbound: transfer is blocked at contract level
    // Implement through ERC-721 without transfer function
    
    function awardParticipation(address participant, uint256 pollId) external onlyIssuer {
        _ensureExists(participant);
        _applyDecay(participant);
        
        ReputationData storage rep = reputation[participant];
        rep.baseScore += BASE_VOTE_SCORE;
        rep.participationCount++;
        rep.lastActivityBlock = block.number;
        
        emit ReputationEarned(participant, BASE_VOTE_SCORE, "participation");
    }
    
    function awardProposalCreation(address participant, bool passed) external onlyIssuer {
        _ensureExists(participant);
        _applyDecay(participant);
        
        ReputationData storage rep = reputation[participant];
        uint256 bonus = passed ? PASSED_PROPOSAL_BONUS : PROPOSAL_BONUS;
        rep.baseScore += bonus;
        rep.proposalsCreated++;
        if (passed) rep.proposalsPassed++;
        rep.lastActivityBlock = block.number;
        
        emit ReputationEarned(participant, bonus, passed ? "passed_proposal" : "created_proposal");
    }
    
    function awardContribution(
        address participant, 
        uint256 amount,
        string calldata reason
    ) external onlyIssuer {
        _ensureExists(participant);
        _applyDecay(participant);
        reputation[participant].baseScore += amount;
        emit ReputationEarned(participant, amount, reason);
    }
    
    function getVotingPower(address participant) external view returns (uint256) {
        if (!reputation[participant].exists) return 0;
        
        ReputationData memory rep = reputation[participant];
        uint256 currentScore = _calculateCurrentScore(participant);
        
        // Nonlinear scaling: prevents monopolization
        // power = sqrt(score) * 100, normalized to base 1000
        return _sqrt(currentScore) * 100;
    }
    
    function _applyDecay(address participant) internal {
        ReputationData storage rep = reputation[participant];
        if (!rep.exists) return;
        
        uint256 inactiveTime = block.timestamp - 
            (rep.lastActivityBlock * 12); // ~12 sec per block
        
        if (inactiveTime > DECAY_PERIOD) {
            uint256 periods = inactiveTime / DECAY_PERIOD;
            uint256 decay = (1000 - DECAY_RATE) ** periods / (1000 ** (periods - 1));
            rep.baseScore = rep.baseScore * decay / 1000;
            emit ReputationDecayed(participant, rep.baseScore);
        }
    }
    
    function _calculateCurrentScore(address participant) internal view returns (uint256) {
        ReputationData memory rep = reputation[participant];
        uint256 inactiveTime = block.timestamp - (rep.lastActivityBlock * 12);
        
        if (inactiveTime <= DECAY_PERIOD) return rep.baseScore;
        
        uint256 periods = inactiveTime / DECAY_PERIOD;
        uint256 score = rep.baseScore;
        for (uint256 i = 0; i < periods && score > 0; i++) {
            score = score * (1000 - DECAY_RATE) / 1000;
        }
        return score;
    }
    
    function _sqrt(uint256 x) internal pure returns (uint256) {
        if (x == 0) return 0;
        uint256 z = (x + 1) / 2;
        uint256 y = x;
        while (z < y) {
            y = z;
            z = (x / z + z) / 2;
        }
        return y;
    }
    
    function _ensureExists(address participant) internal {
        if (!reputation[participant].exists) {
            reputation[participant].exists = true;
            reputation[participant].decayFactor = 1000;
            reputation[participant].lastActivityBlock = block.number;
        }
    }
}

Decay mechanism: fighting accumulated power

Without decay, reputation accumulates and never disappears. A participant active three years ago retains dominant weight forever. This violates inclusiveness: new participants can never enter the decision-making circle.

Decay is a key element of reputation systems. Reputation decreases with inactivity. The idea: you must continue participating to maintain influence.

Linear decay: simplest variant. -X% per month without activity. Predictable, but crude.

Exponential decay: more natural model. Reputation decreases faster with prolonged inactivity. Parameters are tuned to the community.

Activity-gated decay: decay starts only after inactivity threshold (e.g., missing more than 3 votes in a row). Reduces anxiety for occasional misses.

# Decay modeling for parameter tuning
import numpy as np
import matplotlib.pyplot as plt

def simulate_reputation(
    initial_score: float,
    monthly_activity_score: float,   # reputation earned by active participant
    decay_rate: float,               # decay coefficient per inactivity period
    decay_period_months: int,
    simulation_months: int = 60,
    active_months: list = None       # months with activity
) -> list:
    """
    Simulate participant reputation
    active_months: list of months when participant was active
    """
    if active_months is None:
        active_months = list(range(simulation_months))
    
    score = initial_score
    history = [score]
    
    inactive_periods = 0
    
    for month in range(1, simulation_months):
        if month in active_months:
            score += monthly_activity_score
            inactive_periods = 0
        else:
            inactive_periods += 1
            if inactive_periods >= decay_period_months:
                score *= (1 - decay_rate)
        
        history.append(max(0, score))
    
    return history

# Example: participant active first 12 months, disappears for a year, returns
active = list(range(12)) + list(range(24, 36))
scores = simulate_reputation(
    initial_score=0,
    monthly_activity_score=50,
    decay_rate=0.08,      # 8% per inactivity period
    decay_period_months=3,
    simulation_months=48,
    active_months=active
)

Good parameter tuning ensures: active participant maintains stable score, three months inactivity reduces score by ~20–30%, return to participation allows quick position recovery.

Voting Power Computation: nonlinear scaling

Linear reputation-to-voting-power mapping reproduces token-weighted voting problem: whoever has most reputation dominates completely.

Square root: voting_power = √(reputation_score). Classic quadratic voting. Person with 10,000 reputation has power 100, person with 100 reputation has power 10. Gap persists, but proportionally smaller.

Logarithmic scale: voting_power = log2(reputation_score + 1) * scale. Even more smoothed. For very large reputation spread — optimal choice.

Threshold tiers: reputation divides participants into categories (Junior, Member, Senior, Core). Inside category — equal voting power, but different rights (create proposals, veto, etc).

Reputation Tier Rights
0–99 Observer Voting only
100–499 Participant Vote + proposal comments
500–1999 Member Create proposals
2000–9999 Core Veto right, parameter changes
10000+ Maintainer Emergency actions

Reputation Delegation

Reputation can't be sold (soulbound), but voting power can be delegated — similar to delegated voting in Governor Bravo. Delegate votes on your behalf, but reputation stays with you.

contract ReputationDelegation {
    mapping(address => address) public delegates;
    mapping(address => uint256) public delegatedPower;
    
    function delegate(address delegatee) external {
        address previousDelegate = delegates[msg.sender];
        uint256 myPower = reputationToken.getVotingPower(msg.sender);
        
        // Remove delegation from previous delegate
        if (previousDelegate != address(0)) {
            delegatedPower[previousDelegate] -= myPower;
        }
        
        delegates[msg.sender] = delegatee;
        
        if (delegatee != address(0)) {
            delegatedPower[delegatee] += myPower;
        }
        
        emit DelegateChanged(msg.sender, previousDelegate, delegatee);
    }
    
    function getEffectivePower(address account) public view returns (uint256) {
        return reputationToken.getVotingPower(account) + delegatedPower[account];
    }
}

Delegation is critical for protocols with technical decisions: many token holders can't dive into proposal details, they delegate to trusted experts.

Integration with Governor

OpenZeppelin Governor supports custom voting power calculation through IVotes interface. Reputation contract must implement it:

contract ReputationVotes is IVotes, ReputationToken {
    function getVotes(address account) external view override returns (uint256) {
        return getEffectivePower(account);
    }
    
    function getPastVotes(address account, uint256 blockNumber) 
        external view override returns (uint256) 
    {
        // For accuracy need checkpoint mechanism
        return _getPastVotingPower(account, blockNumber);
    }
    
    function getPastTotalSupply(uint256 blockNumber) 
        external view override returns (uint256) 
    {
        return _getPastTotalPower(blockNumber);
    }
    
    // Checkpoint: snapshots of voting power for each block
    // Use ERC20Votes pattern from OpenZeppelin
}

Checkpoint mechanism is mandatory. Governor checks voting power at proposal snapshot block, not current block. Without checkpoints the system doesn't work correctly.

Anti-sybil protection

Reputation systems are especially vulnerable to sybil attacks: creating 100 addresses with minimal reputation is easier than building high reputation on one account.

Proof of Humanity / Worldcoin: uniqueness verification via biometrics. Each verified person gets one reputation account. Harsh, but effective.

Gitcoin Passport: soft approach. Aggregates identity proofs from many sources (ENS, GitHub, Twitter, Lens, etc). More sources = higher "humanness score". Minimum threshold for reputation eligibility.

Social graph analysis: participants with identical voting patterns may be one person. Statistical on-chain behavior analysis.

Stake-based admission: to earn initial reputation requires staking a small amount. Creates financial barrier for sybil while not expensive for real accounts.

Governance parameters and tuning

Reputation-weighted governance requires careful numerical parameter tuning. Wrong values lead to either centralization (few high-reputation participants control everything) or paralysis (reputation too evenly distributed, quorum unreachable).

Quorum: minimum % of total voting power for validity. For reputation-weighted systems typically lower (5–10%) than token-weighted (needing 4–20% circulating supply).

Proposal threshold: minimum voting power to create proposal. Should be enough to filter spam but not exclude new active participants.

Timelock: mandatory. Even with decentralized governance, community reaction time before execution is critical.

// Example configuration for medium-sized DAO
const governanceConfig = {
    // Reputation parameters
    baseVoteScore: 10,              // points per vote
    proposalCreationBonus: 100,
    passedProposalBonus: 500,
    contributionMultiplier: 1.5,    // x for verified contributions
    
    // Decay
    decayPeriodDays: 90,            // 3 months inactivity = decay starts
    decayRatePerPeriod: 0.07,       // 7% per inactivity period
    
    // Voting
    quorumPercent: 8,               // 8% of total voting power
    proposalThreshold: 200,         // min score to create proposal
    votingPeriodDays: 7,
    timelockDays: 2,
    
    // Anti-sybil
    minPassportScore: 15,           // Gitcoin Passport minimum score
    minStakeAmount: '100',          // 100 tokens for admission
};

Monitoring and analytics

Reputation-weighted system requires constant governance health monitoring:

Concentration: Gini index of voting power. If Gini > 0.7 — system is de-facto centralized.

Participation rate: % of eligible voters participating in average proposal. Target — 20–40%.

New entrant mobility: how fast active newcomer accumulates meaningful voting power. If it takes >12 months — system is closed to newcomers.

Proposal success rate: % of proposals reaching quorum. Too low (< 50%) — quorum too high. Too high (> 95%) — quorum too low or community too uniform.

Stack and development timeline

Smart contracts: Solidity + OpenZeppelin Governor + custom IVotes. Hardhat/Foundry for testing. 10–14 weeks including checkpoint mechanism and tests.

Off-chain infrastructure: indexer (The Graph subgraph) for reputation history, API for frontend, service for off-chain contribution reputation.

Frontend: DAO dashboard with reputation profiles, proposal lifecycle, analytics. React + wagmi.

Audit: mandatory. Reputation earning and decay logic — potential attack point.

Full development cycle: 5–8 months for production-ready system with audit.