Разработка системы распределения прибыли фонда

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1306 услуг
Разработка системы распределения прибыли фонда
Сложный
~5 дней
Часто задаваемые вопросы

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

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

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1309
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1222
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    922
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1151
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    614
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    887

Разработка системы распределения прибыли фонда

Когда фонд фиксирует прибыль и нужно распределить её между LP, простой расчёт "делим пропорционально долям" не работает. Разные LP зашли в разные моменты по разным ценам, часть из них запросила partial redemption, кто-то получал interim distributions, у менеджера накопился performance fee. Система распределения прибыли должна быть математически точной, аудитабельной и устойчивой к манипуляциям. Разберём как это делается правильно.

Модели распределения прибыли

Прежде чем писать контракты, выбираем фундаментальную модель учёта долей:

Share-based (токенизированные доли)

LP получают ERC-20 shares при входе. Прибыль отражается через рост NAV per share — один share стоит больше, но количество shares не меняется. Распределение прибыли означает либо рост стоимости shares (capital appreciation), либо выплату дивидендов с уменьшением NAV per share.

Плюсы: стандарт EIP-4626, легко интегрируется с DeFi. Минусы: сложность при частичных выходах и interim distributions.

Point-based (накопительные очки)

Каждый LP накапливает "очки" пропорционально времени и сумме депозита. Прибыль делится пропорционально очкам. Подходит для yield-фондов с регулярными выплатами.

// Паттерн "rewards per token" — стандарт для point-based
contract ProfitDistributor {
    uint256 public rewardPerShareStored;
    uint256 public lastUpdateTime;
    uint256 public totalShares;

    mapping(address => uint256) public rewardPerSharePaid;
    mapping(address => uint256) public pendingRewards;
    mapping(address => uint256) public shares;

    modifier updateReward(address account) {
        rewardPerShareStored = rewardPerShare();
        lastUpdateTime = block.timestamp;
        if (account != address(0)) {
            pendingRewards[account] = earned(account);
            rewardPerSharePaid[account] = rewardPerShareStored;
        }
        _;
    }

    function earned(address account) public view returns (uint256) {
        return shares[account]
            * (rewardPerShare() - rewardPerSharePaid[account])
            / 1e18
            + pendingRewards[account];
    }
}

Этот паттерн ("Synthetix staking rewards") — проверенный способ эффективно считать накопленные rewards без итерации по всем LP.

Сложные случаи: что ломает простые системы

Входы и выходы в середине периода

Если LP зашёл в середине квартала, он не должен получать прибыль за период до его входа. И наоборот — при выходе в середине периода он должен получить свою долю начисленного, но ещё не распределённого.

Решение: snapshot-based distribution. При каждом изменении состояния (вход/выход LP) фиксируем checkpoint с текущим rewardPerShare. При расчёте используем разницу между текущим и checkpoint-значением:

function _updateCheckpoint(address lp) internal {
    uint256 currentRPS = rewardPerShareStored;
    uint256 lpShares = shares[lp];
    uint256 lastRPS = checkpoints[lp].rewardPerShare;

    if (lpShares > 0 && currentRPS > lastRPS) {
        uint256 accrued = lpShares * (currentRPS - lastRPS) / PRECISION;
        checkpoints[lp].pendingReward += accrued;
    }
    checkpoints[lp].rewardPerShare = currentRPS;
}

Performance fee с High Water Mark

Performance fee (обычно 20%) должна браться только с новой прибыли — выше предыдущего HWM. Это защита LP от двойного fee после просадки и восстановления.

Нюанс: HWM считается per-LP или globally? Два подхода:

Global HWM — один HWM для всего фонда. Простота, но несправедливость: LP который зашёл на пике не платит fee пока фонд не вернётся выше этого пика, даже если для нового LP это прибыль.

Per-LP HWM — у каждого LP свой HWM = NAV per share на момент его входа. Более справедливо, но сложнее в реализации и gas-затратно при многих LP.

mapping(address => uint256) public lpHighWaterMark; // NAV per share при входе LP

function calculatePerformanceFee(address lp, uint256 currentNAVPerShare)
    public view returns (uint256 feeAmount)
{
    uint256 hwm = lpHighWaterMark[lp];
    if (currentNAVPerShare <= hwm) return 0;

    uint256 profitPerShare = currentNAVPerShare - hwm;
    uint256 lpShareBalance = shares[lp];

    // fee = profit_per_share * lp_shares * fee_rate / PRECISION
    feeAmount = profitPerShare * lpShareBalance * performanceFeeRate
        / (PRECISION * 10000);
}

Hurdle rate

Некоторые фонды берут performance fee только если доходность превысила benchmark (hurdle rate, например 8% годовых). Реализуется как дополнительный порог поверх HWM:

function isAboveHurdle(address lp, uint256 currentNAVPerShare) public view returns (bool) {
    uint256 entryNAV = lpEntryNAV[lp];
    uint256 entryTime = lpEntryTime[lp];
    uint256 elapsed = block.timestamp - entryTime;

    // Hurdle rate = entryNAV * (1 + annualRate * elapsed / YEAR)
    uint256 hurdleNAV = entryNAV + entryNAV * hurdleRate * elapsed / (10000 * 365 days);
    return currentNAVPerShare > hurdleNAV;
}

