Розробка системи голосування для власників fan-токенів

Розробка системи голосування для власників fan-токенів Ваш спортивний клуб випустив fan-токени, щоб фанати голосували за дизайн форми. Перше ж опитування провалилося через високі газові комісії, а один великий власник перекрив думку тисяч. Потрібен механізм опитувань, який робить голосування дост

Напрямки блокчейн-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1441
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    998
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1267
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    713
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1003

Розробка системи голосування для власників fan-токенів

Ваш спортивний клуб випустив fan-токени, щоб фанати голосували за дизайн форми. Перше ж опитування провалилося через високі газові комісії, а один великий власник перекрив думку тисяч. Потрібен механізм опитувань, який робить голосування доступним і чесним. Ми — команда з 5-річним досвідом у Web3, розробили понад 15 систем голосування для власників fan-токенів. Fan-токени — це governance-токени на платформах Chiliz, Socios та Rally (Chiliz Chain). Спортивні клуби та діячі культури випускають їх, даючи власникам право голосу в нефінансових питаннях: дизайн комплекту, вибір пісні на стадіоні, меню в фанзоні. Це engagement-механіка, а не фінансовий governance, тому архітектура опитувань відрізняється.

Переваги гібридної архітектури

Для опитувань з fan-токенами чистий on-chain підхід працює погано: комісії за транзакції (газ) відлякують масову аудиторію, латентність мережі створює затримки, а опитування «який колір вибрати для нових кедів» не вимагають блокчейну для зберігання кожного голосу. Гібридна схема в 3–5 разів швидше і дешевше — економія на газі досягає 80% (це $0.5–$2 за кожен голос). Для опитування з 10 000 учасників економія складає $5,000–$20,000. Квадратичне голосування знижує вплив китів на 60–70% порівняно з голосуванням '1 токен = 1 голос'.

Рекомендована схема — hybrid:

  • Знімок балансів токенів береться on-chain (конкретний блок = конкретний момент)
  • Самі голоси — off-chain підписи (як у Snapshot Protocol)
  • Агрегований результат та доказ коректного підрахунку публікуються on-chain
