Разработка системы управления апрувалами токенов (revoke)

Разработка системы управления одобрениями токенов (revoke) Мы сталкивались с ситуацией: у пользователя сотни неиспользуемых апрувов на токены, один из протоколов взломали — и кошелёк опустел. Без системы управления апрувалами вы рискуете тем, что любой скомпрометированный протокол может дренирова

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

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

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

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

Разработка системы управления одобрениями токенов (revoke)

Мы сталкивались с ситуацией: у пользователя сотни неиспользуемых апрувов на токены, один из протоколов взломали — и кошелёк опустел. Без системы управления апрувалами вы рискуете тем, что любой скомпрометированный протокол может дренировать ваши средства. Наша команда разрабатывает кастомные решения для просмотра и отзыва одобрений токенов — от простого интерфейса для одной сети до multi-chain системы с риск-скорингом и batch-отзывом. Такая разработка занимает от 2 до 5 дней в зависимости от сложности, и мы выполняем её под ключ: от анализа данных до деплоя. Свяжитесь с нами для оценки вашего проекта.

Технические основы: как работают approvals

ERC-20 allowance

Стандарт ERC-20 определяет allowance(owner, spender) — сколько токенов spender может тратить от имени owner. Устанавливается через approve(spender, amount). Значение type(uint256).max (2^256-1) означает «бесконечно» — большинство протоколов требуют именно это для удобства.

Проблема: allowance не имеет срока истечения. Нет механизма автоматической отмены. Если протокол скомпрометирован через год после вашего approve — allowance всё ещё активна.

ERC-721 и ERC-1155 approvals

Для NFT два типа approvals:

  • approve(operator, tokenId) — разрешение на конкретный токен
  • setApprovalForAll(operator, true) — полный доступ ко всей коллекции

setApprovalForAll используется OpenSea, blur.io и другими маркетплейсами. Это наиболее опасный тип — один взломанный маркетплейс с активным setApprovalForAll = вся коллекция потеряна.

EIP-2612: Permit

permit(owner, spender, value, deadline, v, r, s) — подпись вместо транзакции. Не создаёт постоянного allowance, работает разово с конкретным deadline. Правильно спроектированные dApps используют permit вместо approve.

Но permit имеет нюанс: если DAI, USDC или другой токен поддерживает permit — allowance через permit тоже можно посмотреть через allowance(). Они неотличимы от обычных approve.

Чтение данных об approvals

Через событие Approval

Прямой запрос allowance(owner, spender) требует знать адрес spender. Чтобы получить все активные approvals кошелька — нужно читать события:

