Deployment & Layer 2
You can write, test, and secure smart contracts — but they are worthless until they live on a real network. This final module of Part 2 takes your contracts from localhost to the world: testnet deployment, mainnet checklists, Etherscan verification, and Layer 2 rollups that slash gas costs by 10-100x. By the end, you will have deployed to a real chain.
Recap: From Code to Chain
In Part 2 you have built a complete smart contract development workflow: Solidity fundamentals (Module 7), advanced Solidity patterns (Module 8), ERC-20 tokens (Module 9), professional tooling with Hardhat (Module 10), testing with viem in TypeScript (Module 11), DApp frontends with MetaMask + viem (Module 12), and security best practices (Module 13). Your contracts are written, tested, and audited. Now it is time to deploy them to real networks.
The Deployment Journey
Deployment is not a single step — it is a pipeline. You start on a local node (Hardhat Network) for rapid iteration. Then you deploy to a testnet (Sepolia) to validate in a real blockchain environment with free test ETH. After thorough testing and an audit, you go to mainnet — where every transaction costs real money. Finally, you may deploy to a Layer 2 chain to reduce costs for your users.
Deployment Pipeline
Part 2 Finale
This is the final module of Part 2. After this module, you will have every skill needed to go from an idea to a live, verified, cost-efficient smart contract. Part 3 will shift focus to permissioned blockchains (Hyperledger Fabric) and enterprise use cases.
Testnet Deployment
Testnets are public blockchains that mirror mainnet behavior but use valueless ETH. They are your staging environment — deploy, test, break, and iterate without spending a cent. Sepolia is the recommended Ethereum testnet in 2024-2025.
Why Sepolia?
Sepolia is a proof-of-stake testnet with a small validator set, making it fast and stable. Unlike Goerli (deprecated), Sepolia has a controlled ETH supply so faucets can remain generous. All major tooling supports Sepolia: Hardhat, Foundry, Etherscan, Alchemy, Infura.
Getting Testnet ETH
You need test ETH to pay for gas. Use one of these faucets:
- Alchemy Sepolia Faucet — requires a free Alchemy account. Reliable and fast.
- Infura Sepolia Faucet — requires an Infura account. Good alternative.
- Google Cloud Web3 Faucet — no account needed. Limited to 0.05 ETH per day.
Faucets dispense small amounts (0.05-0.5 ETH). That is enough for dozens of deployments and hundreds of transactions.
Hardhat Configuration
Add Sepolia to your hardhat.config.js. You need an RPC endpoint (from Alchemy or Infura) and a deployer private key stored in a .env file.
// hardhat.config.js
require("dotenv").config();
require("@nomicfoundation/hardhat-toolbox");
module.exports = {
solidity: "0.8.24",
networks: {
sepolia: {
url: process.env.SEPOLIA_RPC_URL,
accounts: [process.env.DEPLOYER_PRIVATE_KEY],
},
},
};Environment Variables (.env)
# .env — NEVER commit this file!
SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY
DEPLOYER_PRIVATE_KEY=0xabc123...your_private_keyNever commit your .env file. Add it to .gitignore immediately. A leaked private key means all funds in that wallet are gone.
Deploy with Hardhat Ignition
Hardhat Ignition is the modern deployment system for Hardhat. You define deployment modules in JavaScript, and Ignition handles the execution, gas estimation, and transaction tracking.
// ignition/modules/MyToken.js
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules");
module.exports = buildModule("MyToken", (m) => {
const token = m.contract("MyToken", ["MyToken", "MTK", 1000000]);
return { token };
});Deploy to Sepolia with a single command:
npx hardhat ignition deploy ignition/modules/MyToken.js \
--network sepoliaAfter Deployment
Hardhat Ignition saves the deployed addresses in ignition/deployments/. You can view your contract on sepolia.etherscan.io by pasting the contract address. At this point, the code is live but not verified — users see raw bytecode, not Solidity source.
Etherscan Verification
Verification means publishing your Solidity source code on Etherscan so anyone can read and audit it. Without verification, users see only opaque bytecode and have no way to trust your contract. A green checkmark on Etherscan signals transparency.
Why Verify?
- Trust: Users can read the source code and confirm what the contract does.
- Interaction: Etherscan generates a Read/Write interface so users can call functions directly from the browser.
- Composability: Other developers can integrate with your contract by reading verified source and ABI.
- Professional standard: Every serious project verifies. Unverified contracts are a red flag.
Get an Etherscan API Key
Create a free account at etherscan.io and generate an API key in your profile settings. Add it to your .env and Hardhat config.
// hardhat.config.js — add etherscan section
module.exports = {
// ... networks config ...
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
},
};Verify with Hardhat
After deployment, run the verify command with the contract address and constructor arguments:
npx hardhat verify --network sepolia \
0xYourContractAddress \
"MyToken" "MTK" 1000000Constructor arguments must match exactly what was passed during deployment — same order, same types, same values. If they do not match, verification fails.
Complex Constructor Arguments
For contracts with complex arguments (arrays, structs, addresses), create an arguments file:
// arguments.js
module.exports = [
"MyToken",
"MTK",
1000000,
"0xOwnerAddress...",
];
// Then verify with:
// npx hardhat verify --network sepolia \
// --constructor-args arguments.js 0xContractAddressThe Green Checkmark
Once verified, your contract page on Etherscan shows a green checkmark on the Contract tab. The source code is displayed, and Read/Write panels let anyone interact with your contract. This is the standard for transparency in the Ethereum ecosystem.
Mainnet Deployment
Mainnet deployment is irreversible. Once deployed, the bytecode lives on Ethereum forever. There is no undo, no rollback, no delete. Every bug, every missing access control, every gas inefficiency is permanent. Mainnet demands preparation.
Pre-Deployment Checklist
- All tests pass: 100% of unit tests and integration tests. No skipped tests. Run
npx hardhat testandforge testone final time. - Security audit completed: At least one professional audit for contracts holding significant value. All critical and high findings resolved.
- Gas budget estimated: Calculate deployment gas cost using
hardhat-gas-reporter. Multiply by current gas price (check etherscan.io/gastracker). Have 2x the estimated cost ready. - Multi-sig ownership: Deploy from a hot wallet, then transfer ownership to a multi-sig (Gnosis Safe). Never leave a single private key as the owner of a production contract.
- Emergency mechanisms: Include a pause function (OpenZeppelin Pausable) for critical contracts. Test it on testnet.
- Documentation: NatSpec comments on every external function. Deployment addresses documented. User-facing documentation ready.
Pre-Deployment Simulation
Before spending real ETH, simulate the full deployment on a local mainnet fork. This catches issues with constructor arguments, gas limits, and contract interactions that only appear with real mainnet state.
# Fork mainnet locally
npx hardhat node --fork https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY
# In another terminal — deploy against the fork
npx hardhat ignition deploy ignition/modules/MyToken.js \
--network localhostA mainnet fork replicates the full Ethereum state locally. Your deployment runs against real token balances, real Uniswap pools, and real oracle prices — but without spending real ETH.
Deploy to Mainnet
Once your checklist is complete and the simulation succeeds, deploy for real. Add mainnet to your Hardhat config:
// hardhat.config.js
networks: {
mainnet: {
url: process.env.MAINNET_RPC_URL,
accounts: [process.env.DEPLOYER_PRIVATE_KEY],
gasPrice: "auto",
},
}
// Deploy
// npx hardhat ignition deploy ignition/modules/MyToken.js \
// --network mainnetPost-Deployment Steps
- Verify on Etherscan — same process as testnet, use
--network mainnet. - Transfer ownership — move admin/owner role to a multi-sig wallet (Gnosis Safe).
- Monitor — set up alerts with OpenZeppelin Defender or Forta for unusual activity.
- Announce — publish the verified contract address in your documentation, Discord, and GitHub.
Layer 2 Solutions
Ethereum mainnet is secure but expensive. A simple ERC-20 transfer costs $5-50 depending on network congestion. Complex DeFi transactions can exceed $100. Layer 2 (L2) solutions process transactions off the main chain while inheriting Ethereum's security, reducing costs by 10x to 100x.
Why Layer 2?
Ethereum processes ~15 transactions per second (TPS). During peak demand, gas prices spike and price out ordinary users. L2 chains batch hundreds of transactions into a single L1 transaction, amortizing the cost across all users. Result: same security, 100x cheaper.
Optimistic Rollups
Optimistic Rollups assume transactions are valid by default and only check them if someone submits a fraud proof during a challenge period (typically 7 days). This makes them fast and cheap, with full EVM compatibility.
ZK Rollups
ZK Rollups generate a cryptographic validity proof (zero-knowledge proof) for every batch of transactions. The L1 contract verifies the proof mathematically — no challenge period needed. Finality is faster, but generating proofs is computationally expensive.
Optimistic vs ZK — Comparison
For most projects today, Optimistic Rollups (Arbitrum, Base, Optimism) offer the best balance of EVM compatibility, tooling support, and cost savings. ZK Rollups are the future but still maturing in terms of developer experience.
Deploying to L2
The best part of L2 deployment: your Solidity code does not change. The same contracts, the same tests, the same tooling — you only change the RPC URL and chain ID. If it works on Sepolia, it works on Arbitrum.
Configure an L2 Network
Add Arbitrum One and Base to your Hardhat config. Each chain needs an RPC URL and chain ID:
// hardhat.config.js
networks: {
arbitrum: {
url: "https://arb1.arbitrum.io/rpc",
chainId: 42161,
accounts: [process.env.DEPLOYER_PRIVATE_KEY],
},
base: {
url: "https://mainnet.base.org",
chainId: 8453,
accounts: [process.env.DEPLOYER_PRIVATE_KEY],
},
}Bridge ETH to L2
Before deploying on an L2, you need ETH on that chain to pay for gas. Use the official bridge for each network:
- Arbitrum Bridge: bridge.arbitrum.io — deposit ETH from mainnet to Arbitrum. Takes ~10 minutes.
- Base Bridge: bridge.base.org — deposit ETH from mainnet to Base. Takes ~10 minutes.
- Third-party bridges: Stargate, Hop, Across — faster and sometimes cheaper, but add trust assumptions.
Deploy to Arbitrum
# Same command, different network flag
npx hardhat ignition deploy ignition/modules/MyToken.js \
--network arbitrum
# Verify on Arbiscan
npx hardhat verify --network arbitrum \
0xYourContractAddress "MyToken" "MTK" 1000000L2 Block Explorers
Each L2 has its own Etherscan-style explorer. Add their API keys to your Hardhat config for verification:
- Arbitrum: arbiscan.io
- Base: basescan.org
- Optimism: optimistic.etherscan.io
Gas Cost Comparison
The same ERC-20 transfer that costs $5-50 on mainnet costs pennies on L2:
L2 gas costs depend on L1 gas prices (L2 batches are posted to L1). When mainnet is congested, L2 costs also rise — but remain 10-100x cheaper.
Multi-Chain Strategy
Many projects deploy the same contract on multiple chains to reach different user bases. A token might live on Ethereum mainnet, Arbitrum, Base, and Polygon simultaneously. This requires careful planning around addresses, bridging, and state consistency.
When to Go Multi-Chain
- User reach: Different users live on different chains. DeFi power users prefer Arbitrum; retail users prefer Base (Coinbase integration).
- Cost optimization: Deploy governance on mainnet (high security), daily operations on L2 (low cost).
- Ecosystem incentives: L2 chains offer grants, gas subsidies, and ecosystem funds to attract projects.
When to Stay Single-Chain
Multi-chain adds complexity. Stay on one chain if: your user base is concentrated on one network, your contract state must be unified (e.g., a single lending pool), or you lack the engineering resources to maintain multiple deployments.
CREATE2: Deterministic Addresses
By default, contract addresses depend on the deployer's nonce — deploy on two chains, get two different addresses. CREATE2 generates a deterministic address based on the deployer address, a salt, and the bytecode hash. Same inputs = same address on every chain.
// CREATE2 address formula:
// address = keccak256(0xff ++ deployer ++ salt ++ keccak256(bytecode))
// Using a CREATE2 factory (e.g., Arachnid's deterministic deployer)
// Deploy the same bytecode with the same salt on every chain
// → identical contract address everywhereCREATE2 is widely used by protocols like Uniswap (pair addresses), Safe (wallet addresses), and OpenZeppelin. It simplifies frontend configuration — one address works across all chains.
Cross-Chain Bridges
If your contract exists on multiple chains, users need to move tokens between them. Bridges lock tokens on one chain and mint equivalent tokens on the other. Key bridge protocols:
- Native bridges: Each L2 has its own official bridge (Arbitrum Bridge, Base Bridge). Most secure, but slow (7-day withdrawal for Optimistic Rollups).
- Liquidity bridges: Stargate, Hop, Across — use liquidity pools for instant transfers. Faster, but add trust assumptions.
- Messaging protocols: LayerZero, Chainlink CCIP, Axelar — send arbitrary messages and data cross-chain. Required for cross-chain governance and state sync.
Bridges are the most attacked infrastructure in crypto. Wormhole ($326M), Ronin ($625M), Nomad ($190M) — all bridge hacks. Use battle-tested bridges and minimize bridged value.
Exercise & Self-Check
Exercises
- Deploy to Sepolia: Take your ERC-20 token from Module 9 (or any contract from Part 2). Configure Hardhat for Sepolia, get test ETH from a faucet, and deploy using Hardhat Ignition. Share the contract address on Sepolia Etherscan.
- Verify on Etherscan: After deploying to Sepolia, get an Etherscan API key and verify your contract. Confirm the green checkmark appears and you can interact via the Read/Write tabs.
- Simulate a mainnet deploy: Fork Ethereum mainnet locally using
npx hardhat node --fork. Deploy your contract against the fork. Compare gas costs between the fork and your Sepolia deployment. - Deploy to an L2 testnet: Configure Hardhat for Arbitrum Sepolia (testnet) or Base Sepolia. Bridge test ETH to the L2 testnet and deploy the same contract. Compare gas costs with L1 Sepolia.
- Multi-chain analysis: Research a real protocol deployed on multiple chains (e.g., Uniswap, Aave, or Chainlink). List all chains they support. Do they use the same contract address on every chain? How do they handle cross-chain token transfers?
Self-Check Questions
- Why should you never commit a
.envfile containing a private key to a Git repository? What would happen if you did? - Explain the difference between contract verification and contract deployment. Why is verification important even though the contract already works without it?
- What is the 7-day challenge period in Optimistic Rollups? Why does it exist, and what happens if someone submits a fraud proof?
- Why does CREATE2 produce the same address on every chain? What are the inputs to the address formula?
- You want to deploy a DeFi lending protocol. Would you choose Optimistic Rollups or ZK Rollups today, and why? What factors would change your decision in 2 years?
Part 2 Summary
Congratulations — you have completed Part 2: Smart Contract Development. You can now write Solidity contracts (Module 8), create tokens and standards (Module 9), implement advanced patterns (Module 10), test with Hardhat and Foundry (Module 11), build DApp frontends (Module 12), audit for security vulnerabilities (Module 13), and deploy to testnets, mainnet, and Layer 2 chains (Module 14). You have every tool needed to ship production smart contracts.