contract FanTokenVoting { struct Poll { uint256 id; string title; string[] choices; uint256 snapshotBlock; // блок для snapshot балансов uint256 startTime; uint256 endTime; uint256 minTokenBalance; // мінімальний баланс для участі PollStatus status; bytes32 resultsHash; // хеш результатів після закриття } enum PollStatus { Draft, Active, Closed, ResultsPublished } IERC20 public immutable fanToken; address public operator; // клуб/команда 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 прямо сейчас 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); } // Верифікація: off-chain сервіс агрегує голоси та публікує хеш // resultsHash = keccak256(abi.encodePacked(pollId, voteCounts, allSignatures)) } 

Захист голосування від маніпуляцій

Зазначимо: коли один власник має 30–40% емісії, стандартне голосування «1 токен = 1 голос» перетворюється на диктатуру. Для fan-токенів це особливо болісно: один великий спекулянт перекриває тисячі реальних фанатів.

Ми застосовуємо кілька механізмів. Квадратичне голосування (Quadratic Voting) — вага голосу = квадратний корінь з балансу. 10 000 токенів дають вагу 100, а не 10 000. Це знижує вплив китів на 60–70%. Також використовуємо capping — максимальна вага голосу обмежена, наприклад, 1% від загальної кількості голосів в опитуванні. І time-weighted balance: враховується середній баланс за останні 30 днів, що перешкоджає купівлі токенів перед голосуванням.

function calculateVotingPower(balance) { // Квадратний корінь з балансу (нормалізованого) const normalizedBalance = Number(balance / 10n**18n); return Math.sqrt(normalizedBalance); } 
Механізм Складність Ефективність проти whale Складність для UX
1 токен = 1 голос Низька Немає Ні
Quadratic voting Середня Висока Середня
Balance capping Низька Середня Ні
Time-weighted Середня Середня Ні
Conviction voting Висока Висока Висока

Як налаштувати голосування для fan-токенів: покрокова інструкція

  1. Створіть смарт-контракт fan-токена (ERC-20) з підтримкою знімків балансів.
  2. Розгорніть контракт голосування з квадратичним зважуванням та capping.
  3. Реалізуйте off-chain сервіс для збору та верифікації підписів (як у прикладі нижче).
  4. Інтегруйте social login через Magic.link або Privy — гаманець створюється автоматично.
  5. Налаштуйте gasless voting через meta-transactions (ERC-2771).
  6. Проведіть тестове опитування з реальними користувачами.
Деталі реалізації gasless voting

Gasless voting реалізується через мета-транзакції: користувач підписує повідомлення, а оператор відправляє його в мережу. Для цього використовується контракт-релей (Forwarder) за стандартом ERC-2771. Вартість газу бере на себе клуб — це знижує поріг входу для фанатів і підвищує залученість у 1.3–1.5 рази порівняно з традиційним on-chain голосуванням.

Off-chain сервіс обробки голосів

Голоси надходять як підписані повідомлення. Сервіс агрегації верифікує кожен підпис, звіряє баланс на snapshot-блоці та агрегує результати.

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: { pollId, choiceIndex, voter, signature } async processVote(vote, poll) { // 1. Верифікуємо підпис 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. Перевіряємо баланс на snapshot блоці const balance = await this.fanToken.balanceOf( vote.voter, { blockTag: poll.snapshotBlock } ); if (balance < poll.minTokenBalance) { throw new Error('Insufficient balance at snapshot'); } // 3. Перевіряємо часові рамки 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 // або нормалізована вага }; } 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; // Беремо лише останній голос користувача } 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; } } 

Як забезпечити прозорість голосування?

Результати опитування мають бути перевірними, але не перевантажувати користувача. Ми публікуємо on-chain хеш результатів та повний набір підписів в IPFS. Будь-хто може верифікувати підрахунок, використовуючи open-source скрипт. Для фанатів підсумки відображаються як діаграма в додатку клубу.

Особливості UX для масової аудиторії

Фанати не розбираються в Web3. Пріоритет — простота:

  • Gasless voting: голоси через підписи (EIP-712) без оплати gas. Транзакцію оплачує оператор.
  • Social login: інтеграція з Magic.link або Privy — гаманець створюється автоматично при вході через Google/Apple.
  • Сповіщення: push-сповіщення про нові опитування та результати через Firebase + мобільний додаток.
  • Прозорість без складності: результати опитування — проста діаграма. Посилання на on-chain дані для тих, хто хоче перевірити.

Як інтегрувати результати голосування з клубними рішеннями?

Опитування має мати обов'язкову силу. Результати автоматично публікуються через API клубу в офіційні канали. Для критичних рішень (зміна назви, дизайн стадіону) додаємо двоетапний процес: soft poll з порогом участі 10% → official poll з on-chain публікацією та юридичним зобов'язанням клубу.

Що входить в роботу (deliverables)

  • Аналіз вимог та проєктування архітектури
  • Смарт-контракт голосування з квадратичним голосуванням та capping
  • Off-chain vote aggregator сервіс (Node.js)
  • Gasless voting через meta-transactions
  • Інтеграція з fan-токеном (ERC-20) та клубним API
  • Документація та навчання адміністраторів
  • Гарантія безпеки: аудит контрактів через Slither та Mythril
Етап Тривалість
Аналітика та проєктування 1–2 тижні
Розробка смарт-контракту 2–3 тижні
Backend та API 2 тижні
Інтеграція та тестування 1–2 тижні
Деплой та документація 1 тиждень

Замовте розробку — ми підготуємо прототип за два тижні. Вартість базового рішення — від $15,000.

Терміни розробки

Backend (vote aggregation service, API) + смарт-контракт + інтеграція з fan-токеном: 6–8 тижнів. Повноцінна платформа з мобільним додатком, social login, сповіщеннями та аналітикою: 4–5 місяців.

Зв'яжіться з нами, щоб обговорити ваше завдання. Отримайте консультацію та попередню оцінку.