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

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

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

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

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

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1311
  • 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

Реінвестування (за замовчуванням): прибуток автоматично збільшує 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, моніторинг інваріантів в реальному часі.