Token Approval Management System Development (Revoke)

Token Approval Management System Development (Revoke) We've encountered a situation: a user has hundreds of unused token approvals, one of the protocols gets hacked — and the wallet is drained. Without an approval management system, any compromised protocol can drain your funds. Our team develops

Blockchain Development Services

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1308
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1269
  • image_logo-advance_0.webp
    B2B Advance company logo design
    717
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    1008
  • image_logo-aider_0.webp
    AIDER company logo development
    951
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1062

Token Approval Management System Development (Revoke)

We've encountered a situation: a user has hundreds of unused token approvals, one of the protocols gets hacked — and the wallet is drained. Without an approval management system, any compromised protocol can drain your funds. Our team develops custom solutions for viewing and revoking token approvals — from a simple single-chain interface to a multi-chain system with risk-scoring and batch revocation. Such development takes from 2 to 5 days depending on complexity, and we do it turnkey: from data analysis to deployment. Contact us for a project evaluation.

Technical Basics: How Approvals Work

ERC-20 Allowance

The ERC-20 standard defines allowance(owner, spender) — how many tokens the spender can spend on behalf of the owner. It is set via approve(spender, amount). A value of type(uint256).max (2^256-1) means "unlimited" — most protocols require this for convenience.

Problem: allowance has no expiration. There is no automatic revocation mechanism. If a protocol is compromised a year after your approve — the allowance is still active.

ERC-721 and ERC-1155 Approvals

For NFTs, there are two types of approvals:

  • approve(operator, tokenId) — permission for a specific token
  • setApprovalForAll(operator, true) — full access to the entire collection

setApprovalForAll is used by OpenSea, blur.io, and other marketplaces. This is the most dangerous type — one hacked marketplace with an active setApprovalForAll equals a lost collection.

EIP-2612: Permit

permit(owner, spender, value, deadline, v, r, s) — signature instead of a transaction. It does not create a permanent allowance, works once with a specific deadline. Well-designed dApps use permit instead of approve.

But permit has a nuance: if DAI, USDC, or another token supports permit — the allowance through permit can still be viewed via allowance(). They are indistinguishable from regular approve.

Reading Approval Data

Via Approval Event

A direct call to allowance(owner, spender) requires knowing the spender address. To get all active approvals for a wallet — you need to read events:

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); } 

Problem with Historical Data

getLogs with fromBlock: 0n is slow and expensive for public RPCs. Solutions:

  • The Graph: index Approval events via a subgraph, instant GraphQL queries
  • Etherscan/Alchemy API: ready endpoints for token approvals (alchemy_getTokenAllowances)
  • Incremental indexing: track the last indexed block, request only new events on each update

For production systems, The Graph subgraph is the optimal solution. One query returns all active approvals with metadata.

Why Batch Revoke is a Complex Task?

Revoking 10 approvals via ERC-20 requires 10 separate transactions, each with the user's signature. This is unacceptable for UX. Multicall doesn't help because approve is an operation on behalf of the user, requiring a signature. Solution — a transaction queue with automatic continuation after confirmation. The user signs each one but sees progress "Revoking 3 of 8…". Alternatively, using Permit2 batch revoke, if the user has migrated their approvals to Permit2 — this reduces gas costs by up to 70%.

How Token Revocation is Done?

ERC-20 Revoke

Revoke = approve(spender, 0). One transaction per token+spender pair.

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) — revoke full access to the collection. More critical, so highlighted in red in the UI. approve(operator, tokenId) followed by revoke is less critical — access to a specific token.

UI Design of the System

The main component is a table with sorting and filtering:

Token Spender Allowance Risk Action
USDC Uniswap V3 Unlimited Medium Revoke
WETH Old Protocol (deprecated) Unlimited High Revoke
DAI Aave V3 1,000 DAI Low Revoke

Risk scoring is an important UX feature. Spender addresses are identified via:

  • Etherscan Labels API
  • DefiLlama protocol database
  • Custom whitelist of known protocols

Verified protocol = medium risk (approval exists, but protocol is reliable). Unknown contract = high risk. Deprecated/dead contract = critical risk.

Filters: by network, by type (ERC-20 / NFT), by risk level, only unlimited approvals.

Comparison of Data Retrieval Approaches

Approach Speed Complexity Gas for Queries
RPC Events Slow Low High (many calls)
The Graph 10x faster Medium (subgraph needed) Zero (GraphQL)
Alchemy API Fast Low (ready endpoint) Subscription based

What's Included in Our Work

  • UI interface development (React + wagmi + TanStack Table) with approval table
  • Integration with chosen data source (RPC, The Graph, Alchemy)
  • Implementation of batch revocation via transaction queue
  • Multi-chain support (Ethereum, Arbitrum, Polygon, Base, BNB Chain)
  • Risk-scoring based on whitelist and external APIs
  • API documentation and user instructions
  • Deployment on your domain or white-label

Company in Numbers

Over 5 years of experience in blockchain development, 30+ implemented projects, including DeFi protocols and NFT marketplaces. Our team consists of senior engineers with deep knowledge of Solidity, Rust (Solana), and TypeScript.

Estimated Timeframes

Basic system for one network (ERC-20, RPC Events) — from 2 days. Multi-chain with NFT and The Graph — from 5 days. Exact cost is calculated individually — contact us for a consultation.