Разработка скриптов деплоя смарт-контрактов
Разовый деплой через forge create или npx hardhat deploy с хардкодированными параметрами — это технический долг. Когда нужно задеплоить на 5 чейнов, потом воспроизвести на тестнете для аудиторов, потом повторить через 3 месяца для новой версии — выясняется, что никто не помнит точный порядок вызовов, какие контракты нужно инициализировать после деплоя и на каком блоке была верификация.
Forge Script как стандарт
Foundry Script (.s.sol) — Solidity-файл, который выполняется как деплой-скрипт. Ключевое преимущество: один язык для контрактов и деплоя, проверка типов компилятором, возможность тестировать сам скрипт.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Script, console} from "forge-std/Script.sol";
import {MyProtocol} from "../src/MyProtocol.sol";
import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
contract DeployMyProtocol is Script {
function run() external {
uint256 deployerKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerKey);
vm.startBroadcast(deployerKey);
ProxyAdmin admin = new ProxyAdmin(deployer);
MyProtocol implementation = new MyProtocol();
bytes memory initData = abi.encodeCall(
MyProtocol.initialize,
(vm.envAddress("TREASURY"), vm.envUint("FEE_BPS"))
);
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(implementation),
address(admin),
initData
);
console.log("ProxyAdmin:", address(admin));
console.log("Implementation:", address(implementation));
console.log("Proxy:", address(proxy));
vm.stopBroadcast();
}
}
Запуск: forge script script/DeployMyProtocol.s.sol --rpc-url $RPC --broadcast --verify.
Флаг --verify автоматически верифицирует все задеплоенные контракты через Etherscan API. --slow добавляет задержку между транзакциями — нужно для RPC-провайдеров с rate limits.
Параметризация через окружение
Никакого хардкода адресов в скрипте. Всё через environment variables:
address treasury = vm.envAddress("TREASURY");
uint256 fee = vm.envUint("FEE_BPS");
bool isMainnet = vm.envBool("IS_MAINNET");
Для разных окружений: файлы .env.sepolia, .env.mainnet, .env.polygon. Скрипт деплоя один и тот же.
Логирование адресов задеплоенных контрактов
После деплоя адреса нужно зафиксировать. Подходы:
JSON файл через foundry's --json флаг. forge script ... --json > deployments/sepolia.json. Структурированный вывод с адресами, transaction hashes, block numbers.
Кастомное логирование в скрипте. Через vm.writeJson() и vm.writeFile():
string memory json = vm.serializeAddress("deployment", "proxy", address(proxy));
vm.writeJson(json, string.concat("deployments/", vm.toString(block.chainid), ".json"));
Файлы деплоя коммитим в репозиторий — они служат источником истины для frontend, аналитики и будущих upgrade-скриптов.
Upgrade скрипты
Для UUPS и Transparent Proxy паттернов отдельный скрипт для каждого upgrade:
contract UpgradeV2 is Script {
function run() external {
address proxy = vm.envAddress("PROXY_ADDRESS");
address admin = vm.envAddress("PROXY_ADMIN");
vm.startBroadcast(vm.envUint("PRIVATE_KEY"));
MyProtocolV2 newImpl = new MyProtocolV2();
ProxyAdmin(admin).upgradeAndCall(
ITransparentUpgradeableProxy(proxy),
address(newImpl),
""
);
vm.stopBroadcast();
}
}
Каждый upgrade-скрипт имеет имя с версией (UpgradeToV2.s.sol) и хранится в истории репозитория.
Мультичейн деплой
Скрипт запускается последовательно для каждого чейна:
forge script script/Deploy.s.sol --rpc-url $ETHEREUM_RPC --broadcast --verify
forge script script/Deploy.s.sol --rpc-url $POLYGON_RPC --broadcast --verify --verifier-url $POLYGONSCAN_API
forge script script/Deploy.s.sol --rpc-url $ARBITRUM_RPC --broadcast --verify
Или через Makefile / shell скрипт с итерацией по массиву RPC endpoints.
Сроки
Написание базового деплой-скрипта с параметризацией и логированием: 1 день. Полная deployment infrastructure с upgrade скриптами, мультичейн поддержкой и CI интеграцией: 2-3 дня.
Стоимость рассчитывается после уточнения количества контрактов и целевых сетей.







