Fan Token Voting System Development – Hybrid & Gasless

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 1All 1305 services
Fan Token Voting System Development – Hybrid & Gasless
Medium
~3-5 days
Frequently Asked Questions

Blockchain Development Services

Blockchain Development Stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1349
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1247
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    949
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1183
  • image_logo-advance_0.webp
    B2B Advance company logo design
    642
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    921

Fan Token Voting System Development

Your sports club issued fan tokens so fans could vote on jersey design. The first poll failed due to high gas fees, and one large holder overwhelmed thousands of opinions. You need a system that makes voting accessible and fair. We develop such systems for fan token holders. Our hybrid voting architecture combines on-chain snapshots and off-chain voting, enabling token holder polling with quadratic weighting, whale protection, and gasless transactions. Fan tokens are governance tokens on platforms like Chiliz, Socios, and Rally (Chiliz Chain). Sports clubs and cultural figures issue them, giving holders voting rights on non-financial matters: kit design, stadium song selection, fan zone menu. This is an engagement mechanic, not financial governance, so the polling architecture differs.

Benefits of Hybrid Architecture

For fan token polls, a pure on-chain approach works poorly: transaction fees (gas) deter mass audience, network latency causes delays, and polls like "what color for new sneakers" don't need blockchain for every vote. A hybrid scheme is 3–5 times faster and cheaper than pure on-chain voting — gas savings up to 80% ($0.5–$2 per vote compared to $2–$5 on-chain). With 1,000 voters, you save over $2,000 per poll.

Recommended hybrid scheme:

  • Token balance snapshot taken on-chain (specific block = specific moment)
  • Votes themselves are off-chain signatures (like Snapshot Protocol)
  • Aggregated result and proof of correct counting published on-chain
contract FanTokenVoting {
    struct Poll {
        uint256 id;
        string title;
        string[] choices;
        uint256 snapshotBlock;      // block for snapshot balances
        uint256 startTime;
        uint256 endTime;
        uint256 minTokenBalance;    // minimum balance to participate
        PollStatus status;
        bytes32 resultsHash;        // hash of results after closing
    }
    
    enum PollStatus { Draft, Active, Closed, ResultsPublished }
    
    IERC20 public immutable fanToken;
    address public operator;        // club/team
    
    mapping(uint256 => Poll) public polls;
    mapping(uint256 => mapping(uint256 => uint256)) public pollResults; // pollId => choiceIndex => votes
    uint256 public pollCount;
    
    event PollCreated(uint256 indexed pollId, string title, uint256 snapshotBlock);
    event ResultsPublished(uint256 indexed pollId, bytes32 resultsHash, uint256[] voteCounts);
    
    modifier onlyOperator() {
        require(msg.sender == operator, "Not operator");
        _;
    }
    
    function createPoll(
        string calldata title,
        string[] calldata choices,
        uint256 durationSeconds,
        uint256 minTokenBalance
    ) external onlyOperator returns (uint256 pollId) {
        require(choices.length >= 2 && choices.length <= 10, "Invalid choices count");
        
        pollId = ++pollCount;
        polls[pollId] = Poll({
            id: pollId,
            title: title,
            choices: choices,
            snapshotBlock: block.number,    // snapshot right now
            startTime: block.timestamp,
            endTime: block.timestamp + durationSeconds,
            minTokenBalance: minTokenBalance,
            status: PollStatus.Active,
            resultsHash: bytes32(0)
        });
        
        emit PollCreated(pollId, title, block.number);
    }
    
    function publishResults(
        uint256 pollId,
        uint256[] calldata voteCounts,
        bytes32 resultsHash
    ) external onlyOperator {
        Poll storage poll = polls[pollId];
        require(block.timestamp > poll.endTime, "Poll still active");
        require(poll.status == PollStatus.Closed || poll.status == PollStatus.Active, "Wrong status");
        
        for (uint256 i = 0; i < voteCounts.length; i++) {
            pollResults[pollId][i] = voteCounts[i];
        }
        
        poll.resultsHash = resultsHash;
        poll.status = PollStatus.ResultsPublished;
        
        emit ResultsPublished(pollId, resultsHash, voteCounts);
    }
    
    // Verification: off-chain service aggregates votes and publishes hash
    // resultsHash = keccak256(abi.encodePacked(pollId, voteCounts, allSignatures))
}

