# How to Deploy a Smart Contract on Ethereum — Step by Step
Deploying a smart contract is the moment your code becomes immutable reality on the blockchain. It's permanent, public, and unstoppable.
In this guide, we'll walk through three deployment methods—Remix (browser-based), Hardhat (JavaScript), and Foundry (Rust)—from testnet to mainnet, including verification on Etherscan.
Prerequisites
Before deploying, you need:
- Testnet: Get free Sepolia ETH from faucet
- Mainnet: Real ETH from an exchange
Cost estimate:
- Testnet: Free (faucet ETH)
- Mainnet: ~$5-50 depending on contract size and gas price
The Smart Contract We'll Deploy
We'll use a simple but complete example:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
contract SimpleStorage {
uint256 public storedValue;
address public owner;
event ValueUpdated(uint256 newValue, address updatedBy);
constructor(uint256 _initialValue) {
storedValue = _initialValue;
owner = msg.sender;
}
function setValue(uint256 _value) public {
require(msg.sender == owner, "Only owner can update");
storedValue = _value;
emit ValueUpdated(_value, msg.sender);
}
function getValue() public view returns (uint256) {
return storedValue;
}
}
Simple, but includes:
- Constructor with arguments
- State variables
- Access control
- Events
- View function
Method 1: Remix (Quickest, No Setup)
Best for: Beginners, quick tests, learning.
Step 1: Write the Contract
SimpleStorage.sol in the contracts folderStep 2: Compile
0.8.20Step 3: Connect Wallet
Step 4: Deploy
SimpleStorage42 (initial value)Step 5: Verify Deployment
Step 6: Interact
In Remix, under "Deployed Contracts":
- Click
getValue: Returns42
- Click
setValue, enter100, transact
- Click
getValueagain: Returns100
Pros: No installation, visual, instant.
Cons: Not scriptable, hard to reproduce, no version control.
---
Method 2: Hardhat (JavaScript Ecosystem)
Best for: Web3 teams, JavaScript developers, existing Node.js projects.
Step 1: Setup Project
mkdir simple-storage-hardhat
cd simple-storage-hardhat
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npx hardhat init
# Select: Create a JavaScript project
Step 2: Write Contract
Create contracts/SimpleStorage.sol (same code as above).
Step 3: Configure Network
Edit hardhat.config.js:
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();
module.exports = {
solidity: "0.8.20",
networks: {
sepolia: {
url: process.env.SEPOLIA_RPC_URL,
accounts: [process.env.PRIVATE_KEY]
}
},
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY
}
};
Create .env:
SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/YOUR_INFURA_KEY
PRIVATE_KEY=your_wallet_private_key_here
ETHERSCAN_API_KEY=your_etherscan_api_key
⚠️ Never commit .env to git! Add to .gitignore.
Step 4: Write Deploy Script
Create scripts/deploy.js:
const hre = require("hardhat");
async function main() {
const initialValue = 42;
console.log("Deploying SimpleStorage with initial value:", initialValue);
const SimpleStorage = await hre.ethers.getContractFactory("SimpleStorage");
const simpleStorage = await SimpleStorage.deploy(initialValue);
await simpleStorage.waitForDeployment();
const address = await simpleStorage.getAddress();
console.log("SimpleStorage deployed to:", address);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Step 5: Deploy to Sepolia
npx hardhat run scripts/deploy.js --network sepolia
Output:
Deploying SimpleStorage with initial value: 42
SimpleStorage deployed to: 0x1234567890AbcdEF1234567890aBcdef12345678
Step 6: Verify on Etherscan
npx hardhat verify --network sepolia 0x1234...5678 42
(Replace address with your deployed address, 42 is constructor arg)
Pros: Industry standard, great tooling, integrates with npm.
Cons: Slower than Foundry, JavaScript dependency hell.
---
Method 3: Foundry (Fastest, Most Powerful)
Best for: Advanced developers, high-performance needs, Solidity-native testing.
Step 1: Install Foundry
curl -L https://foundry.paradigm.xyz | bash
foundryup
Step 2: Create Project
forge init simple-storage-foundry
cd simple-storage-foundry
Step 3: Write Contract
Replace src/Counter.sol with src/SimpleStorage.sol (same code).
Step 4: Write Test
Create test/SimpleStorage.t.sol:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "forge-std/Test.sol";
import "../src/SimpleStorage.sol";
contract SimpleStorageTest is Test {
SimpleStorage public store;
function setUp() public {
store = new SimpleStorage(42);
}
function testInitialValue() public {
assertEq(store.getValue(), 42);
}
function testSetValue() public {
store.setValue(100);
assertEq(store.getValue(), 100);
}
function testOnlyOwner() public {
vm.prank(address(0x123)); // Simulate different caller
vm.expectRevert("Only owner can update");
store.setValue(999);
}
}
Run tests:
forge test -vvv
Step 5: Configure .env
SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/YOUR_KEY
PRIVATE_KEY=your_private_key
ETHERSCAN_API_KEY=your_api_key
Step 6: Deploy
source .env
forge create src/SimpleStorage.sol:SimpleStorage \
--rpc-url $SEPOLIA_RPC_URL \
--private-key $PRIVATE_KEY \
--constructor-args 42 \
--verify \
--etherscan-api-key $ETHERSCAN_API_KEY
Or use a script (script/Deploy.s.sol):
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "forge-std/Script.sol";
import "../src/SimpleStorage.sol";
contract DeployScript is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
SimpleStorage store = new SimpleStorage(42);
console.log("Deployed at:", address(store));
vm.stopBroadcast();
}
}
Deploy with script:
forge script script/Deploy.s.sol:DeployScript \
--rpc-url $SEPOLIA_RPC_URL \
--broadcast \
--verify \
-vvvv
Pros: Blazing fast, Solidity-native tests, best tooling.
Cons: Rust learning curve for contributions.
---
Deploying to Mainnet
⚠️ WARNING: Mainnet is permanent and expensive. Triple-check everything.
Pre-Mainnet Checklist
- [ ] All tests passing
- [ ] Security audit (for high-value contracts)
- [ ] Deployed and tested on Sepolia
- [ ] Verified on Sepolia Etherscan
- [ ] Reviewed all constructor arguments
- [ ] Checked gas estimates (
forge inspect SimpleStorage gas)
- [ ] Funded deployer wallet with ETH (estimate + 20% buffer)
- [ ] Considered multi-sig ownership for upgrades
- [ ] Backup of private key (encrypted, offline)
Mainnet Deployment (Foundry)
# Setup mainnet RPC
MAINNET_RPC_URL=https://mainnet.infura.io/v3/YOUR_KEY
# Estimate gas cost
forge script script/Deploy.s.sol --rpc-url $MAINNET_RPC_URL
# If cost is acceptable, deploy
forge script script/Deploy.s.sol \
--rpc-url $MAINNET_RPC_URL \
--broadcast \
--verify \
-vvvv
Gas optimization before mainnet:
forge test --gas-report
This shows gas usage per function—optimize expensive ones.
---
Common Deployment Mistakes
1. Wrong Network
Always check MetaMask network indicator. Mainnet is red, testnets are colored.
2. Insufficient Gas
If transaction fails, you still pay gas. Set reasonable gas limit:
# Estimate first
cast estimate <contract_bytecode> --rpc-url $RPC_URL
3. Constructor Arguments Encoding
# Wrong
--constructor-args 42
# Right (for complex types)
--constructor-args $(cast abi-encode "constructor(uint256)" 42)
4. Private Key Exposure
- Never hardcode private keys
- Use
.envand.gitignore
- For production, use hardware wallets or Gnosis Safe
5. Verification Failure
If Etherscan verification fails:
# Manual verification
forge verify-contract \
<CONTRACT_ADDRESS> \
src/SimpleStorage.sol:SimpleStorage \
--chain sepolia \
--constructor-args $(cast abi-encode "constructor(uint256)" 42) \
--etherscan-api-key $ETHERSCAN_API_KEY
---
After Deployment: Next Steps
Get Contract ABI
# Foundry
forge inspect SimpleStorage abi > SimpleStorage.json
# Hardhat
npx hardhat compile
# ABI in artifacts/contracts/SimpleStorage.sol/SimpleStorage.json
---
Testnets vs Mainnet
| Aspect | Testnet (Sepolia) | Mainnet |
|--------|-------------------|---------|
| Cost | Free (faucet) | Real ETH (~$5-50/deploy) |
| Speed | ~15 sec blocks | ~12 sec blocks |
| Risk | Zero (fake money) | High (immutable) |
| Use case | Development, testing | Production |
Always deploy to testnet first!
---
Deployment Costs (Mainnet)
Example costs at 30 gwei gas price:
| Contract Size | Gas Used | Cost (ETH) | Cost (USD @ $2000/ETH) |
|---------------|----------|------------|------------------------|
| Tiny (SimpleStorage) | ~200k | 0.006 | $12 |
| Medium (ERC-20) | ~800k | 0.024 | $48 |
| Large (NFT + marketplace) | ~2M | 0.06 | $120 |
Gas optimization = direct cost savings.
---
Conclusion
You've now deployed a smart contract using three different methods:
- Remix: Quick, visual, beginner-friendly
- Hardhat: JavaScript ecosystem, great for web3 teams
- Foundry: Fastest, most powerful, Solidity-native
Recommendations:
- Learning: Use Remix
- Professional projects: Use Foundry (or Hardhat if JS-heavy)
- Mainnet: Always test on Sepolia first, get an audit for valuable contracts
Next steps:
- Deploy an ERC-20 token
- Build a frontend with ethers.js or wagmi
- Explore upgradeable contracts (proxies)
Your code is now on the blockchain—permanent, transparent, unstoppable. Welcome to Web3. 🚀