The Deployment That Cost Me $847 in Gas
I deployed my first smart contract to Ethereum mainnet at 11 PM on a Friday. Gas was 180 gwei. The contract had a bug in the constructor. I couldn't fix it without redeploying.
That mistake cost me three sleepless nights and $847 in wasted gas fees.
What you'll learn:
- Test deployment on Sepolia with real-world scenarios
- Audit your contract before mainnet (free tools)
- Deploy to mainnet with gas optimization
- Verify contracts on Etherscan automatically
Time needed: 45 minutes | Difficulty: Intermediate
Why "Just Deploy It" Failed
What I tried:
- Deployed directly to mainnet after local testing - Missed network-specific issues with block timestamps
- Used Remix IDE for quick deployment - No version control, lost deployment scripts
- Skipped testnet because "it's just a simple contract" - Constructor bug cost $847
Time wasted: 16 hours fixing + $1,200 in total gas fees
My Setup
- OS: macOS Ventura 13.4
- Node: 20.3.1
- Hardhat: 2.19.1
- Solidity: 0.8.20
- Network: Sepolia testnet â†' Ethereum mainnet
My Hardhat project showing network configs and deployment scripts
Tip: "I keep separate .env files for testnet and mainnet. Never mix them."
Step-by-Step Solution
Step 1: Set Up Your Hardhat Project
What this does: Creates isolated network configurations so you can't accidentally deploy to mainnet
// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();
module.exports = {
solidity: {
version: "0.8.20",
settings: {
optimizer: {
enabled: true,
runs: 200 // Personal note: Use 200 for balanced gas costs
}
}
},
networks: {
sepolia: {
url: process.env.SEPOLIA_RPC_URL,
accounts: [process.env.TESTNET_PRIVATE_KEY],
chainId: 11155111
},
mainnet: {
url: process.env.MAINNET_RPC_URL,
accounts: [process.env.MAINNET_PRIVATE_KEY],
chainId: 1,
gasPrice: "auto" // Watch out: Set max fee or you'll overpay
}
},
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY
}
};
Expected output: hardhat.config.js with separate network configs
My Terminal after running npx hardhat compile - yours should show 0 errors
Tip: "Use Alchemy or Infura for RPC URLs. Free tier works fine for testing."
Troubleshooting:
- Error: "Invalid API Key": Check your
.envfile hasSEPOLIA_RPC_URL="https://eth-sepolia..." - Error: "Insufficient funds": Get Sepolia ETH from faucet: sepolia-faucet.pk910.de
Step 2: Deploy and Test on Sepolia
What this does: Catches constructor bugs and gas estimation issues before mainnet
// scripts/deploy.js
const hre = require("hardhat");
async function main() {
console.log("Deploying to:", hre.network.name);
// Personal note: Always log network name to avoid mistakes
if (hre.network.name === "mainnet") {
console.log("⚠️ MAINNET DEPLOYMENT - Confirm gas price!");
await new Promise(resolve => setTimeout(resolve, 5000));
}
const Contract = await hre.ethers.getContractFactory("MyContract");
// Watch out: Constructor args must match exactly
const contract = await Contract.deploy(
"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", // initialOwner
1000000 // initialSupply
);
await contract.waitForDeployment();
const address = await contract.getAddress();
console.log("Contract deployed to:", address);
console.log("Transaction hash:", contract.deploymentTransaction().hash);
// Verify on Etherscan automatically
if (hre.network.name !== "hardhat") {
console.log("Waiting 60s for Etherscan indexing...");
await new Promise(resolve => setTimeout(resolve, 60000));
await hre.run("verify:verify", {
address: address,
constructorArguments: [
"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
1000000
]
});
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Run deployment:
npx hardhat run scripts/deploy.js --network sepolia
Expected output: Contract address + verified Etherscan link
Tip: "I deploy to Sepolia 3-5 times, testing different scenarios. Gas is free, mistakes aren't."
Troubleshooting:
- Error: "Nonce too high": Reset MetaMask: Settings â†' Advanced â†' Clear activity tab data
- Verification failed: Wait 2 minutes and run:
npx hardhat verify --network sepolia <ADDRESS> <CONSTRUCTOR_ARGS>
Step 3: Run Security Checks
What this does: Catches common vulnerabilities before you spend real money
# Install Slither (free security analyzer)
pip3 install slither-analyzer
# Run security scan
slither . --exclude-dependencies
# Check for common issues
slither . --detect reentrancy-eth,arbitrary-send-eth
My security checklist:
- No reentrancy vulnerabilities
- Access control on critical functions
- Integer overflow protection (Solidity 0.8+ has this built-in)
- No hardcoded addresses
- Events for all state changes
Slither scan showing 0 high-severity issues - this is what you want
Tip: "I also test with Foundry's fuzz testing. Found 2 edge cases Slither missed."
Step 4: Deploy to Mainnet
What this does: The moment of truth - deploying with optimized gas
# Check current gas prices (aim for <50 gwei)
curl -s https://etherscan.io/gastracker | grep -o "Average: [0-9]* Gwei"
# Deploy when gas is low (early morning US time)
npx hardhat run scripts/deploy.js --network mainnet
My deployment timing strategy:
- ✅ Saturday/Sunday 2-6 AM EST (gas often <30 gwei)
- ✅ Check https://etherscan.io/gastracker before deploying
- ⌠Thursday/Friday evenings (gas spikes to 100+ gwei)
Expected output:
Deploying to: mainnet
⚠️ MAINNET DEPLOYMENT - Confirm gas price!
Contract deployed to: 0x1234...5678
Transaction hash: 0xabcd...ef01
Gas used: 1,247,893
Total cost: 0.0374 ETH ($67.23)
Real deployment costs: Sepolia (free) vs Mainnet at different gas prices
Troubleshooting:
- Transaction stuck: Check Etherscan. If pending 10+ mins, use "Speed Up" in MetaMask
- Gas too high: Wait. I've seen gas drop from 120 gwei to 35 gwei in 4 hours
Testing Results
How I tested:
- Deployed to Sepolia 5 times with different scenarios
- Called all functions with edge case inputs
- Tested with multiple wallets (owner, user, attacker)
- Simulated high gas conditions
Measured results:
- Deployment gas: 1,247,893 (optimizer helped)
- Function calls: avg 47,000 gas
- Total testing cost: $0 (Sepolia) vs estimated $2,400 without testing
Etherscan showing verified contract with green checkmark - took 8 minutes total
Key Takeaways
- Deploy to testnet 3+ times: Every constructor bug you find saves $200+ in gas
- Watch gas prices like a hawk: Deploying at 35 gwei vs 120 gwei saved me $180
- Automate verification: Hardhat's verify plugin saves 15 minutes per deployment
- Keep separate wallets: I use different MetaMask accounts for testnet vs mainnet
Limitations: This guide covers basic deployments. Proxy patterns and upgradeable contracts need different approaches.
Your Next Steps
- Deploy your contract to Sepolia right now (takes 5 minutes)
- Run Slither scan to catch vulnerabilities
- Set up gas price alerts on Etherscan
- Deploy to mainnet when gas <50 gwei
Level up:
- Beginners: Start with "Writing Your First Smart Contract in Solidity"
- Advanced: Learn "Implementing Upgradeable Contracts with UUPS Pattern"
Tools I use:
- Hardhat: Best for TypeScript projects - hardhat.org
- Slither: Free security scanner - github.com/crytic/slither
- Sepolia Faucet: Free testnet ETH - sepolia-faucet.pk910.de
- Gas Tracker: Real-time prices - etherscan.io/gastracker