Механизм выплат: токены vs native

Реинвестирование (default): прибыль автоматически увеличивает NAV per share. LP не получает выплату — растёт стоимость его shares. Газово эффективно, не требует on-chain транзакций.

Cash distribution: менеджер инициирует распределение, LP клеймят свои выплаты. Два паттерна:

Pull pattern (рекомендуется): контракт хранит накопленные rewards, LP клеймит сам. Менеджер не знает адреса всех LP, нет риска gas exhaustion при большом числе LP.

function claimRewards() external updateReward(msg.sender) {
    uint256 reward = pendingRewards[msg.sender];
    require(reward > 0, "Nothing to claim");
    pendingRewards[msg.sender] = 0;
    IERC20(rewardToken).safeTransfer(msg.sender, reward);
    emit RewardClaimed(msg.sender, reward);
}

Push pattern — менеджер рассылает всем LP. Проблема: если LP — смарт-контракт который reverts при получении токенов, вся транзакция упадёт. Или gas limit кончится при 1000+ LP. Используйте только для малого числа LP и после whitelist-проверки.

Merkle-based distribution для масштаба

При сотнях и тысячах LP — Merkle drop паттерн. Менеджер вычисляет выплаты off-chain, строит Merkle tree, публикует root on-chain. Каждый LP клеймит свою выплату с proof:

bytes32 public merkleRoot;

function claimMerkle(
    uint256 index,
    address account,
    uint256 amount,
    bytes32[] calldata merkleProof
) external {
    require(!isClaimed(index), "Already claimed");

    bytes32 node = keccak256(abi.encodePacked(index, account, amount));
    require(MerkleProof.verify(merkleProof, merkleRoot, node), "Invalid proof");

    _setClaimed(index);
    IERC20(rewardToken).safeTransfer(account, amount);
    emit Claimed(index, account, amount);
}

Это даёт O(log N) gas для клейма независимо от числа получателей. Uniswap использует этот паттерн для UNI-дистрибуции.

Налоговая и регуляторная отчётность

Каждое распределение должно генерировать отчёт для LP: дата, сумма, тип (return of capital / capital gain / ordinary income). В разных юрисдикциях классификация дохода разная.

Минимальный набор данных для каждого события распределения:

  • Timestamp и block number
  • LP address и идентификатор (KYC data хранится off-chain)
  • Сумма в токенах и USD-эквивалент на момент distribution
  • NAV per share до и после distribution
  • Accumulated fee, удержанный менеджером

Хэш каждого distribution report публикуется on-chain — LP могут верифицировать что off-chain данные не изменялись.

Безопасность и аудит

Reentrancy — классическая уязвимость при выплатах. Всегда обнулять pendingRewards[msg.sender] до transfer, не после. OpenZeppelin ReentrancyGuard как дополнительный слой защиты.

Precision loss при делении. Все вычисления в fixed-point арифметике с масштабом 1e18. rewardPerShare хранить с максимальной точностью, округлять вниз при выплатах (в пользу контракта, не LP — иначе контракт опустеет раньше времени).

Oracle manipulation при расчёте performance fee. Если fee считается от NAV per share, а NAV зависит от DEX-цен — flash loan атака может временно поднять NAV, триггернуть settleFee, забрать fee, восстановить NAV. Защита: TWAP вместо spot prices, cooldown между NAV updates и fee settlements.

Централизация менеджера. Менеджер с правом updateNAV и settleFee имеет большую власть. Минимизация через timelock (операции вступают в силу через 24-48 часов) и multi-sig (3-of-5 для fee settlement).

Тестирование

Инвариантные тесты через Foundry (Invariant Testing):

  • sum(lpShares) == totalSupply() — всегда
  • sum(pendingRewards) <= contractBalance(rewardToken) — контракт не может быть неплатёжеспособен
  • После любой последовательности deposit/withdraw/distribute — earned() должен быть корректен
// Foundry invariant test
contract ProfitDistributorInvariant is Test {
    ProfitDistributor distributor;

    function invariant_solvency() public {
        uint256 totalPending = 0;
        for (uint i = 0; i < lps.length; i++) {
            totalPending += distributor.earned(lps[i]);
        }
        assertLe(totalPending, IERC20(rewardToken).balanceOf(address(distributor)));
    }
}

Процесс разработки

Проектирование (3-5 дней). Выбор модели (share-based vs point-based), структуры fee, HWM per-LP vs global, механизма выплат. Формализация инвариантов системы.

Разработка смарт-контрактов (7-10 дней). Vault, distributor, fee module. Юнит-тесты + fuzz-тесты + инвариантные тесты на Foundry.

Off-chain компоненты (3-5 дней). NAV calculator, Merkle tree builder, скрипты клейма.

Аудит (1-2 недели). Внешний аудит обязателен для любой системы управляющей чужими деньгами.

Деплой и мониторинг (2-3 дня). Graduated rollout, мониторинг инвариантов в реальном времени.