Fan Token Voting System Development – Hybrid & Gasless

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

Blockchain Development Services

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1441
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    998
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1267
  • image_logo-advance_0.webp
    B2B Advance company logo design
    713
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    1003

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.