Scripting
Forge scripts are Solidity files that deploy contracts and execute transactions on-chain. They replace deployment scripts traditionally written in JavaScript.
For sender selection, CREATE2, library linking, nonces, simulation, and resume semantics, see How scripting works.
Script structure
Scripts inherit from Script and implement a run() function:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {Script} from "forge-std/Script.sol";
import {Counter} from "../src/Counter.sol";
contract DeployScript is Script {
function run() public {
vm.startBroadcast();
Counter counter = new Counter();
counter.setNumber(42);
vm.stopBroadcast();
}
}Key elements:
- Inherit from
forge-std/Script.sol - Script files end with
.s.sol - Wrap deployment logic in
vm.startBroadcast()/vm.stopBroadcast()
Running scripts
Simulate a deployment (no transactions sent):
$ forge script script/Deploy.s.solBroadcast transactions to a network:
$ forge script script/Deploy.s.sol --broadcast --rpc-url $RPC_URLUsing RPC aliases
Define aliases in foundry.toml and pass the alias to --rpc-url:
[rpc_endpoints]
sepolia = "${SEPOLIA_RPC_URL}"$ forge script script/Deploy.s.sol --broadcast --rpc-url sepoliaProviding a private key
$ forge script script/Deploy.s.sol --broadcast --rpc-url $RPC_URL --account deployer$ forge script script/Deploy.s.sol --broadcast --rpc-url $RPC_URL --ledger$ forge script script/Deploy.s.sol --broadcast --rpc-url $RPC_URL --browser$ forge script script/Deploy.s.sol --broadcast --rpc-url $RPC_URL --private-key $PRIVATE_KEYSee Browser Wallet Signing for the local connection flow, network checks, and a simulation-first deployment workflow.
Broadcasting from a specific address
To broadcast from a specific address:
vm.startBroadcast(deployerAddress);Or derive the sender from a private key read from the environment:
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);The address overload selects a transaction sender but does not supply a signing key. Provide a matching wallet when broadcasting, or use --unlocked with a node that controls that account. The private-key overload derives the address and adds the key to Forge's script wallets.
Starting a broadcast changes the sender of outgoing calls; it does not change msg.sender in the script's current frame. Pass the intended owner or deployer explicitly in transaction arguments. Inside a contract called directly by the broadcast transaction, msg.sender is the transaction sender.
Overriding the sender nonce
By default the sender's starting nonce is fetched from the RPC endpoint, or set to 1 when no endpoint is configured. Pass --sender-nonce to pin it instead:
$ forge script script/Deploy.s.sol --rpc-url $RPC_URL --sender-nonce 7The override applies to script execution and transaction generation and is kept even when broadcasting switches to a different sender. Because addresses of contracts deployed with CREATE depend on the sender's nonce, this keeps simulated deployment addresses consistent with the nonce you plan to broadcast from. See forge script for the full option reference.
Verifying deployed contracts
Verify on Etherscan during deployment:
$ forge script script/Deploy.s.sol \
--broadcast \
--rpc-url $RPC_URL \
--verify \
--etherscan-api-key $ETHERSCAN_API_KEYResuming failed broadcasts
If a broadcast fails partway through, resume from where it left off:
$ forge script script/Deploy.s.sol --broadcast --rpc-url $RPC_URL --resumeMulti-chain deployments
Deploy to multiple chains by running the script with different RPC URLs:
$ forge script script/Deploy.s.sol --broadcast --rpc-url $MAINNET_RPC
$ forge script script/Deploy.s.sol --broadcast --rpc-url $ARBITRUM_RPC
$ forge script script/Deploy.s.sol --broadcast --rpc-url $OPTIMISM_RPCReading deployment artifacts
Scripts write transaction receipts to broadcast/. Access deployed addresses in subsequent scripts:
function run() public {
string memory json = vm.readFile("broadcast/Deploy.s.sol/1/run-latest.json");
address counter = vm.parseJsonAddress(json, ".transactions[0].contractAddress");
}Script cheatcodes
Scripts have access to all cheatcodes. Common ones for scripting:
// Read environment variables
string memory rpcUrl = vm.envString("RPC_URL");
uint256 privateKey = vm.envUint("PRIVATE_KEY");
// Read/write files
string memory config = vm.readFile("config.json");
vm.writeFile("output.txt", "deployed");
// Parse JSON
address addr = vm.parseJsonAddress(json, ".address");
// Console logging
console.log("Deploying to:", block.chainid);Dry run
Test a script without sending transactions:
$ forge script script/Deploy.s.sol --rpc-url $RPC_URLThis simulates against the live chain state and shows what would happen.
Was this helpful?
