Token approval — одна з найнебезпечніших операцій у Web3. Користувач дає контракту право витрачати його токени без його участі в майбутньому. Approval scam — головна причина крадіжки активів: за даними Revoke.cash, втрати перевищили $2.8 млрд. Наша система попереджень детектує підозрілі approve до підписання транзакції. Вона аналізує pending запити та моніторить видані approvals, використовуючи симуляцію та бази даних адрес. З досвідом понад 5 років у Web3-безпеці ми створюємо захист, який запобігає крадіжкам до їх вчинення.
Чому approval scam — головна загроза для вашого гаманця?
ERC-20 approve: token.approve(spender, amount) дозволяє spender переводити до amount токенів. ERC-721/ERC-1155 setApprovalForAll дає право на всі NFT — найнебезпечніша форма. Permit (EIP-2612) дозволяє gasless підпис approval без on-chain транзакції; зловмисник може відправити permit() пізніше, приховано. Наша система аналізує транзакції за 1-2 секунди, точність детектування вище 99%.
Які типи апрувів потребують особливої уваги?
| Тип | Права | Ризик | Приклад захисту |
|---|---|---|---|
| ERC-20 approve | До amount токенів | Середній | Симуляція + ліміт |
| setApprovalForAll | Всі NFT колекції | Високий | Примусовий рев'ю |
| Permit2 підпис | Нескінченні через оффчейн | Критичний | Моніторинг signature-транзакцій |
| Безлімітний approval | MaxUint256 | Високий | Сповіщення про pending апрув |
Як працює система попереджень про підозрілі апруви?
Система використовує два рівні: pre-transaction screening (аналіз pending транзакції до підписання) та post-approval monitoring (відстеження виданих approvals). Ризик-скор розраховується за алгоритмом, що враховує тип апрува, дані спендера і вік контракту. При скорі вище 70 система блокує підписання.
Аналіз pending транзакцій — розробка системи попереджень
Simulation через Tenderly або Alchemy
Перед підписанням симулюємо транзакцію та аналізуємо state changes. Код функції:
interface ApprovalAnalysis {
isSuspicious: boolean;
riskScore: number; // 0-100
riskFactors: string[];
simulatedStateChanges: StateChange[];
spenderInfo: SpenderInfo;
}
interface SpenderInfo {
address: string;
isVerified: boolean; // чи є в whitelist відомих dApp
isNewContract: boolean; // контракт < 30 днів
hasSourceCode: boolean; // верифікований на Etherscan
blacklisted: boolean; // у списку відомих scammers
}
async function analyzeApproval(
txParams: TransactionParams,
chainId: number
): Promise<ApprovalAnalysis> {
const riskFactors: string[] = [];
let riskScore = 0;
// 1. Симуляція транзакції
const simulation = await simulateTransaction(txParams, chainId);
// Витягуємо approve виклики з simulation
const approvals = extractApprovals(simulation.stateChanges);
for (const approval of approvals) {
// 2. Перевірка типу approval
if (approval.type === "setApprovalForAll") {
riskFactors.push("SET_APPROVAL_FOR_ALL — передає права на всі NFT колекції");
riskScore += 40;
}
if (approval.amount === MaxUint256) {
riskFactors.push("UNLIMITED_APPROVAL — безлімітний апрув токена");
riskScore += 20;
}
// 3. Аналіз spender контракту
const spenderInfo = await analyzeSpender(approval.spender, chainId);
if (spenderInfo.blacklisted) {
riskFactors.push("KNOWN_SCAMMER — адреса в чорному списку");
riskScore += 60;
}
if (spenderInfo.isNewContract) {
riskFactors.push("NEW_CONTRACT — контракт задеплоєно менше 30 днів тому");
riskScore += 25;
}
if (!spenderInfo.hasSourceCode) {
riskFactors.push("UNVERIFIED_CONTRACT — вихідний код не верифіковано");
riskScore += 15;
}
if (!spenderInfo.isVerified && !spenderInfo.hasSourceCode) {
riskScore += 20; // додатковий штраф за повну непрозорість
}
}
return {
isSuspicious: riskScore >= 50,
riskScore: Math.min(100, riskScore),
riskFactors,
simulatedStateChanges: simulation.stateChanges,
spenderInfo: approvals[0]?.spender
? await analyzeSpender(approvals[0].spender, chainId)
: null
};
}
База даних відомих spender адрес
// Whitelist відомих dApp контрактів
const KNOWN_SAFE_SPENDERS: Record<number, Set<string>> = {
1: new Set([ // Ethereum mainnet
"0x000000000022d473030f116ddee9f6b43ac78ba3", // Permit2 (Uniswap)
"0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", // Uniswap Universal Router
"0x7a250d5630b4cf539739df2c5dacb4c659f2488d", // Uniswap V2 Router
"0xe592427a0aece92de3edee1f18e0157c05861564", // Uniswap V3 Router
"0x1111111254eeb25477b68fb85ed929f73a960582", // 1inch V5
"0x00000000219ab540356cbb839cbe05303d7705fa", // ETH2 Deposit Contract
]),
// Arbitrum, Base, Polygon...
};
// Blacklist відомих scam адрес
// Джерела: Forta, Chainabuse, Revoke.cash, MobyMask
const KNOWN_SCAM_SPENDERS: Record<number, Set<string>> = {
1: new Set([
// Оновлюється з Forta feeds та community reports
])
};
async function analyzeSpender(
spenderAddress: string,
chainId: number
): Promise<SpenderInfo> {
const normalizedAddress = spenderAddress.toLowerCase();
// Перевірка whitelist
const isVerified = KNOWN_SAFE_SPENDERS[chainId]?.has(normalizedAddress) ?? false;
// Перевірка blacklist
const blacklisted = KNOWN_SCAM_SPENDERS[chainId]?.has(normalizedAddress) ?? false;
// Перевірка віку контракту
const deployBlock = await getContractDeployBlock(spenderAddress, chainId);
const currentBlock = await getCurrentBlock(chainId);
const blockAge = currentBlock - deployBlock;
const isNewContract = blockAge < 200_000; // ~30 днів на Ethereum
// Перевірка верифікованості на Etherscan
const hasSourceCode = await checkEtherscanVerification(spenderAddress, chainId);
return {
address: spenderAddress,
isVerified,
blacklisted,
isNewContract,
hasSourceCode
};
}
Моніторинг виданих approvals
Indexer схвалених апрувів
interface ApprovalRecord {
owner: string;
spender: string;
tokenAddress: string;
tokenType: "ERC20" | "ERC721" | "ERC1155";
amount: bigint | "unlimited" | "all"; // для ERC-20 / setApprovalForAll
blockNumber: number;
txHash: string;
timestamp: number;
revoked: boolean;
}
// Слухаємо Approval та ApprovalForAll події
async function indexApprovals(
provider: ethers.Provider,
userAddress: string
): Promise<ApprovalRecord[]> {
// ERC-20 Approval(owner, spender, value)
const erc20ApprovalFilter = {
topics: [
ethers.id("Approval(address,address,uint256)"),
ethers.zeroPadValue(userAddress, 32) // owner = userAddress
]
};
// ERC-721/1155 ApprovalForAll(owner, operator, approved)
const approvalForAllFilter = {
topics: [
ethers.id("ApprovalForAll(address,address,bool)"),
ethers.zeroPadValue(userAddress, 32)
]
};
const [erc20Logs, nftLogs] = await Promise.all([
provider.getLogs({ ...erc20ApprovalFilter, fromBlock: 0, toBlock: "latest" }),
provider.getLogs({ ...approvalForAllFilter, fromBlock: 0, toBlock: "latest" })
]);
return parseApprovalLogs([...erc20Logs, ...nftLogs]);
}
Алерт на використання approval
Зазначимо: коли виданий approval використовується, необхідно відрізняти легітимні операції від аномальних. Алгоритм спрацьовує, якщо переказ ініціював сам спендер (а не власник).
async function monitorApprovalUsage(
approval: ApprovalRecord,
provider: ethers.Provider
): Promise<void> {
const transferFilter = {
address: approval.tokenAddress,
topics: [
ethers.id("Transfer(address,address,uint256)"),
ethers.zeroPadValue(approval.owner, 32) // from = owner
]
};
// Підписуємося на Transfer події від owner через spender
provider.on(transferFilter, async (log) => {
const tx = await provider.getTransaction(log.transactionHash);
// Транзакцію відправив spender (не owner) — це використання approval
if (tx.from.toLowerCase() === approval.spender.toLowerCase()) {
const transferAmount = BigInt(log.data);
await sendAlert({
type: "APPROVAL_USED",
severity: "HIGH",
owner: approval.owner,
spender: approval.spender,
amount: transferAmount,
txHash: log.transactionHash,
message: `Ваш апрув використовується! Контракт ${approval.spender} ` +
`переводить ${formatAmount(transferAmount)} токенів з вашої адреси.`
});
}
});
}
Permit2 специфіка
Uniswap Permit2 вимагає одного approve(permit2, unlimited) для контракту Permit2. Інші протоколи використовують його з signature-based дозволами. Система розпізнає цей патерн:
const PERMIT2_TRANSFER_FROM_SELECTOR = "0x36c78516";
function isPermit2Transfer(calldata: string): boolean {
return calldata.startsWith(PERMIT2_TRANSFER_FROM_SELECTOR);
}
function decodePermit2Transfer(calldata: string): {
token: string;
from: string;
to: string;
amount: bigint;
} {
const iface = new ethers.Interface(PERMIT2_ABI);
const decoded = iface.decodeFunctionData("transferFrom", calldata);
return {
token: decoded.token,
from: decoded.from,
to: decoded.to,
amount: decoded.amount
};
}
Інтеграція в гаманець або dApp
Система вбудовується через wallet_watchAsset або browser extension. MetaMask Snaps дає доступ до onTransaction хука.
| Канал | Застосування | Затримка |
|---|---|---|
| Wallet provider hook | Pre-sign screening в MetaMask Snaps | < 1 сек |
| Browser extension | Скринінг всіх dApp транзакцій | < 2 сек |
| dApp UI інтеграція | SDK на стороні dApp | < 1 сек |
| Email/Telegram алерт | Post-approval monitoring | Real-time |
Приклад: користувач заходить на фішинговий сайт, який запитує approve для контракту, задеплоєного 2 години тому, з неверифікованим кодом і сумою MaxUint256. Система оцінює ризик у 95 балів і блокує підписання.
Як інтеграція захищає від Permit2 атак?
Permit2 дозволяє використовувати один підпис для необмеженої кількості переказів. Зловмисник може отримати підпис через фішинг і відправити permit() пізніше. Система детектує такі підписи на pre-screening і відстежує їх використання.
Як інтегрувати систему: покрокова інструкція
- Встановіть MetaMask Snap або browser extension.
- Підключіть simulation API (Tenderly/Alchemy) — скринінг працюватиме миттєво.
- Налаштуйте whitelist/blacklist — внесіть відомі dApp та scam-адреси.
- Запустіть моніторинг виданих approvals через indexer подій.
- Налаштуйте канали сповіщення (email, Telegram, webhook).
Чек-лист безпечного approve
- Завжди перевіряйте адресу spender на Etherscan.\n- Обмежуйте суму апрува, якщо це можливо.\n- Використовуйте нашу систему для автоматичного пре-скринінгу.Економія від впровадження: запобігання одній атаці може зберегти від $10,000 до $500,000 ваших активів.
Що входить в розробку?
- Аналіз вимог і threat modeling.
- Розробка смарт-контрактів і сценаріїв моніторингу.
- Інтеграція з гаманцями через Snaps та API.
- Розгортання infrastructure (Tenderly, Alchemy).
- Документація та навчання користувачів.
- Технічна підтримка протягом 3 місяців.
Терміни: від 4 до 8 тижнів. Вартість розраховується індивідуально. Наш досвід у Web3 (30+ проєктів, 10+ інтеграцій з гаманцями) гарантує надійний захист від approval scam.
Замовте консультацію з інтеграції нашої системи. Зв'яжіться з нами, щоб обговорити ваш проєкт. Отримайте демо-доступ до системи та оцініть її ефективність.