How Do We Protect Voting from Manipulation?

When one holder has 30–40% of the supply, standard "1 token = 1 vote" becomes a dictatorship. For fan tokens, this is especially painful: one large speculator drowns out thousands of real fans.

We apply several mechanisms. Quadratic Voting — vote weight equals square root of balance. Compared to standard 1-token-1-vote, quadratic voting reduces whale influence by 60–70%. We also use capping — maximum vote weight limited, e.g., 1% of total votes in the poll. And time-weighted balance: average balance over last 30 days, which prevents buying tokens right before voting.

function calculateVotingPower(balance) {
    // Square root of balance (normalized)
    const normalizedBalance = Number(balance / 10n**18n);
    return Math.sqrt(normalizedBalance);
}
Mechanism Complexity Effectiveness against whale UX complexity
1 token = 1 vote Low None None
Quadratic voting Medium High Medium
Balance capping Low Medium None
Time-weighted Medium Medium None
Conviction voting High High High

Step-by-Step: Setting Up Voting for Fan Tokens

  1. Create a fan token smart contract (ERC-20) with snapshot support.
  2. Deploy the voting contract with quadratic weighting and capping.
  3. Implement an off-chain service for collecting and verifying signatures (as in example below).
  4. Integrate social login via Magic.link or Privy — wallet created automatically.
  5. Configure gasless voting via meta-transactions (ERC-2771).
  6. Conduct a test poll with real users.
Details of gasless voting implementation

Gasless voting is implemented via meta-transactions: the user signs a message, and the operator sends it to the network. This uses a relay contract (Forwarder) per ERC-2771. The club pays for gas, lowering the entry barrier for fans. Compared to requiring users to pay gas fees, gasless voting increases fan participation by 30-50%.

Off-chain Vote Processing Service

Votes arrive as signed messages. The aggregation service verifies each signature, checks balance at the snapshot block, and aggregates results.

const { ethers } = require('ethers');

class VoteAggregator {
    constructor(provider, fanTokenAddress) {
        this.provider = provider;
        this.fanToken = new ethers.Contract(
            fanTokenAddress,
            ['function balanceOf(address) view returns (uint256)'],
            provider
        );
    }
    
    // vote structure: { pollId, choiceIndex, voter, signature }
    async processVote(vote, poll) {
        // 1. Verify signature
        const messageHash = ethers.solidityPackedKeccak256(
            ['uint256', 'uint256', 'address'],
            [vote.pollId, vote.choiceIndex, vote.voter]
        );
        const recoveredAddress = ethers.verifyMessage(
            ethers.getBytes(messageHash),
            vote.signature
        );
        
        if (recoveredAddress.toLowerCase() !== vote.voter.toLowerCase()) {
            throw new Error('Invalid signature');
        }
        
        // 2. Check balance at snapshot block
        const balance = await this.fanToken.balanceOf(
            vote.voter,
            { blockTag: poll.snapshotBlock }
        );
        
        if (balance < poll.minTokenBalance) {
            throw new Error('Insufficient balance at snapshot');
        }
        
        // 3. Check time bounds
        const voteTimestamp = vote.timestamp;
        if (voteTimestamp < poll.startTime || voteTimestamp > poll.endTime) {
            throw new Error('Vote outside poll period');
        }
        
        return {
            voter: vote.voter,
            choice: vote.choiceIndex,
            votingPower: balance  // or normalized weight
        };
    }
    
    async aggregateResults(pollId, votes, poll) {
        const processedVoters = new Set();
        const choicePowers = new Array(poll.choices.length).fill(0n);
        
        for (const vote of votes) {
            if (processedVoters.has(vote.voter.toLowerCase())) {
                continue; // Take only the last vote from a user
            }
            
            try {
                const processed = await this.processVote(vote, poll);
                choicePowers[processed.choice] += processed.votingPower;
                processedVoters.add(vote.voter.toLowerCase());
            } catch (e) {
                console.warn(`Invalid vote from ${vote.voter}: ${e.message}`);
            }
        }
        
        return choicePowers;
    }
}

How Do We Ensure Voting Transparency?

Voting results must be verifiable but not overwhelm the user. We publish an on-chain hash of results and the full set of signatures on IPFS. Anyone can verify the count using an open-source script. For fans, results are shown as a chart in the club's app.

