Deploy and verify smart contracts with Foundry. Use when deploying contracts, writing deployment scripts, verifying on block explorers, or managing multi-chain deployments.
Deploy and verify smart contracts using Foundry's deployment tools.
# 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
See scripts.md for comprehensive script patterns.
// 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();
}
}
# 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
[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}" }
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=...
forge create src/MyContract.sol:MyContract \
--rpc-url sepolia \
--private-key $PRIVATE_KEY \
--verify \
--etherscan-api-key $ETHERSCAN_API_KEY \
--broadcast
# 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
forge verify-contract \
0xContractAddress \
src/MyContract.sol:MyContract \
--chain sepolia \
--verifier sourcify
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
}
}
function computeCreate2Address(
bytes32 salt,
bytes32 initCodeHash,
address deployer
) internal pure returns (address) {
return address(uint160(uint256(keccak256(abi.encodePacked(
bytes1(0xff),
deployer,
salt,
initCodeHash
)))));
}
See multichain.md for patterns.
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();
}
}
# Estimate gas before deployment
forge script script/Deploy.s.sol --rpc-url $RPC_URL
# Output shows estimated gas for each transaction
# 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 ...
Broadcasts are saved to:
broadcast/
└── Deploy.s.sol/
└── 11155111/ # Chain ID (Sepolia)
├── run-latest.json # Latest run
└── run-1234567890.json
--broadcast to simulate