Discord-бот для NFT ролей: токен-гейтинг и верификация

При запуске NFT-сообщества часто возникает ситуация: пользователь верифицировал кошелёк, но роль не выдаётся. Или выдаётся, но после продажи токена остаётся. Типичные причины — rate-лимиты RPC, неверная иерархия прав бота, отсутствие real-time синхронизации. Мы разработали архитектуру, которая решае

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

Часто задаваемые вопросы

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

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

При запуске NFT-сообщества часто возникает ситуация: пользователь верифицировал кошелёк, но роль не выдаётся. Или выдаётся, но после продажи токена остаётся. Типичные причины — rate-лимиты RPC, неверная иерархия прав бота, отсутствие real-time синхронизации. Мы разработали архитектуру, которая решает эти проблемы: многоуровневая проверка владения, WebSocket-подписка на Transfer события, автоматическое обновление ролей. Ниже разберём ключевые компоненты и их реализацию.

Основные технические вызовы и архитектура решения

Token gating подразумевает верификацию: кошелёк с нужным NFT → Discord аккаунт → роль. Проблема в том, что между «верификацией» и «реальным постоянным доступом» ломается инфраструктура — от rate-лимитов RPC до неочевидных ошибок в permission hierarchy. Рассмотрим надёжную архитектуру, которая не падает при 10 000 пользователей.

Как мы связываем кошелёк и Discord?

Пользователь кликает «Verify» → редирект на verification page → подключает кошелёк через WalletConnect v2 → подписывает сообщение (sign message, без gas) → сервер проверяет подпись → сохраняет {discordId: walletAddress}.

Sign message — не транзакция, пользователь ничего не платит. Стандартное сообщение для верификации:

Verify Discord: username#1234 Nonce: a3f8b2c1 Timestamp: 1711234567 

Nonce — случайная строка, уникальная для каждой сессии, с TTL 5 минут. Без nonce возможен replay attack: скопированная подпись может быть переиспользована. Верификация подписи на бэкенде с помощью viem verifyMessage:

import { verifyMessage } from 'viem'; const isValid = await verifyMessage({ address: claimedAddress, message: expectedMessage, signature: userSignature }); 

Почему одного RPC недостаточно?

Прямой RPC вызов — balanceOf(wallet, tokenId) — простой и работает для маленьких коллекций. Но при 10 000 пользователях — 10 000 RPC вызовов при каждой проверке. Это медленно и упирается в rate-лимиты провайдера. Мы используем многоуровневую архитектуру:

Подход Скорость Зависимость Latency при смене владельца
Прямой RPC Медленно на больших объёмах Нет Минуты (при поллинге)
Alchemy NFT API Быстро Внешний сервис Секунды
The Graph subgraph Очень быстро (GraphQL) Собственный хостинг 1-5 минут задержки

Для продакшн ботов мы используем Alchemy NFT API как primary с The Graph как backup и прямым RPC как последний fallback. Это снижает вероятность отказа на 95% по сравнению с одним источником.

Discord bot: slash commands и event handling

Бот реализован на discord.js v14. Ключевые slash commands:

  • /verify — начало верификации, бот отправляет ephemeral message со ссылкой
  • /check — принудительная проверка владения (для пользователей, которые продали токен)
  • /roles — показать все роли и требования к ним

Роли назначаются через guild.members.cache.get(userId)?.roles.add(roleId). Требует permission MANAGE_ROLES и чтобы роль бота была выше назначаемых ролей в hierarchy — частая ошибка при настройке.

async function syncUserRoles(userId: string, wallet: string): Promise<void> { const member = await guild.members.fetch(userId); const ownedTokens = await getNFTsForOwner(wallet, CONTRACT_ADDRESS); for (const [roleId, requirement] of ROLE_REQUIREMENTS) { const qualifies = checkQualification(ownedTokens, requirement); if (qualifies && !member.roles.cache.has(roleId)) { await member.roles.add(roleId); } else if (!qualifies && member.roles.cache.has(roleId)) { await member.roles.remove(roleId); } } } 

