Разработка NFT-стейкинга

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

Разработка NFT-стейкинга

NFT-стейкинг — это механика удержания: пользователь лочит NFT в контракте на период, получает ERC-20 токены в качестве награды. Работает как retention tool для gaming проектов, как governance power accumulator, как emission mechanism для новых токенов. Контракт выглядит просто, но имеет три неочевидных места, где что-то идёт не так.

Где ломается стейкинг-контракт

Reward calculation: накопленные ошибки округления

Самый частый баг — drift в расчёте наград из-за integer division. Стандартный паттерн:

rewardPerTokenStored += rewardRate * deltaTime / totalStaked

При totalStaked = 1000 NFT и rewardRate = 1e18 wei/sec каждую секунду rewardPerTokenStored увеличивается на 1e15. Всё корректно. Но при totalStaked = 3 NFT: 1e18 / 3 = 333333333333333333 — 1 wei потеряно на каждой секунде. За год это ~31M секунд = ~31M wei потеряно. Незначительно, но при большом rewardRate или малом totalStaked drift растёт.

Решение: reward per token с точностью 1e36 (не 1e18). Хранить rewardPerTokenStored в ray-подобных единицах с масштабированием, делить при выплате. Это стандарт в Synthetix-derived стейкинг-контрактах (StakingRewards.sol).

ERC-721 transfer после стейкинга: ownership confusion

Наивный контракт: при стейке сохраняем stakers[tokenId] = msg.sender, НЕ переводим NFT в контракт. Надеемся, что пользователь сам не переведёт. Это сломается: пользователь стейкает, потом продаёт NFT на маркетплейсе, новый владелец ничего не знает о стейкинге, а старый продолжает получать награды. Или старый пытается unstake чужой NFT.

Правильно: при стейке NFT физически переводится в контракт через transferFrom(msg.sender, address(this), tokenId). При unstake — возвращается. Контракт — custodial. Пользователь не может продать застейканный NFT без unstake.

Альтернатива — non-custodial стейкинг через одобрение + off-chain snapshot. Менее распространено, требует другой архитектуры с Merkle-based claim.

Reentrancy через ERC-721 onERC721Received

При вызове safeTransferFrom контракт вызывает onERC721Received на получателе. Если в unstake функции сначала происходит transfer NFT назад, а потом обновляется stakedTokens[msg.sender] — открывается reentrancy через ERC-721 callback. Сценарий: злоумышленник деплоит получателя с onERC721Received, который повторно вызывает unstake. Итог — двойной вывод наград.

Паттерн Checks-Effects-Interactions: сначала обновляем все storage переменные (delete stakedTokens[msg.sender], totalStaked--, обновляем rewards[msg.sender]), потом делаем внешние вызовы (transfer NFT, transfer rewards). nonReentrant из OpenZeppelin — дополнительный слой защиты.

Архитектура стейкинг-контракта

Мультиколлекция и бусты

Для gaming проектов часто нужно стейкать NFT из разных коллекций с разными весами: герой-легенда даёт 10x rewards/день, обычный герой — 1x. Реализация через mapping(address collection => uint256 multiplier) — owner регистрирует коллекции с весами.

Trait-based буст — NFT с редкими атрибутами дают больше наград. Trait данные хранятся on-chain (дорого) или доступны через Chainlink Functions / собственный oracle. Прагматичное решение: backend подписывает (tokenId, multiplier, nonce), пользователь передаёт подпись при стейке — контракт верифицирует ECDSA.

Lock periods и bonuses

Стейкинг с lock: пользователь выбирает период (30/90/180 дней) при стейке, получает multiplier за долгосрочный лок. Структура:

struct StakeInfo {
    address owner;
    uint256 stakedAt;
    uint256 lockUntil;
    uint256 rewardMultiplier; // в basis points: 10000 = 1x, 15000 = 1.5x
    uint256 rewardDebt;
}

Early unstake возможен, но с penalty — часть накопленных наград сжигается или остаётся в контракте.

ERC-4626-подобный подход для fungible staking

Если стейкать одинаковые NFT (например, edition коллекция), можно реализовать vault с ERC-4626-подобной механикой: shares пропорционально вкладу, autocompounding наград. Менее применимо для PFP с уникальными ID.

Интеграция с reward token

Стейкинг контракт должен минтить или переводить reward token. Два подхода:

Pre-funded vault — контракт держит фиксированный запас ERC-20 для наград. Прозрачно, предсказуемо, но требует upfront funding.

Minter role — контракт имеет MINTER_ROLE в reward token и минтит награды по запросу. Гибко, но инфляция не ограничена контрактом — нужна внешняя токеномика с emission cap.

Процесс работы

Разработка (3-5 дней). StakingRewards.sol адаптация + тесты в Foundry. Фuzz на расчёт наград с граничными значениями totalStaked.

Аудит. Reentrancy проверка, overflow в reward math, privilege escalation через owner functions.

Деплой. Hardhat-deploy для воспроизводимого деплоя с конфигурацией параметров.

Ориентиры по срокам

Базовый стейкинг одной коллекции — 3-4 дня. Мультиколлекция с trait-бустами и lock periods — 1-1.5 недели.

Стоимость рассчитывается индивидуально.