Розробка системи revenue sharing (розподіл доходів токен-холдерам)

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка системи revenue sharing (розподіл доходів токен-холдерам)
Середній
~3-5 днів
Часті запитання

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

Етапи блокчейн-розробки

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

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

Розробка системи розподілу доходів для власників токенів

Revenue sharing — механізм розповсюження доходу протоколу між власниками токенів. Звучить просто, але реалізація містить кілька нетривіальних завдань: як честиво розраховувати долю кожного власника, як ефективно розповсюджувати між тисячами адрес, що робити з невиконаним доходом та як уникнути flash loan атак.

Моделі розповсюження

Continuous streaming (Superfluid/Sablier)

Дохід «течет» до власників постійно пропорційно балансу. Теоретично ідеально — практично складно: при кожному трансфері токена потрібно перераховувати потоки. На Ethereum це дорого при великій кількості власників.

Snapshot + Merkle Distribution

Найпоширеніша модель. Раз на період (тиждень/місяць) робиться snapshot балансів, розраховується кожна доля, будується Merkle tree. Власники самі забирають свою частку, надаючи Merkle proof.

contract RevenueDistributor {
    IERC20 public immutable rewardToken;
    bytes32 public merkleRoot;
    uint256 public distributionId;
    mapping(uint256 => mapping(address => bool)) public claimed;

    function setDistribution(bytes32 _root) external onlyOwner {
        distributionId++;
        merkleRoot = _root;
        emit DistributionSet(distributionId, _root);
    }

    function claim(
        uint256 amount,
        bytes32[] calldata proof
    ) external {
        require(!claimed[distributionId][msg.sender], "Already claimed");
        bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount));
        require(MerkleProof.verify(proof, merkleRoot, leaf), "Invalid proof");
        claimed[distributionId][msg.sender] = true;
        rewardToken.transfer(msg.sender, amount);
        emit Claimed(distributionId, msg.sender, amount);
    }
}

Плюс: масштабується на будь-яку кількість власників, користувач платить газ. Мінус: потребує off-chain інфраструктури для snapshot та генерації Merkle tree.

Dividend-bearing token (редистрибуція через індекс)

Модель MasterChef / Synthetix Rewards: контракт зберігає rewardPerTokenStored. При кожному новому поступленні доходу індекс оновлюється. При claim користувач отримує різницю між поточним індексом та тим, що був при останньому claim.

uint256 public rewardPerTokenStored;
mapping(address => uint256) public userRewardPerTokenPaid;
mapping(address => uint256) public rewards;

function rewardPerToken() public view returns (uint256) {
    if (totalStaked == 0) return rewardPerTokenStored;
    return rewardPerTokenStored + (
        (rewardRate * (block.timestamp - lastUpdateTime) * 1e18) / totalStaked
    );
}

function earned(address account) public view returns (uint256) {
    return (
        (balanceOf(account) * (rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18
    ) + rewards[account];
}

Це операція O(1) per user — не потрібно робити snapshot усіх власників. Ідеально для staking контрактів. Не підходить, якщо токени не застейковані — потрібно враховувати non-staked балансі.

Захист від flash loan атак

Найкритичніша проблема: зловмисник бере flash loan на огромну суму токенів, snapshot попадає в той же блок, він забирає непропорційно велику частку.

Рішення 1: Time-weighted balance. Snapshot розраховує не поточний баланс, а time-weighted average за період. Навіть якщо атакуючий має великий баланс у момент snapshot — це не допоможе, якщо до цього баланс був нульовим.

Рішення 2: Minimum holding period. Право на revenue sharing отримують тільки адреси, що тримають токени довше N днів. Реалізується через timestamp останнього transfer.

Рішення 3: Snapshot через Chainlink або commit-reveal. Момент snapshot не відомий заздалегідь, визначається випадково або з затримкою. Атакуючий не може підготуватися.

// Запис timestamps останніх входящих переводів
mapping(address => uint256) public lastReceived;

function _afterTokenTransfer(address, address to, uint256) internal override {
    lastReceived[to] = block.timestamp;
}

// При snapshot: включаємо тільки адреси з lastReceived > N днів назад
function isEligible(address holder) public view returns (bool) {
    return lastReceived[holder] <= block.timestamp - MIN_HOLD_DURATION;
}

Мультитокенне розповсюження

Протокол часто генерує дохід у різних токенах: ETH від trading fees, USDC від стабілізаційного механізму, native токен з emission. Система повинна агрегувати всі це.

Варіант 1: все конвертується у единий reward token (наприклад, USDC) перед розповсюженням. Просто для користувачів, потребує on-chain swap через DEX.

Варіант 2: multi-reward контракт з окремим індексом для кожного токена. Користувач забирає кілька токенів за одну транзакцію.

// Multi-reward: окремий rewardPerToken для кожного reward токена
mapping(address => uint256) public rewardPerTokenStored; // rewardToken => value
mapping(address => mapping(address => uint256)) public userRewardPerTokenPaid;

Off-chain інфраструктура для Merkle Distribution

Генерація Merkle tree — off-chain процес. Потрібен pipeline:

  1. Snapshot: запрос до archival node (Alchemy/Infura з archive access) для отримання балансів на конкретному block number
  2. Calculation: розрахунок доля кожної адреси пропорційно балансу
  3. Tree building: бібліотека @openzeppelin/merkle-tree
  4. Publishing: Merkle root постить on-chain, повне дерево в IPFS або публічний API
  5. Proof service: API для отримання proof по адресі

Весь pipeline повинен бути відтворюваним та верифікованим: будь-який учасник може самостійно перерахувати root та переконатися у коректності.

Практичні параметри вибору

Якщо власників < 1000 та дохід розповсюджується часто — Synthetix-style staking rewards. Якщо власників > 10,000 та розповсюження періодичне — Merkle distribution. Якщо потрібна гнучкість (різні токени, різні правила) — гібридна схема з off-chain snapshot та on-chain verification.

Терміни розробки: 3-5 тижнів на базову систему, 6-9 тижнів з multi-reward, anti-flash-loan захистом та frontend dashboard.