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

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка системы revenue sharing (распределение доходов токен-холдерам)
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1056
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Разработка системы revenue sharing

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);
    }
}

Плюс: масштабируется на любое число холдеров, gas платит получатель. Минус: требует 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 balances.

Защита от 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. Если нужна гибкость (разные токены, разные правила eligibility) — гибридная схема с off-chain snapshot и on-chain verification.

Сроки разработки: 3-5 недель на базовую систему, 6-9 недель с multi-reward, anti-flash-loan защитой и frontend dashboard.