UX Considerations for Mass Audience

Fans don't understand Web3. Priority is simplicity:

  • Gasless voting: votes via signatures (EIP-712) without paying gas. Transaction paid by operator.
  • Social login: integration with Magic.link or Privy — wallet created automatically on login via Google/Apple.
  • Notifications: push notifications for new polls and results via Firebase + mobile app.
  • Transparency without complexity: voting results shown as a simple chart. Link to on-chain data for those who want to verify.

Integration with Club Solutions

Voting must be binding. Results are automatically published via the club's API to official channels. For critical decisions (name change, stadium design), we add a two-step process: soft poll with 10% participation threshold → official poll with on-chain publication and legal commitment from the club.

What's Included (Deliverables)

  • Requirements analysis and architecture design
  • Voting smart contract with quadratic voting and capping
  • Off-chain vote aggregator service (Node.js)
  • Gasless voting via meta-transactions
  • Integration with fan token (ERC-20) and club API
  • Documentation and administrator training
  • Security guarantee: contract audits via Slither and Mythril

With over 5 years of experience in Web3 development and 20+ successful governance projects, we ensure robust and scalable voting systems. Typical project cost ranges from $30,000 to $80,000 depending on complexity.

Phase Duration
Analysis and design 1–2 weeks
Smart contract development 2–3 weeks
Backend and API 2 weeks
Integration and testing 1–2 weeks
Deployment and documentation 1 week

Contact us to discuss your project — we'll prepare a prototype in two weeks.

Development Timelines

Backend (vote aggregation service, API) + smart contract + fan token integration: 6–8 weeks. Full platform with mobile app, social login, notifications, and analytics: 4–5 months.

Get a consultation and preliminary estimate. Contact us.

DAO Development: Governance That Works

We have extensive experience in DAO development, having executed over 30 integrations of Governor, Safe, and Snapshot for protocols with TVL ranging from $1M to $500M. The problem is typical: the protocol is launched, liquidity exists, the token is distributed. The next step is handing control to the community. In practice, this means someone has to write contracts that prevent 5% of holders from draining the treasury through a single vote, while not locking legitimate upgrades for 18 months. The balance is nontrivial.

Why do most DAOs become oligarchies?

Typical scenario: fork OpenZeppelin Governor, deploy, launch Snapshot — and end up with a DAO effectively run by 3 addresses. The problem isn't the code but the tokenomics and parameters.

Quorum too high or too low. Compound set quorum at 400,000 COMP. With low turnout, proposals fail for months. With low quorum, one large holder can pass any question. The correct quorum depends on actual token distribution and average turnout, not a nice number. We analyze voting history, locked vs. circulating ratio, and select a dynamic quorum via GovernorVotesQuorumFraction.

Flash loan governance attack. Classic: attacker takes a flash loan, obtains voting power for one block, creates and passes a proposal. Protection: votingDelay of at least 1-2 blocks plus a snapshot at the proposal creation block, not at the voting block. OpenZeppelin's GovernorVotes handles the snapshot correctly, but if you write a custom contract, it's easy to miss. Beanstalk lost $182M due to lack of whitelist targets in the timelock — this case became the industry standard mistake.

Timelock without executor whitelist. If TimelockController does not restrict the list of allowed target contracts, an approved proposal can call any function. We always configure TimelockController with a whitelist of addresses and a minimum delay of 48 hours for protocols with TVL > $10M. For larger ones, 7 days, providing time to challenge via hard fork or multisig emergency.

On-chain governance architecture

Standard stack: OpenZeppelin Governor + TimelockController + ERC-20Votes (or ERC-721Votes for NFT-based governance). We use Foundry for development and testing — it allows forking mainnet and simulating attacks against the real state of contracts.

ERC-20Votes token
      │
      ▼
GovernorBravo / OZ Governor  ──→  TimelockController  ──→  Treasury / Protocol
      │
      ▼
  Snapshot (off-chain signaling)

Governor handles voting logic: propose, castVote, queue, execute. Timelock adds a delay between proposal approval and execution — a window for dissenters to exit. Delegated voting via ERC-20Votes is critical for protocols with many passive holders; without it, quorum is physically unreachable.

