Розробка системи управління схваленнями токенів (revoke)
Ми стикалися з ситуацією: у користувача сотні невикористовуваних апрувів на токени, один із протоколів зламали — і гаманець спорожнів. Без системи управління апрувалами ви ризикуєте тим, що будь-який скомпрометований протокол може дренувати ваші кошти. Наша команда розробляє кастомні рішення для перегляду та відкликання схвалень токенів — від простого інтерфейсу для однієї мережі до multi-chain системи з risk-scoring та 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 днів. Точну вартість розраховуємо індивідуально — зв'яжіться з нами для консультації.







