Перейти к основному содержанию
Это руководство демонстрирует, как построить гибкую систему мониторинга, которая может отслеживать любое on-chain событие, обрабатывать данные транзакций и доставлять уведомления в реальном времени через webhook. Объединяя мощные критерии триггеров системы Alert Tenderly с Web3 Actions, вы создадите модульную инфраструктуру мониторинга, способную адаптироваться под любые потребности мониторинга блокчейна — от простого отслеживания событий до сложных мультиконтрактных взаимодействий.
В этом руководстве предполагается, что у вас есть аккаунт Tenderly и базовое знакомство с Web3 Actions и событиями блокчейна.

Модульный мониторинг с Web3 Actions

Предварительные требования

Перед началом убедитесь, что у вас есть:
  • Node.js (в настоящее время в качестве последней версии поддерживается v20)
  • Установленный Tenderly CLI
  • Конечная точка webhook для получения данных
  • Базовое понимание TypeScript

Настройка системы

1
Создание Alert
2
  • Тип оповещения (Alert Type): мы настроим его как Successful Transaction. Подробные шаги можно найти здесь
  • 3
    Setup Successful Transaction Alert
    4
  • Цель оповещения (Alert Target): в качестве цели мы используем Tag, который позволяет отслеживать все контракты и кошельки, помеченные конкретным тегом. Здесь можно посмотреть, как добавить Tag для контракта или кошелька.
  • 5
    Setup Tag as an Alert Target
    6
  • Параметр оповещения (Alert Parameter): выберите Tag из выпадающего списка, содержащий контракты, которые мы хотим отслеживать
  • 7
    Choose Tag you want to use
    8
  • Назначение оповещения (Alert Destination): пока вы можете настроить что угодно, на следующем этапе мы настроим Web3 Action как назначение для этого оповещения
  • 9
    Установка зависимостей
    10
  • Клонируйте репозиторий. Ссылку на шаблон можно найти здесь
  • После клонирования репозитория выполните следующую команду в CLI:
  • 11
    cd actions && npm install
    
    12
    Настройка переменных окружения
    13
    Установите эти Secrets в вашем Tenderly Web3 Action:
    14
    WEBHOOK_URL = <Your webhook URL>
    BEARER = <Your Tenderly API bearer token>
    ACCOUNT_SLUG = <Your Tenderly account slug>
    PROJECT_SLUG = <Your project slug>
    EVENT_NAME = <Name of the event to track>
    
    15
    Настройка tenderly.yaml
    16
    Ваш tenderly.yaml должен выглядеть так:
    17
    account_id: "<YOUR_ACCOUNT_ID>"
    actions:
      <YOUR_ACCOUNT_ID>/<PROJECT_SLUG>:
        runtime: v2
        sources: actions
        specs:
          example:
            description: "Event tracking system"
            function: example:modularFn
            trigger:
              type: alert
              alert: { <YOUR_ALERT_ID> }
            execution_type: parallel
    project_slug: "<PROJECT_SLUG>"
    
    18
    Обязательно замените плейсхолдеры следующими значениями:
    19
  • YOUR_ACCOUNT_ID: ваш ID аккаунта, который можно получить, следуя этому руководству.
  • PROJECT_SLUG: ваш slug проекта, который можно получить, следуя этому руководству.
  • YOUR_ALERT_ID: ID оповещения, которое вы создали на предыдущем шаге. ID можно найти в панели.
  • 20
    Finding the Alert ID
    21
    Разверните ваш Web3 Action
    22
    Запустите следующую команду для развёртывания вашего Web3 Action:
    23
    tenderly actions deploy
    

    Основные компоненты

    Обработка транзакции

    Система начинает с обработки информации о транзакции:
    showLineNumbers
    export const modularFn = async (context: Context, transactionEvent: TransactionEvent) => {
      const txHash = transactionEvent.hash;
      const txNetwork = transactionEvent.network;
    
      const blockHash = transactionEvent.blockHash;
      const blockNumber = transactionEvent.blockNumber;
    
      const WEBHOOK_URL = await context.secrets.get("WEBHOOK_URL");
      const BEARER = await context.secrets.get("BEARER");
      const ACCOUNT_SLUG = await context.secrets.get("ACCOUNT_SLUG");
      const PROJECT_SLUG = await context.secrets.get("PROJECT_SLUG");
      const EVENT_NAME = await context.secrets.get("EVENT_NAME");
    
      if (!EVENT_NAME) {
        throw new Error("EVENT_NAME not found in environment variables");
      }
    
      const url = `https://api.tenderly.co/api/v1/public-contract/${txNetwork}/trace/${txHash}`;
    
      const traceResponse = await axios.get(url, {
        headers: {
          authorization: BEARER,
        },
      });
    
      const traceData = traceResponse.data;
    
    Этот компонент:
    • Захватывает хеш транзакции и сеть
    • Получает трейсы транзакции через Tenderly API
    • Обрабатывает информацию о блоке

    Получение информации о контракте

    Информация о контракте (имена и ABI) получается с помощью Contracts API.
    showLineNumbers
    const fetchContractName = async (addr: string) => {
      const contractEndpoint = `https://api.tenderly.co/api/v1/account/${ACCOUNT_SLUG}/project/${PROJECT_SLUG}/contract/${txNetwork}/${addr}`;
    
      try {
        const contractResponse = await axios.get(contractEndpoint, {headers: {'Authorization': BEARER}});
        return contractResponse.data.contract.contract_name;
      } catch (error) {
        console.error(`Error fetching contract name from endpoint for ${addr}:`, error);
      }
    };
    
    const fetchContractAbi = async (addr: string) => {
      const contractEndpoint = `https://api.tenderly.co/api/v1/account/${ACCOUNT_SLUG}/project/${PROJECT_SLUG}/contract/${txNetwork}/${addr}`;
    
      try {
        const contractResponse = await axios.get(contractEndpoint, {headers: {'Authorization': BEARER}});
        return contractResponse.data.contract.data.abi;
      } catch (error) {
        console.error(`Error fetching contract ABI from endpoint for ${addr}:`, error);
      }
    };
    

    Обработка событий

    Обработка событий выполняется с помощью специализированных функций:
    showLineNumbers
    const extractEventAddresses = (logs: any[]): string[] =>
      Array.from(new Set(
        logs
          .filter(log => log.name === EVENT_NAME)
          .map(log => log.raw.address)
      ));
    
    const extractEventDetails = (logs: any[]) => {
      return logs
        .filter(log => log.name === EVENT_NAME)
        .map(log => {
          const result: { [key: string]: string } = {
            name: log.name
          };
    
        log.inputs.forEach((input: any) => {
          const name = input.soltype.name;
          const value = input.value.toString();
          result[name] = value;
        });
    
        return result;
      });
    };
    
    Этот раздел:
    • Фильтрует события по имени
    • Извлекает параметры события
    • Обрабатывает адреса событий

    Взаимодействие с webhook

    Система отправляет эти структурированные данные на ваш webhook с помощью Axios:
    showLineNumbers
    {
      hash: string,              // Transaction hash
      transaction: object,       // Full transaction event
      blockHash: string,        // Block hash
      blockNumber: number,      // Block number
      matchReasons: Array<{     // Event details
        name: string,
        [parameterName: string]: string
      }>,
      sentinel: {               // Contract information
        contractName: string,
        abi: object
      },
      traceData: object,        // Transaction trace data
      addresses: string[]       // Involved addresses
    }
    

    Устранение неполадок

    Распространённые проблемы и решения:
    1. Пропущенные события
      • Убедитесь, что EVENT_NAME точно соответствует событиям контракта
      • Проверьте конфигурацию оповещения в панели Tenderly
      • Просмотрите логи транзакции на предмет генерации события
    2. Ошибки API
      • Проверьте права токена BEARER
      • Проверьте доступность endpoint API
      • Проверьте настройки сети
    3. Проблемы с webhook
      • Убедитесь в доступности WEBHOOK_URL
      • Проверьте, что формат полезной нагрузки соответствует ожиданиям
      • Отслеживайте логи endpoint’а webhook