Foundry пришёл в экосистему Solidity как инструмент, который умеет многое и делает это быстро. В этой статье я подробно объясню, как начать работать с Foundry, какие у него ключевые возможности и на что стоит обращать внимание при написании тестов.

Что такое Foundry и почему он нужен

Foundry — это набор инструментов для разработки и тестирования смарт-контрактов на Solidity. В него входят forge для компиляции и запуска тестов, anvil как локальный тестовый узел и cast для взаимодействия с сетью из командной строки.

Главное преимущество в скорости и удобстве рабочего процесса: компиляция и тесты выполняются заметно быстрее, чем в традиционных окружениях на базе JavaScript. Кроме того, встроенные «cheatcodes» дают контроль над окружением теста — можно изменять время, баланс адресов и симулировать поведение транзакций без написания сложных вспомогательных скриптов.

Установка и базовая настройка

Установка — простой шаг. На официальном сайте Foundry есть скрипт для быстрого развёртывания, но в CI предпочтительнее использовать официальные actions или пакеты в менеджерах системы.

После установки достаточно инициализировать проект командой forge init внутри папки проекта. Эта команда создаёт минимальную структуру: папку src для контрактов и folder test для тестов, а также файл foundry.toml с конфигурацией.

Примеры команд

Ниже приведён набор основных команд, с которыми вы будете регулярно работать. Они помогают компилировать, тестировать и запускать локальную ноду.

Команда Назначение
forge build Компиляция контрактов
forge test Запуск тестов
anvil Локальная нода для тестирования и форкинга сети
cast Отправка транзакций и вызовов через CLI

Структура тестов и шаблонный пример

В Foundry тесты обычно пишут на Solidity и организуют в файлах внутри каталога test. Тестовые контракты наследуются от Test из пакета forge-std, который предоставляет удобные утилиты и доступ к vm — интерфейсу для управления окружением.

Ниже простой пример теста. Он демонстрирует setUp, создание экземпляра контракта и использование assertEq для проверки состояния.

pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import "../src/MyContract.sol";

contract MyContractTest is Test {
    MyContract c;

    function setUp() public {
        c = new MyContract();
    }

    function testInitialValue() public {
        assertEq(c.value(), 0);
    }
}

Этот подход делает тесты компактными и читаемыми: вся логика и проверки находятся в Solidity, без перехода на JavaScript. Благодаря этому легче поддерживать соответствие между контрактами и их тестами.

Cheatcodes и работа с окружением

Одно из самых полезных свойств Foundry — наличие cheatcodes. Через vm можно подменять адрес отправителя, изменять время и баланс, ожидать revert и многое другое. Это снижает количество подготовительного кода.

Примеры часто используемых функций: vm.prank(address) для эмуляции транзакции от другого адреса, vm.warp(timestamp) для изменения времени в блокчейне и vm.deal(address, amount) для пополнения баланса тестового адреса. Они упрощают проверку сценариев, зависящих от внешних условий.

Пример с cheatcodes

Ниже небольшой фрагмент, иллюстрирующий использование vm.prank и vm.expectRevert при тестировании проверок доступа.

function testOnlyOwner() public {
    vm.prank(address(0xBEEF));
    vm.expectRevert("Ownable: caller is not the owner");
    c.restrictedAction();
}

Такие приёмы сокращают объём вспомогательного кода и делают тесты более нацеленными на конкретные поведения контракта.

Fuzzing, форкинг и другие продвинутые возможности

Foundry поддерживает fuzz-тестирование из коробки. Достаточно объявить параметры функции теста, и runner будет генерировать случайные значения. Это мощный способ находить граничные случаи, которые сложно предусмотреть вручную.

Для контроля генерации можно использовать vm.assume, чтобы отбрасывать недопустимые значения. В случае провала Foundry пытается «сжать» входные данные до минимального примера, что облегчает отладку.

Форкинг mainnet и анализ взаимодействий

Анвил позволяет форкать mainnet и запускать тесты против реального состояния сети. Это удобно, когда нужно проверить взаимодействие с популярными протоколами или контрактами сторонних авторов.

Команда для запуска тестов с форком выглядит примерно так: forge test —fork-url . При работе с форком стоит учитывать лимиты провайдера RPC и следить за приватными ключами при использовании в CI.

Интеграция в CI и рекомендации по рабочему процессу

Включение Foundry в CI повышает уверенность в релизах. Базовый сценарий включает установку Foundry на агенте, запуск forge test и, при необходимости, использование anvil для тестов с форком.

Для GitHub Actions существует официальный action, который упрощает установку. Важно кэшировать папки с кэшем сборки и артефактами компиляции, чтобы уменьшить время выполнения пайплайнов.

# Пример шага для GitHub Actions
- name: Setup Foundry
  uses: foundry-rs/setup-foundry@v1
- name: Run tests
  run: forge test --fork-url ${{ secrets.RPC_URL }}

Практические советы и типичные ошибки

Одна из частых ошибок — неправильные remappings для импортов, особенно при использовании внешних библиотек. Проверяйте foundry.toml и корректно указывайте пути, чтобы компилятор находил зависимости.

Ещё одна ловушка — тесты, зависящие от глобального состояния. Если тесты не изолированы, они могут становиться флакy — вести себя по-разному при повторных запусках. Решение простое: восстанавливайте состояние в setUp и используйте отдельные экземпляры контрактов.

Также стоит следить за версиями Solidity. Даже незначительные изменения в компиляторе могут повлиять на поведение, поэтому фиксируйте версию в pragma и в конфигурации проекта.

Мой опыт: как Foundry поменял подход к тестированию

В нескольких проектах я заметил, что переход на Foundry резко сократил время на обратную связь. Тесты, которые раньше выполнялись минуты, теперь проходят за секунды. Это стимулирует писать больше проверок и чаще запускать их локально.

Особенно помогли cheatcodes: тесты, для которых раньше требовалось развертывание сложного окружения, стали простыми и устойчивыми. Форкинг mainnet упростил проверку интеграций с DeFi-протоколами без необходимости развертывать моки.

Когнитивная экономия: как писать тесты, которые почти не ломаются

Пиши тесты небольшими и явными. Каждая функция теста должна проверять одну поведение. Если в тесте много шагов, при ошибке сложнее понять причину и восстановить минимальный пример.

Используй invariants и property-based подходы для контроля общих свойств контракта. Это часто даёт больше пользы, чем множество частных кейсов, поскольку ловит неожиданные нарушения логики при изменениях кода.

Заключительная мысль

Foundry — это не просто ещё один инструмент в наборе разработчика, это платформа, которая меняет рабочий цикл разработки смарт-контрактов. Быстрая компиляция, удобные cheatcodes, встроенный fuzzing и простая интеграция в CI делают его отличным выбором для серьезных проектов.

Если вы ещё не пробовали работать с ним, начните с небольшого тестового репозитория: инициализируйте проект, напишите пару тестов и оцените скорость обратной связи. Через несколько итераций вы заметите, как подход к качеству кода становится более практичным и предсказуемым.