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

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1306 услуг
Разработка системы предупреждений о подозрительных апрувалах
Средний
~3-5 дней
Часто задаваемые вопросы

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

Этапы блокчейн-разработки

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

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

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

Token approval — одна из наиболее опасных операций в Web3. Пользователь даёт смарт-контракту право тратить его токены без его участия в будущем. Approval scam — главный способ кражи активов через фишинг и вредоносные dApp. Revoke.cash зафиксировал более $2.8B потерь через approval-based атаки. Система предупреждений позволяет детектировать подозрительные approve запросы в реальном времени — до подтверждения транзакции.

Анатомия approval атаки

ERC-20 approve: token.approve(spender, amount) — разрешает spender переводить до amount токенов с адреса пользователя.

ERC-721/ERC-1155 setApprovalForAll: даёт право на все NFT из коллекции. Наиболее опасная форма — одна транзакция передаёт права на всю коллекцию.

Permit (EIP-2612): gasless подпись approval без on-chain транзакции. Пользователь подписывает сообщение — злоумышленник сам отправляет транзакцию permit(). Пользователь не видит approval в кошельке до момента кражи.

Архитектура системы предупреждений

Система работает на двух уровнях:

Pre-transaction screening: анализ pending транзакции до её подписания. Интегрируется в кошелёк или dApp через simulation API.

Post-approval monitoring: отслеживание уже выданных approvals, алерт при обнаружении аномального использования.

Анализ 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 используется — это не всегда плохо (легитимный dApp). Алерт должен срабатывать на аномальное использование:

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 разрешениями. Система должна понимать этот паттерн:

// Permit2 TransferFrom имеет другой ABI
const PERMIT2_TRANSFER_FROM_SELECTOR = "0x36c78516";

function isPermit2Transfer(calldata: string): boolean {
  return calldata.startsWith(PERMIT2_TRANSFER_FROM_SELECTOR);
}

// Декодируем Permit2 транзакцию для понимания реального спендера
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, перехватывающий eth_sendTransaction:

Канал Применение Задержка
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

MetaMask Snaps — наиболее перспективный путь для интеграции transaction insights. Snap получает доступ к onTransaction хуку и может показать пользователю предупреждение до подписания.