Розробка контрактів ліквідіті-майнінгу

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

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

Ліквідиті-майнінг — це механізм, при якому користувачі надають ліквідність і отримують винагороди в токенах протоколу. SushiSwap у 2020 році провів «vampire attack» на Uniswap саме через це: розгорнув аналогічний AMM з агресивними SUSHI-винагородами і протягом 24 годин привабив $1B TVL. Механіка працює.

Але за зовнішною простотою «стейкуєш LP-токен → отримуєш винагороду» приховується нетривіальна математика та кілька класів вразливостей, які регулярно зустрічаються в production.

Математика розподілу винагород

Алгоритм Masterchef (Synthetix StakingRewards)

Базовий алгоритм, використовуваний більшістю протоколів, заснований на накопленій вартості винагороди на одиницю стейка (rewardPerTokenStored).

// Накопленої винагороди на токен
uint256 public rewardPerTokenStored;

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

// Винагорода конкретного користувача
function earned(address account) public view returns (uint256) {
    return (balanceOf[account] * 
        (rewardPerToken() - userRewardPerTokenPaid[account]) 
        / 1e18) 
        + rewards[account];
}

Ключова властивість: оновлення rewardPerTokenStored відбувається при кожному stake/withdraw/getReward, а не при кожному блоці. Це O(1) операція незалежно від кількості учасників.

При кожній взаємодії користувача зберігаємо userRewardPerTokenPaid[user] = rewardPerToken(). Різниця між поточним rewardPerToken() та збереженим значенням — накопленої винагороди з моменту останньої взаємодії.

Ця математика коректна при безперервному часі. На практиці дискретні блоки створюють похибку округлення, яку необхідно враховувати.

Boosted Rewards (ve-токени)

Модель Convex/Curve: винагорода залежить не лише від суми стейка, а й від кількості заблокованого governance-токена (veToken). Формула:

effective_balance = min(
    0.4 * balance + 0.6 * (totalSupply * veBal / veTotalSupply),
    balance
)

Це створює стимул для утримання governance-токена, зменшує тиск на продаж reward-токена. Математично складніше, потребує ретельного тестування edge cases при нульовому veBalance.

Multi-reward розподіл

Коли потрібно розподіляти кілька токенів одночасно (наприклад, протокольний токен + USDC fee sharing), архітектура ускладнюється. Кожен reward-токен потребує власного rewardPerTokenStored. OpenZeppelin не надає готову реалізацію для multi-reward; використовуємо Synthetix StakingMultiRewards як референс.

Вразливості, які ми бачили в аудитах

Inflation attack при першому депозиті

При порожньому стейкинг-контракті (totalSupply = 0) перший користувач може маніпулювати rewardPerToken(). Атака: розгорнув контракт, атакуючий робить мікро-депозит (1 wei), контракт накопичує всі винагороди для 1 wei, атакуючий отримує непропорційно велику частку.

Рішення: віртуальний початковий баланс (VIRTUAL_TOTAL_SUPPLY = 1e18), який ніколи не виводиться, або перевірка мінімального депозиту.

Flash loan атака на винагороди

Атакуючий бере flash loan на LP-токени, стейкує величезну суму, отримує винагороду за один блок, виводить. При високому rewardRate це може бути прибутковим.

Захист: мінімальний період стейкингу (lockup period, 1-7 днів). Альтернатива — vesting винагород: не видавати одразу, а лінійно протягом N днів. Навіть 24-годинний vesting робить flash loan атаку нерентабельною.

Griefing через updateReward

Функції stake, withdraw, getReward повинні викликати _updateReward(msg.sender). Якщо це modifier — кожен вклик оновлює стан для msg.sender. Але якщо _updateReward дорогостояний (наприклад, ітерує по масиву), зовнішній користувач може викликати функції в циклі і зробити контракт недоступним через gas griefing.

Рішення: _updateReward повинен бути O(1). Не ітеруйте по масивам учасників.

Втрата точності при малих сумах

При діленні rewardRate * dt / totalSupply, якщо totalSupply дуже великий, а dt малий, результат може бути 0 через цілочисленне ділення. Винагороди «випаровуються».

Рішення: масштабування через 1e18 (або 1e27 для Aave-like precision), накопичення remainder'ів.

Економіка та параметри

rewardRate — кількість reward-токенів за секунду. Обчислюється як rewardAmount / duration. При notifyRewardAmount() перераховується з урахуванням залишку поточного періоду.

function notifyRewardAmount(uint256 reward) external onlyOwner {
    if (block.timestamp >= periodFinish) {
        rewardRate = reward / rewardsDuration;
    } else {
        uint256 remaining = periodFinish - block.timestamp;
        uint256 leftover = remaining * rewardRate;
        rewardRate = (reward + leftover) / rewardsDuration;
    }
    // ...
}

Emergency withdrawal — користувач повинен мати можливість вивести стейк навіть якщо контракт на паузі. Винагороди при цьому можна не видавати, але основний стейк — обов'язково. Цього вимагає не тільки здоровий глузд, але й деякі юрисдикції.

Процес роботи

Проектування (0.5-1 день). Визначаємо: один reward-токен або кілька, потрібен ли boosting, мінімальний lockup, механізм поповнення винагород (manual vs автоматизований).

Розроблення (2-3 дні). Основний контракт, события для indexing (The Graph субграф будується за events), тести з fuzz-тестуванням арифметики.

Тести (1-2 дні). Покриваємо: нормальний стейкинг/вивід, flash loan сценарій з мінімальним lockup, precision tests з граничними значеннями, multi-user сценарії з різними пропорціями.

Деплой. Окремий deployer-скрипт з конфігурацією початкового rewardRate, transferOwnership на multisig.

Для більшості проектів повний цикл — 3-5 робочих днів. Складні схеми з veToken boosting та multiple reward tokens — 8-12 днів.