Forge Deployment
Deploy and verify smart contracts using Foundry's deployment tools.
When to Use
- •Deploying contracts to testnets or mainnet
- •Writing deployment scripts in Solidity
- •Verifying contracts on Etherscan/Sourcify
- •Managing multi-chain deployments
- •Setting up deterministic deployments (CREATE2)
Quick Deploy with forge create
bash
# Basic deployment
forge create src/MyContract.sol:MyContract \
--rpc-url $RPC_URL \
--private-key $PRIVATE_KEY \
--broadcast
# With constructor arguments
forge create src/MyContract.sol:MyContract \
--rpc-url $RPC_URL \
--private-key $PRIVATE_KEY \
--constructor-args "arg1" 123 0xAddress \
--broadcast
# Deploy and verify
forge create src/MyContract.sol:MyContract \
--rpc-url $RPC_URL \
--private-key $PRIVATE_KEY \
--verify \
--etherscan-api-key $ETHERSCAN_API_KEY \
--broadcast
Deployment Scripts
See scripts.md for comprehensive script patterns.
Basic Script
solidity
// script/Deploy.s.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Script, console} from "forge-std/Script.sol";
import {MyContract} from "../src/MyContract.sol";
contract DeployScript is Script {
function run() public {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
MyContract myContract = new MyContract();
console.log("MyContract deployed to:", address(myContract));
vm.stopBroadcast();
}
}
Running Scripts
bash
# Dry run (simulation)
forge script script/Deploy.s.sol
# Broadcast to network
forge script script/Deploy.s.sol \
--rpc-url $RPC_URL \
--broadcast
# Broadcast and verify
forge script script/Deploy.s.sol \
--rpc-url $RPC_URL \
--broadcast \
--verify \
--etherscan-api-key $ETHERSCAN_API_KEY
# Resume failed broadcast
forge script script/Deploy.s.sol \
--rpc-url $RPC_URL \
--resume
Environment Configuration
foundry.toml
toml
[rpc_endpoints]
mainnet = "${MAINNET_RPC_URL}"
sepolia = "${SEPOLIA_RPC_URL}"
arbitrum = "${ARBITRUM_RPC_URL}"
base = "${BASE_RPC_URL}"
[etherscan]
mainnet = { key = "${ETHERSCAN_API_KEY}" }
sepolia = { key = "${ETHERSCAN_API_KEY}" }
arbitrum = { key = "${ARBISCAN_API_KEY}" }
base = { key = "${BASESCAN_API_KEY}" }
.env File
bash
PRIVATE_KEY=0x... MAINNET_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/... SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/... ETHERSCAN_API_KEY=...
Contract Verification
During Deployment
bash
forge create src/MyContract.sol:MyContract \
--rpc-url sepolia \
--private-key $PRIVATE_KEY \
--verify \
--etherscan-api-key $ETHERSCAN_API_KEY \
--broadcast
After Deployment
bash
# Basic verification
forge verify-contract \
0xContractAddress \
src/MyContract.sol:MyContract \
--chain sepolia \
--etherscan-api-key $ETHERSCAN_API_KEY
# With constructor args
forge verify-contract \
0xContractAddress \
src/MyContract.sol:MyContract \
--chain sepolia \
--constructor-args $(cast abi-encode "constructor(address,uint256)" 0xOwner 1000) \
--etherscan-api-key $ETHERSCAN_API_KEY
# Watch for completion
forge verify-contract \
0xContractAddress \
src/MyContract.sol:MyContract \
--chain sepolia \
--watch
Verify on Sourcify
bash
forge verify-contract \
0xContractAddress \
src/MyContract.sol:MyContract \
--chain sepolia \
--verifier sourcify
Deterministic Deployments (CREATE2)
Using CREATE2
solidity
contract DeployScript is Script {
function run() public {
bytes32 salt = keccak256("my-salt-v1");
vm.startBroadcast();
// CREATE2 deployment
MyContract myContract = new MyContract{salt: salt}();
vm.stopBroadcast();
// Address is deterministic based on:
// - deployer address
// - salt
// - contract bytecode
}
}
Pre-computing Address
solidity
function computeCreate2Address(
bytes32 salt,
bytes32 initCodeHash,
address deployer
) internal pure returns (address) {
return address(uint160(uint256(keccak256(abi.encodePacked(
bytes1(0xff),
deployer,
salt,
initCodeHash
)))));
}
Multi-Chain Deployment
See multichain.md for patterns.
solidity
contract MultiChainDeploy is Script {
function run() public {
// Deploy to multiple chains
deployToChain("mainnet");
deployToChain("arbitrum");
deployToChain("base");
}
function deployToChain(string memory chain) internal {
vm.createSelectFork(chain);
vm.startBroadcast();
new MyContract();
vm.stopBroadcast();
}
}
Gas Estimation
bash
# Estimate gas before deployment forge script script/Deploy.s.sol --rpc-url $RPC_URL # Output shows estimated gas for each transaction
Transaction Management
Pending Transactions
bash
# List pending transactions forge script script/Deploy.s.sol --rpc-url $RPC_URL --broadcast # Resume after failure forge script script/Deploy.s.sol --rpc-url $RPC_URL --resume # Speed up pending tx cast send --rpc-url $RPC_URL --gas-price 50gwei ...
Transaction Files
Broadcasts are saved to:
code
broadcast/
└── Deploy.s.sol/
└── 11155111/ # Chain ID (Sepolia)
├── run-latest.json # Latest run
└── run-1234567890.json
Best Practices
- •Always dry-run first: Run without
--broadcastto simulate - •Use environment variables: Never commit private keys
- •Verify contracts: Always verify source code on explorers
- •Save deployment addresses: Log addresses for future reference
- •Test on testnets first: Deploy to Sepolia before mainnet
- •Use deterministic deployments: CREATE2 for consistent addresses