One-time deployment via forge create or npx hardhat deploy with hardcoded parameters is technical debt. When you need to deploy to 5 chains, then reproduce on testnet for auditors, then repeat 3 months later for a new version — it turns out no one remembers the exact call order, which contracts need initialization after deployment, or at which block verification happened. Recently, a protocol team approached us: after their developer left, no one could reproduce the mainnet deployment. We wrote a script in one day, and now deployment takes 5 minutes. We specialize in creating professional turnkey deployment scripts: from parameterization to CI/CD integration. Our experience: 5+ years in Web3, over 50 successful projects. Reach out for a consultation or get an individual project estimate.
What Problems Do Deployment Scripts Solve?
Manual deployment via forge create or Hardhat console leads to several typical issues:
- Lack of reproducibility: repeating the exact call order a month later is a lottery.
- Initialization errors: forgot to call
initialize()after proxy — contract is non-functional. - Lost addresses: no one recorded where the proxy or admin is stored.
- Slow network switching: deploying to 5 networks manually wastes 80% of time.
Scripts solve these: single run, full log, automatic verification.
Why Forge Script Is the Industry Standard?
Foundry Script (.s.sol) is a Solidity file executed as a deployment script. Key advantage: one language for contracts and deployment, type checking by the compiler, ability to test the script itself. The Foundry documentation emphasizes that this significantly reduces errors compared to JS scripts.
// 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();
}
}
Run: forge script script/DeployMyProtocol.s.sol --rpc-url $RPC --broadcast --verify. The --verify flag automatically verifies all deployed contracts via Etherscan API. --slow adds a delay between transactions — needed for RPC providers with rate limits.
How Parameterization Eliminates Errors?
No hardcoded addresses in the script. Everything via environment variables:
address treasury = vm.envAddress("TREASURY");
uint256 fee = vm.envUint("FEE_BPS");
bool isMainnet = vm.envBool("IS_MAINNET");
For different environments: .env.sepolia, .env.mainnet, .env.polygon files. The deployment script is the same. This saves up to 40% of deployment preparation time.
Logging Deployed Contract Addresses
After deployment, addresses need to be recorded. Approaches:
JSON file via Foundry's --json flag. forge script ... --json > deployments/sepolia.json — structured output with addresses, transaction hashes, block numbers.
Custom logging in the script via vm.writeJson() and vm.writeFile():
string memory json = vm.serializeAddress("deployment", "proxy", address(proxy));
vm.writeJson(json, string.concat("deployments/", vm.toString(block.chainid), ".json"));
Deployment files are committed to the repository — they serve as the source of truth for frontend, analytics, and future upgrade scripts.
Upgrade Scripts and Multichain Deployment
For UUPS and Transparent Proxy patterns, a separate script per 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();
}
}
Each upgrade script is named with a version (UpgradeToV2.s.sol) and stored in the repository history.
Multichain deployment: script is run sequentially for each chain:
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
Or via a Makefile/shell script iterating over an array of RPC endpoints. Our scripts reduce deployment time by 80% compared to manual approach.
What Is Included in Our Work?
- Development of a base deployment script with parameterization (env).
- Setup of automatic verification via Etherscan API.
- Logging of all addresses and artifacts in JSON.
- Creation of upgrade scripts for UUPS/Transparent Proxy.
- Multichain support (Ethereum, Polygon, Arbitrum, BNB Chain, etc.).
- Integration with CI/CD (GitHub Actions, GitLab CI).
- Documentation and training for your team.
- Guarantee of correct deployment on testnet before mainnet.
Timelines and Cost
Writing a base deployment script with parameterization and logging: 1 day. Full deployment infrastructure with upgrade scripts, multichain support, and CI integration: 2-3 days. Cost is calculated individually after analyzing your project. Clients save up to 70% of deployment time, which, converted to engineer salary, means tens of thousands of dollars in savings per year. We'll assess your project for free — reach out to us.
Comparison: Manual Deployment vs Scripts
| Criteria | Manual Deployment | Deployment Scripts (Ours) |
|---|---|---|
| Time per 1 chain | 30-60 min | 2-5 min |
| Verification | manual via Etherscan | automatic |
| Reproducibility | low | 100% |
| Initialization errors | frequent | eliminated by tests |
| Multichain | days | 1 hour |
How We Guarantee Correctness?
We write tests for deployment scripts (e.g., via Foundry), simulate transactions in Tenderly, review logs, and conduct code reviews. After deployment, contracts are automatically verified. Get an engineer consultation — we'll discuss your project and propose a solution.







