Complete Guide to Deploying Smart Contracts on Mainnet with Foundry

Table Of Contents
- Introduction to Foundry and Mainnet Deployments
- Prerequisites
- Setting Up Your Foundry Project
- Creating a Simple Smart Contract
- Testing Your Smart Contract
- Configuring for Mainnet Deployment
- Deploying to Mainnet
- Verifying Your Contract
- Best Practices for Mainnet Deployments
- Troubleshooting Common Issues
- Conclusion
Complete Guide to Deploying Smart Contracts on Mainnet with Foundry
Deploying smart contracts to Ethereum mainnet represents a significant milestone in any blockchain developer's journey. It's the moment when your code transitions from a theoretical construct to a living entity on the world's most established blockchain network. While development and testnet deployments provide valuable experience, mainnet deployments introduce new considerations around security, gas optimization, and real financial impact.
Foundry has emerged as one of the most powerful and developer-friendly toolchains in the Ethereum ecosystem. Created by Paradigm, this Rust-based suite of tools offers significant improvements in testing speed, deployment options, and overall developer experience compared to earlier alternatives. With its integrated testing framework (Forge), contract deployment tool (Anvil), and script execution environment (Cast), Foundry provides everything needed to take your smart contract from concept to mainnet.
In this comprehensive tutorial, we'll walk through the entire process of deploying an Ethereum smart contract to mainnet using Foundry. We'll cover project setup, contract creation, thorough testing, secure configuration for mainnet, the actual deployment process, and contract verification. By the end, you'll have the knowledge and confidence to deploy your own contracts to Ethereum mainnet using industry-standard tools and best practices.
Introduction to Foundry and Mainnet Deployments
Foundry represents the next generation of Ethereum development tools, designed from the ground up with speed and developer experience in mind. Unlike earlier frameworks that relied on JavaScript, Foundry is built in Rust, offering significant performance improvements - particularly for testing and compilation tasks.
The Foundry toolchain consists of three main components:
- Forge - A testing framework for Ethereum smart contracts
- Cast - A command-line tool for interacting with smart contracts, sending transactions, and getting blockchain data
- Anvil - A local Ethereum node designed for development purposes, similar to Ganache
When deploying to mainnet, we're making the jump from test environments to the live Ethereum blockchain where real economic value is at stake. This transition introduces several important considerations:
- Real costs: Mainnet deployments and transactions cost actual ETH
- Permanence: Once deployed, contract code cannot be changed (unless specifically designed for upgradability)
- Security: Vulnerabilities can lead to real financial losses
- Gas optimization: Efficiency becomes critical when users are paying real gas fees
This tutorial assumes you've already worked with smart contracts on testnets and are ready to make the leap to mainnet deployment.
Prerequisites
Before we begin our mainnet deployment journey, you'll need to have the following ready:
- Foundry installed: Make sure you have the latest version of Foundry installed on your system
- An Ethereum account with ETH: You'll need an account with sufficient ETH to cover deployment gas costs
- A secure way to manage your private key: Using environment variables or a secure key management solution
- Basic Solidity knowledge: Understanding of smart contract development fundamentals
- RPC endpoint: Access to an Ethereum mainnet RPC endpoint (through providers like Infura, Alchemy, or your own node)
If you haven't installed Foundry yet, you can do so with the following command:
bash curl -L https://foundry.paradigm.xyz | bash foundryup
Setting Up Your Foundry Project
Let's start by creating a new Foundry project. Open your terminal and run:
bash forge init mainnet_deployment_demo cd mainnet_deployment_demo
This creates a new Foundry project with the following structure:
mainnet_deployment_demo/ ├── foundry.toml # Foundry configuration file ├── lib/ # Dependencies directory ├── script/ # Deployment scripts │ └── Counter.s.sol ├── src/ # Smart contract source files │ └── Counter.sol └── test/ # Test files └── Counter.t.sol
The default project comes with a simple Counter contract. For this tutorial, we'll create our own smart contract to deploy to mainnet.
Creating a Simple Smart Contract
Let's create a straightforward ERC20 token contract as our deployment example. This is a common use case and demonstrates many of the key principles involved in mainnet deployments.
Create a new file in the src
directory called Token.sol
:
solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol";
contract HackQuestToken is ERC20, Ownable { constructor(address initialOwner) ERC20("HackQuest Token", "HQT") Ownable(initialOwner) { // Mint 1 million tokens to the contract creator _mint(initialOwner, 1_000_000 * 10 ** decimals()); }
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
To use OpenZeppelin contracts, we need to install them as a dependency:
bash forge install OpenZeppelin/openzeppelin-contracts --no-commit
Now, let's update the foundry.toml
file to include the proper remappings:
toml [profile.default] src = "src" out = "out" libs = ["lib"]
Add OpenZeppelin remapping
remappings = ["@openzeppelin/=lib/openzeppelin-contracts/"]
[rpc_endpoints] mainnet = "${MAINNET_RPC_URL}"
[etherscan] mainnet = { key = "${ETHERSCAN_API_KEY}" }
Testing Your Smart Contract
Before deploying to mainnet, thorough testing is essential. Let's create a test file for our token contract. Create a new file in the test
directory called Token.t.sol
:
solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.13;
import "forge-std/Test.sol"; import "../src/Token.sol";
contract TokenTest is Test { HackQuestToken public token; address public owner; address public user;
function setUp() public {
owner = address(this);
user = address(0x1);
token = new HackQuestToken(owner);
}
function testInitialSupply() public {
assertEq(token.totalSupply(), 1_000_000 * 10 ** 18);
assertEq(token.balanceOf(owner), 1_000_000 * 10 ** 18);
}
function testMinting() public {
uint256 initialSupply = token.totalSupply();
token.mint(user, 1000 * 10 ** 18);
assertEq(token.totalSupply(), initialSupply + 1000 * 10 ** 18);
assertEq(token.balanceOf(user), 1000 * 10 ** 18);
}
function testOnlyOwnerCanMint() public {
vm.prank(user);
vm.expectRevert();
token.mint(user, 1000 * 10 ** 18);
}
function testTransfer() public {
token.transfer(user, 1000 * 10 ** 18);
assertEq(token.balanceOf(user), 1000 * 10 ** 18);
assertEq(token.balanceOf(owner), 1_000_000 * 10 ** 18 - 1000 * 10 ** 18);
}
}
Run the tests to make sure everything works as expected:
bash forge test
You should see all tests passing. This gives us confidence that our smart contract is functioning correctly before we deploy it to mainnet.
Configuring for Mainnet Deployment
Now that we have a tested contract, we need to prepare for mainnet deployment. This involves setting up environment variables and creating a deployment script.
Setting Up Environment Variables
For security reasons, we should never hardcode private keys or API keys in our codebase. Instead, we'll use environment variables. Create a .env
file at the root of your project (and make sure to add it to .gitignore
):
bash MAINNET_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY PRIVATE_KEY=your_private_key_here ETHERSCAN_API_KEY=your_etherscan_api_key_here
Load these environment variables in your terminal:
bash source .env
Configuring foundry.toml
We've already updated our foundry.toml
file earlier, but let's make sure it has the necessary configurations for mainnet deployment:
toml [profile.default] src = "src" out = "out" libs = ["lib"] remappings = ["@openzeppelin/=lib/openzeppelin-contracts/"]
[rpc_endpoints] mainnet = "${MAINNET_RPC_URL}"
[etherscan] mainnet = { key = "${ETHERSCAN_API_KEY}" }
For deployment scripts
[profile.default.fuzz] runs = 256
[profile.default.invariant] runs = 256
[profile.ci.fuzz] runs = 1000
[profile.ci.invariant] runs = 1000
Next, let's create a deployment script. Create a new file in the script
directory called DeployToken.s.sol
:
solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.13;
import "forge-std/Script.sol"; import "../src/Token.sol";
contract DeployToken is Script { function run() external { // Retrieve the private key from environment variable uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); address deployerAddress = vm.addr(deployerPrivateKey);
console.log("Deploying HackQuestToken with address: ", deployerAddress);
// Start broadcasting transactions
vm.startBroadcast(deployerPrivateKey);
// Deploy the token contract, passing the deployer address as the initial owner
HackQuestToken token = new HackQuestToken(deployerAddress);
console.log("Token deployed at: ", address(token));
console.log("Initial supply: ", token.totalSupply() / 10**18, " HQT");
// Stop broadcasting transactions
vm.stopBroadcast();
}
}
Deploying to Mainnet
Before we actually deploy to mainnet, it's wise to simulate the deployment and estimate the gas costs involved.
Estimating Gas Costs
To estimate the gas required for deployment, we can run the script in a dry-run mode:
bash forge script script/DeployToken.s.sol --rpc-url $MAINNET_RPC_URL --private-key $PRIVATE_KEY --estimate
This command will provide an estimate of the gas required for deployment. Make note of this value and ensure you have enough ETH in your deployer account to cover the gas cost plus a safety margin.
Executing the Deployment
Now that we've estimated the gas costs, we're ready to deploy our contract to mainnet. This is a significant step that will cost real ETH, so double-check everything before proceeding.
To deploy the contract:
bash forge script script/DeployToken.s.sol --rpc-url $MAINNET_RPC_URL --private-key $PRIVATE_KEY --broadcast --verify
The --broadcast
flag tells Forge to actually send the transactions to the network, and --verify
will attempt to verify the contract on Etherscan after deployment.
During the deployment process, Forge will display information about the transactions being sent, including the contract address once deployment is complete. Make sure to save this address!
Verifying Your Contract
If the automatic verification with the --verify
flag didn't work, you can manually verify your contract on Etherscan:
bash forge verify-contract <DEPLOYED_CONTRACT_ADDRESS> src/Token.sol:HackQuestToken --chain mainnet --etherscan-api-key $ETHERSCAN_API_KEY --constructor-args $(cast abi-encode "constructor(address)" <OWNER_ADDRESS>)
Replace <DEPLOYED_CONTRACT_ADDRESS>
with your contract's address and <OWNER_ADDRESS>
with the address you used as the initial owner.
Verification allows users to inspect your contract's code on Etherscan, which is important for transparency and trust.
Best Practices for Mainnet Deployments
When deploying to mainnet, consider these best practices to ensure security and reliability:
-
Audit your code: For significant contracts, consider a professional audit before deployment
-
Test on multiple testnets: Before mainnet, deploy and test on multiple testnets including Sepolia and Goerli
-
Gas optimization: Review your contract for gas efficiency, especially for functions that will be called frequently
-
Use time-locks for admin functions: Consider implementing time delays for sensitive operations
-
Emergency stops: Include emergency stop mechanisms for critical contracts
-
Monitoring plan: Set up monitoring for your deployed contracts to quickly detect any issues
-
Securely manage private keys: Use hardware wallets or secure key management solutions for deployment
-
Documentation: Document your deployment process and contract addresses for future reference
Troubleshooting Common Issues
Even with careful planning, you may encounter issues during deployment. Here are solutions to common problems:
-
Transaction Underpriced: If gas prices spike during your deployment, your transaction might fail with an "underpriced" error. Solution: Increase the gas price and try again.
-
Nonce Too Low: This occurs if you've submitted multiple transactions. Solution: Use the
--legacy
flag with an appropriate nonce value. -
Out of Gas: Your deployment might require more gas than estimated. Solution: Increase the gas limit for your deployment transaction.
-
Verification Failures: Etherscan verification can fail if compiler settings don't match. Solution: Ensure your Solidity version and optimization settings match what was used for deployment.
-
RPC Errors: Issues with your RPC provider. Solution: Try a different RPC endpoint or provider.
For most deployment issues, the Foundry logs provide detailed information that can help diagnose the problem.
Conclusion
Congratulations! You've successfully learned how to deploy a smart contract to Ethereum mainnet using Foundry. This is a significant milestone in your Web3 development journey.
In this tutorial, we covered:
- Setting up a Foundry project from scratch
- Creating and testing an ERC20 token contract
- Configuring secure deployment using environment variables
- Estimating gas costs before deployment
- Deploying the contract to Ethereum mainnet
- Verifying the contract on Etherscan
- Best practices for mainnet deployments
- Troubleshooting common deployment issues
Mainnet deployments represent a transition from experimentation to production-grade blockchain development. The stakes are higher, but so is the impact of your work. By following the best practices outlined in this guide, you're well-positioned to deploy secure, efficient smart contracts that can operate reliably in production environments.
As you continue your Web3 development journey, remember that the blockchain ecosystem is constantly evolving. Stay updated with the latest security practices, gas optimization techniques, and tool improvements to ensure your smart contracts remain state-of-the-art.
Ready to deepen your blockchain development skills? Check out HackQuest's comprehensive learning tracks covering Ethereum, Solana, Arbitrum, Mantle, and other major ecosystems. Our interactive, hands-on approach will take you from beginner to certified Web3 developer with real-world projects and an integrated development environment.
Need testnet tokens for your development work? Visit our faucets page to get started.
Join HackQuest today and become part of a vibrant community of Web3 developers, participating in hackathons, co-learning camps, and exciting blockchain events!