Настройка Tenderly Web3 Actions
On-chain события требуют реакции — иногда немедленной. Если цена актива упала ниже порога ликвидации, если в контракт поступил крупный депозит, если oracle вернул устаревшее значение — всё это нужно обработать в секунды, не в часы. Классический подход: cron-скрипт на сервере, который каждую минуту опрашивает RPC. Это надёжно примерно до первого сбоя сервера или RPC-таймаута.
Tenderly Web3 Actions — это event-driven функции, которые запускаются непосредственно от on-chain событий. Инфраструктура Tenderly мониторит ноды, вы пишете логику.
Как это работает
Web3 Action — это TypeScript функция, которая выполняется в managed среде Tenderly при наступлении триггера. Триггеры бывают трёх типов:
Transaction triggers — реакция на транзакцию к конкретному контракту или от конкретного адреса. Можно фильтровать по статусу (success/failed), функции (by selector), или параметрам события.
Block triggers — выполнение на каждом блоке (или каждые N блоков). Используется для мониторинга состояния без ожидания конкретных событий: проверка health factor в lending протоколе, обновление off-chain индексов.
Webhook triggers — вызов Action через HTTP из внешней системы. Полезно для тестирования и для интеграции с off-chain системами.
Alert triggers — реакция на алерты из Tenderly Alerting (порог газа, крупная транзакция, изменение состояния контракта).
Настройка и деплой
Рабочий процесс через Tenderly CLI:
npm install -g @tenderly/cli
tenderly login
tenderly init
Структура проекта:
tenderly.yaml
actions/
src/
index.ts # точки входа для Actions
package.json
Пример Action, который реагирует на событие LargeDeposit и отправляет уведомление в Slack:
// actions/src/index.ts
import { ActionFn, Context, Event, TransactionEvent } from "@tenderly/actions";
import { ethers } from "ethers";
const THRESHOLD = ethers.parseEther("100"); // 100 ETH
export const onLargeDeposit: ActionFn = async (context: Context, event: Event) => {
const txEvent = event as TransactionEvent;
// Парсим logs транзакции
const iface = new ethers.Interface([
"event Deposit(address indexed user, uint256 amount)"
]);
for (const log of txEvent.logs) {
try {
const parsed = iface.parseLog(log);
if (parsed?.name === "Deposit" && parsed.args.amount > THRESHOLD) {
const webhookUrl = await context.secrets.get("SLACK_WEBHOOK_URL");
await fetch(webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: `Large deposit: ${ethers.formatEther(parsed.args.amount)} ETH from ${parsed.args.user}\nTx: ${txEvent.hash}`
})
});
}
} catch {
// Log не относится к нашему контракту — пропускаем
}
}
};
Конфигурация в tenderly.yaml:
account_id: "my-account"
project_slug: "my-project"
actions:
my-project/on-large-deposit:
runtime: v2
sources: actions/src
entrypoint: index:onLargeDeposit
trigger:
type: transaction
transaction:
status:
- mined
filters:
- network: 1
eventEmitted:
contract:
address: "0xYourContractAddress"
name: Deposit
Деплой:
tenderly actions deploy
Секреты и переменные окружения
API ключи, webhook URLs, private keys — всё через Tenderly Secrets, не хардкодим в код:
const apiKey = await context.secrets.get("MY_API_KEY");
const rpcUrl = await context.secrets.get("ALCHEMY_RPC_URL");
Секреты управляются через Dashboard или CLI:
tenderly actions secret set MY_API_KEY "value"
Storage между вызовами
Actions stateless по умолчанию, но Tenderly предоставляет простое key-value хранилище для persistence между вызовами:
// Сохраняем последний обработанный блок
await context.storage.putNumber("lastProcessedBlock", blockNumber);
// Читаем при следующем вызове
const lastBlock = await context.storage.getNumber("lastProcessedBlock");
Storage используем для: дедупликации уведомлений (не слать второй раз за то же событие), накопления статистики, хранения состояния между вызовами block trigger.
Практические сценарии
Автоматическое пополнение Chainlink subscription. Action следит за балансом LINK subscription. Если баланс падает ниже минимума — вызывает функцию topUp() через private key из Secrets.
Мониторинг health factor в lending. Block trigger каждые 10 блоков читает health factor всех открытых позиций. Если позиция близка к ликвидации — уведомление пользователю через push или email.
Синхронизация с off-chain базой данных. Транзакционный триггер на events Transfer/Mint/Burn — обновление PostgreSQL через REST API или прямое подключение.
Автоматический rebalancing. Если дисбаланс в treasury превысил порог — Action собирает и отправляет транзакцию через Tenderly Simulation сначала, потом live.
Ограничения
Максимальное время выполнения Action — 25 секунд. Для долгих задач (агрегация данных, сложные расчёты) нужен webhook к внешнему сервису или очередь задач. Runtime — Node.js v20, поддерживается большинство npm пакетов, но heavy native bindings не поддерживаются.
Настройка Tenderly Web3 Actions занимает 1-3 рабочих дня в зависимости от количества сценариев и интеграций. Стоимость рассчитывается индивидуально.