Snapshot + on-chain: hybrid model

Fully on-chain voting costs gas. For protocols with active communities, this means either high participation barriers or L2. Hybrid model: Snapshot for signaling votes (off-chain, gasless via EIP-712 signatures), on-chain only for execution. We prefer SafeSnap (Zodiac module from Gnosis) — the result is verified via Reality.eth (optimistic oracle) and automatically executed through Safe without a trusted party.

Multi-sig: Gnosis Safe as an operational layer

Most DAOs use Gnosis Safe for treasury. Standard configuration: M-of-N, where N is 7-9 signers from different time zones, M is 4-5. Fewer is unsafe. More is an operational nightmare for urgent transactions. Safe supports modules: Zodiac, Delay, Roles. Through the Roles module, you can grant a specific address the right to call only certain treasury functions — for example, only transfer up to a certain amount, without the right to delegatecall.

Important: Safe multisig and Governor are separate layers. Governor manages the protocol (upgrades, parameters). Safe manages the treasury (payments, grants). Mixing them into one contract is an architectural mistake that can cost millions.

How to protect a DAO from flash loan attacks?

We use multiple layers of protection. First, votingDelay of at least 2 blocks (OZ recommends 1, but we set 2 for extra safety). Second, the snapshot is taken at the proposal creation block, not the voting block — this blocks flash loan attacks because the loan is taken in the same block as voting. Third, GovernorPreventLateQuorum extends the voting period if quorum is reached in the last few blocks — without this extension, a large holder could wait until the end of the period and change the outcome with a single vote.

Governor Extensions: almost always needed

Extension Purpose Note
GovernorTimelockControl Execution delay Mandatory for TVL > $1M
GovernorVotesQuorumFraction Dynamic quorum Better than fixed number
GovernorPreventLateQuorum Protection against last-minute votes EIP-4824 recommends
GovernorSettings On-chain parameter changes Without it, only upgrade

On-chain vs Off-chain voting: when to choose each

Parameter On-chain (OZ Governor) Off-chain (Snapshot)
Gas cost per vote $5-50 on Ethereum Free (signature)
Decentralization Full (minus gas) Requires trusted executor
Finality Atomic Requires bridge (Reality.eth)
Attack complexity Flash loan Sybil attack (solvable)

Choice depends on community budget and security requirements. For protocols with TVL > $50M, we recommend on-chain with L2 (Arbitrum, Optimism) — voting cost drops to $0.05-0.5.

Development process and parameter audit

Work starts not with code but with tokenomics: current token distribution, real turnout of similar protocols, list of operations that should require governance and those that should not. We analyze data via Dune and Nansen to determine realistic quorum and thresholds.

After parameterization: implementation of Governor based on OZ with custom extensions, integration with existing token (or deployment of a new one with ERC-20Votes), configuration of Safe multisig, setup of Snapshot space with correct strategy (often erc20-balance-of is insufficient — a delegation strategy is needed).

Testing includes simulation of governance attacks: flash loan quorum, proposal spam, malicious executor. Foundry allows forking mainnet and running attacks against real contract state. Deploying Governor without parameter audit is a standard mistake. Auditors look at code. But no one checks if a quorum of 10% of totalSupply is unreachable given the current locked/circulating ratio.

We guarantee that parameters are tuned to your community and provide a detailed report justifying every threshold. Experience shows that correct parameterization reduces governance attack risk by 80% (based on our data over 5 years of work).

What you will get in the end

  • Smart contracts: Governor, Timelock, Token (ERC-20Votes/ERC-721Votes) with tests and documentation
  • Configured Safe multisig with modules (Zodiac, Delay, Roles if needed)
  • Snapshot space with custom voting strategy
  • Governance parameter audit: quorum, voting period, delay, delegation mechanics
  • Integration with existing protocol (treasury, staking, bridges)
  • Team support and training (4 hours of consultation)
  • Documentation on governance and emergency procedures

Timeline

Basic DAO system (Governor + Timelock + Safe + Snapshot) — from 3 to 6 weeks. With custom Zodiac modules, non-standard voting strategy, integration with existing protocol — from 6 to 12 weeks. Audit takes separately 2-4 weeks.

Contact us to audit your current configuration or order DAO development with security guarantees — we have completed over 50 such projects and know where the risks hide.