Зачем нужен Tenderly Fork в продакшн-тестировании?
Представьте: вы разрабатываете DeFi-протокол, и нужно воспроизвести сложный flash loan attack, который произошёл на определённом блоке mainnet. Локальный anvil --fork-url требует настройки у каждого разработчика, а состояние не сохраняется между перезапусками. Мы используем Tenderly Fork для создания изолированной копии основной сети с полным контролем состояния. Это облачный форк, доступный по RPC URL, который можно передать команде или аудитору. В отличие от локального anvil --fork-url, форк Tenderly живёт в облаке и не умирает при закрытии терминала. Наш опыт работы с DeFi-протоколами показывает, что такой подход сокращает время на воспроизведение багов на 80%, а затраты на инфраструктуру тестирования снижаются в 2 раза. Tenderly Fork поддерживает Ethereum mainnet, Arbitrum, Polygon и другие сети, что позволяет тестировать межсетевые сценарии без развертывания собственных нод.
Почему Tenderly Fork превосходит anvil для командной работы?
anvil --fork-url запускается локально и умирает вместе с процессом. Tenderly Fork создаётся через API, живёт пока не удалён, его RPC URL можно передать фронтенду или мобильному приложению. Реальный кейс: контракт vault проходит аудит. Аудиторская команда хочет воспроизвести конкретный attack scenario — drain через flash loan в конкретном состоянии протокола. Вместо того чтобы настраивать локальную среду у каждого аудитора, создаётся Tenderly Fork, закрепляется на блоке с нужным состоянием, выставляется нужный баланс атакующего. Аудитор получает один RPC URL. Закажите настройку Tenderly Fork — и ваша команда сэкономит до 70% времени на подготовку тестовых сред.
Как создать Tenderly Fork через API?
const response = await fetch("https://api.tenderly.co/api/v1/account/MY_ACCOUNT/project/MY_PROJECT/fork", {
method: "POST",
headers: {
"X-Access-Key": process.env.TENDERLY_API_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify({
network_id: "1", // Ethereum mainnet
block_number: 19500000, // конкретный блок
transaction_index: 0,
initial_balance: 100,
chain_config: {
chain_id: 1
}
})
});
const { simulation_fork } = await response.json();
const forkRpcUrl = `https://rpc.tenderly.co/fork/${simulation_fork.id}`;
Манипуляция состоянием через JSON-RPC
Tenderly Fork поддерживает нестандартные методы:
// Установить баланс адреса
await provider.send("tenderly_setBalance", [
["0xUserAddress"],
"0x56BC75E2D63100000" // 100 ETH в hex
]);
// Имперсонировать аккаунт (подписывать от его имени)
await provider.send("tenderly_addBalance", [
["0xWhaleAddress"],
"0xDE0B6B3A7640000"
]);
// Установить значение в storage напрямую
await provider.send("tenderly_setStorageAt", [
contractAddress,
storageSlot, // keccak256 slot
newValue
]);
// Изменить timestamp
await provider.send("evm_setNextBlockTimestamp", [futureTimestamp]);
await provider.send("evm_mine", []);
Прямая запись в storage — мощный инструмент для тестирования: выставить paused = true в контракте без вызова pause(), симулировать что пользователь уже вложил $1M, обойти cooldown period.
Интеграция с Foundry
# Запуск тестов против Tenderly Fork
forge test --fork-url $TENDERLY_FORK_RPC --fork-block-number 19500000 -vvv
В Foundry тестах можно использовать vm.prank(whale) и vm.deal(attacker, 1000 ether) — они работают через Tenderly Fork так же как через anvil. Разница: состояние сохраняется между запусками если форк не пересоздан.
Как симулировать сложный сценарий атаки с помощью Tenderly Fork?
Tenderly Simulate API позволяет симулировать транзакцию и получить детальный trace до отправки на mainnet:
const simulation = await fetch(`https://api.tenderly.co/api/v1/account/${account}/project/${project}/simulate`, {
method: "POST",
headers: { "X-Access-Key": apiKey },
body: JSON.stringify({
network_id: "1",
from: senderAddress,
to: contractAddress,
input: calldata,
gas: 500000,
value: "0",
save: true // сохранить для просмотра в dashboard
})
});
Результат — полный call trace с газом на каждый вызов, storage changes, events. Это дешевле реального деплоя на mainnet для отладки сложных сценариев. Мы гарантируем, что симуляции будут точными и воспроизводимыми.
Типичные сценарии
Тестирование апгрейда на mainnet-состоянии. Fork на блок перед апгрейдом, выполняем upgrade транзакцию, проверяем что все пользовательские позиции читаются корректно новой имплементацией.
Воспроизведение инцидента. Форк на блок до взлома, воспроизводим вектор атаки. Находим root cause. Патчим. Проверяем что пропатченная версия устойчива.
Демо для инвесторов. Создаём Fork с нужным начальным состоянием (пользователи, позиции, балансы), передаём RPC URL. Инвестор взаимодействует с протоколом как с mainnet, но без реальных средств.
Сравнение инструментов для форка
| Параметр | Tenderly Fork | anvil | Hardhat fork |
|---|---|---|---|
| Размещение | Облако | Локально | Локально |
| Доступность | Постоянный RPC URL | Только локально | Только локально |
| Расшаренная ссылка | Да | Нет | Нет |
| Манипуляция состоянием | REST API + JSON-RPC | JSON-RPC (встроенные) | JSON-RPC (встроенные) |
| Интеграция с CI | Через API | Через скрипты | Через скрипты |
| Цена (оплата за использование) | Есть | Бесплатно | Бесплатно |
Этапы настройки Tenderly Fork
| Этап | Длительность | Результат |
|---|---|---|
| Создание форка и настройка API | 1 день | Рабочий RPC URL с нужным состоянием |
| Интеграция с CI/CD | 1 день | Автоматическое создание/удаление форков при тестах |
| Написание скриптов сценариев | 1-2 дня | Воспроизводимые тесты атак и апгрейдов |
| Документация и обучение команды | 0.5 дня | Гайд по использованию и API |
Что входит в настройку Tenderly Fork?
- Создание форка с привязкой к блоку и нужной сетью
- Настройка JSON-RPC методов для манипуляции состоянием (балансы, storage, время)
- Интеграция с CI/CD для автоматического создания и удаления форков
- Написание скриптов для воспроизведения тестовых сценариев
- Документация по использованию форка и API
- Поддержка в течение месяца после сдачи
Пример: воспроизведение взлома через flash loan
1. Создаём форк на блок за час до атаки. 2. Устанавливаем баланс атакующего: 1000 ETH. 3. Имперсонируем контракт пула ликвидности. 4. Вызываем функцию `flashLoan` с произвольными данными. 5. Проверяем, что баланс протокола уменьшился на сумму атаки.Источник: Tenderly Documentation
Получите консультацию по настройке Tenderly Fork — наши инженеры помогут подобрать оптимальную конфигурацию для вашего проекта.