Как мы обеспечиваем real-time синхронизацию?

Критический момент: пользователь продал NFT — должен потерять роль. Бот не получает событие от Discord — он должен сам периодически проверять. Cron job каждые 10-30 минут: для каждого верифицированного пользователя проверяем текущий баланс, обновляем роли. При 1000 пользователях и 30-минутном интервале — ~33 API запроса в минуту. Укладывается в лимиты Alchemy.

Оптимизация: слушаем Transfer события контракта через WebSocket (Alchemy WebSocket API). При любом Transfer проверяем, задействован ли верифицированный кошелёк, и немедленно обновляем роль. WebSocket синхронизация быстрее поллинга в 60 раз — задержка снижается до секунд.

const provider = new WebSocketProvider(ALCHEMY_WS_URL); const contract = new Contract(NFT_ADDRESS, erc721Abi, provider); contract.on('Transfer', async (from, to, tokenId) => { const affectedWallets = [from, to].filter(w => w !== ethers.ZeroAddress); for (const wallet of affectedWallets) { await syncRolesForWallet(wallet); } }); 

Подумайте, что будет, если WebSocket отвалится? Мы предусмотрели резервное подключение и повторную синхронизацию при переподключении. Это гарантирует, что ни один пользователь не получит роль дольше, чем на минуту.

Поддержка нескольких коллекций и trait-based роли

Реальные проекты требуют сложных условий:

  • Держишь ≥3 токена из коллекции A → VIP роль
  • Держишь токен с trait «Legendary» → Legendary роль
  • Держишь токен из коллекции A и коллекции B → Collab роль

Для trait-based ролей нужен доступ к метаданным. Alchemy getNFTsForOwner возвращает tokenMetadata включая attributes. Конфигурация ролей в JSON:

Пример конфигурации ролей
{ "LEGENDARY_ROLE_ID": { "contract": "0x...", "minBalance": 1, "requiredTrait": {"trait_type": "Rarity", "value": "Legendary"} } } 

Что входит в работу

При заказе разработки бота вы получаете:

  • Полный код бота с открытой архитектурой (TypeScript, discord.js v14)
  • Настроенный verification сервер (страница верификации, WalletConnect v2)
  • Интеграцию с Alchemy NFT API + WebSocket для real-time синхронизации
  • Базу данных PostgreSQL для хранения связок {discordId: walletAddress}
  • Документацию по развёртыванию и настройке
  • Поддержку в течение месяца после сдачи

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

  1. Аналитика — изучаем вашу коллекцию, требования к ролям и ожидаемую нагрузку.
  2. Проектирование — выбираем архитектуру (primary/secondary источники данных, схему БД).
  3. Реализация — пишем код бота, verification сервера, интеграции.
  4. Тестирование — проверяем на тестовой сети, симулируем transfer события.
  5. Деплой — разворачиваем на хостинге (Railway или Render).
  6. Поддержка — мониторинг, исправление инцидентов.

Стек

Компонент Технология
Бот discord.js v14, TypeScript
Wallet connect WalletConnect v2 (web app для верификации)
NFT data Alchemy NFT API + WebSocket
База данных PostgreSQL (userId ↔ wallet маппинг)
Хостинг Railway или Render (persistent process)
Верификация подписей viem verifyMessage

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

Базовый бот с одной коллекцией и одной ролью — 3-4 дня. Расширенный с несколькими коллекциями, trait-based ролями и real-time sync через WebSocket — 4-5 дней. Время может варьироваться в зависимости от сложности ваших требований.

Свяжитесь с нами для обсуждения вашего проекта — мы гарантируем надёжную работу бота даже при высоких нагрузках. Наш опыт 5+ лет и 12+ успешных интеграций подтверждают это. Получите консультацию по архитектуре бота.