import { createPublicClient, http, parseAbi } from 'viem'; const ERC20_ABI = parseAbi([ 'event Approval(address indexed owner, address indexed spender, uint256 value)', 'function allowance(address owner, address spender) view returns (uint256)', 'function symbol() view returns (string)', 'function decimals() view returns (uint8)', ]); async function getTokenApprovals(ownerAddress: `0x${string}`) { const client = createPublicClient({ chain: mainnet, transport: http(RPC_URL) }); const approvalLogs = await client.getLogs({ event: ERC20_ABI[0], args: { owner: ownerAddress }, fromBlock: 0n, toBlock: 'latest' }); const latestApprovals = new Map<string, typeof approvalLogs[0]>(); for (const log of approvalLogs) { const key = `${log.address}-${log.args.spender}`; latestApprovals.set(key, log); } const results = await Promise.all( Array.from(latestApprovals.values()).map(async (log) => { const [allowance, symbol, decimals] = await Promise.all([ client.readContract({ address: log.address, abi: ERC20_ABI, functionName: 'allowance', args: [ownerAddress, log.args.spender!] }), client.readContract({ address: log.address, abi: ERC20_ABI, functionName: 'symbol' }), client.readContract({ address: log.address, abi: ERC20_ABI, functionName: 'decimals' }), ]); return { tokenAddress: log.address, spenderAddress: log.args.spender!, allowance, symbol, decimals, isUnlimited: allowance === BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'), }; }) ); return results.filter(r => r.allowance > 0n); } 

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

getLogs с fromBlock: 0n — медленный и дорогой запрос для публичных RPC. Решения:

  • The Graph: индексируем Approval события через субграф, GraphQL запросы мгновенные
  • Etherscan/Alchemy API: готовые endpoint для token approvals (alchemy_getTokenAllowances)
  • Incremental indexing: отслеживаем последний проиндексированный блок, при каждом обновлении запрашиваем только новые события

Для production системы The Graph субграф — оптимальное решение. Один запрос возвращает все активные approvals с метаданными.

Почему batch revoke — сложная задача?

Отзыв 10 approvals через ERC-20 требует 10 отдельных транзакций, каждая с подписью пользователя. Это неприемлемо для UX. Multicall не помогает, потому что approve — это операция от имени пользователя, требующая подписи. Решение — очередь транзакций с автоматическим продолжением после подтверждения. Пользователь подписывает каждую, но видит прогресс «Отзываю 3 из 8…». Также можно использовать Permit2 batch revoke, если пользователь перевёл свои апрувы на Permit2 — это снижает газовые затраты до 70%.

Как выполняется отзыв токенов?

ERC-20 revoke

Revoke = approve(spender, 0). Одна транзакция на каждую пару token+spender.

async function revokeERC20Approval( tokenAddress: `0x${string}`, spenderAddress: `0x${string}` ) { const { writeContract } = useWriteContract(); writeContract({ address: tokenAddress, abi: erc20Abi, functionName: 'approve', args: [spenderAddress, 0n] }); } 

ERC-721 / ERC-1155 revoke

setApprovalForAll(operator, false) — отзыв полного доступа к коллекции. Более критично, поэтому в UI выделяем красным. approve(operator, tokenId) с последующим revoke менее критичен — доступ к конкретному токену.

UI дизайн системы

Основной компонент — таблица с сортировкой и фильтрацией:

Токен Spender Allowance Риск Действие
USDC Uniswap V3 Unlimited Средний Revoke
WETH Old Protocol (deprecated) Unlimited Высокий Revoke
DAI Aave V3 1,000 DAI Низкий Revoke

Risk scoring — важная часть UX. Spender адреса идентифицируются через:

  • Etherscan Labels API
  • DefiLlama протокол базы
  • Собственный whitelist известных протоколов

Verified протокол = средний риск (approve существует, но протокол надёжный). Неизвестный контракт = высокий риск. Deprecated/мёртвый контракт = критический риск.

Filters: по сети, по типу (ERC-20 / NFT), по уровню риска, только unlimited approvals.

Сравнение подходов к получению данных

Подход Скорость Сложность Газ для запросов
RPC Events Медленно Низкая Высокий (много вызовов)
The Graph Быстро в 10× Средняя (нужен субграф) Нулевой (GraphQL)
Alchemy API Быстро Низкая (готовый endpoint) По подписке

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

  • Разработка UI-интерфейса (React + wagmi + TanStack Table) с таблицей approvals
  • Интеграция с выбранным источником данных (RPC, The Graph, Alchemy)
  • Реализация batch-отзыва через очередь транзакций
  • Поддержка мультисетей (Ethereum, Arbitrum, Polygon, Base, BNB Chain)
  • Риск-скоринг на основе whitelist и внешних API
  • Документация по API и инструкция для пользователей
  • Развёртывание на вашем домене или white-label

Компания в цифрах

Более 5 лет опыта в блокчейн-разработке, 30+ реализованных проектов, включая DeFi-протоколы и NFT-маркетплейсы. Наша команда состоит из senior-инженеров с глубоким знанием Solidity, Rust (Solana) и TypeScript.

Ориентировочные сроки

Базовая система для одной сети (ERC-20, RPC Events) — от 2 дней. Multi-chain с NFT и The Graph — от 5 дней. Точную стоимость рассчитываем индивидуально — свяжитесь с нами для консультации.