EVMTools

How to Deploy a Smart Contract

Step-by-step guide to deploying smart contracts using Remix, Hardhat, and Foundry. Covers testnet deployment, Etherscan verification, and mainnet considerations.

Deploying a smart contract to Ethereum is the process of compiling your Solidity code into bytecode and broadcasting a special transaction that stores that bytecode on the blockchain. Once deployed, your contract gets a permanent address and can be called by anyone. This guide walks you through every step — from writing the contract to verifying it on Etherscan — using the three most popular development tools: Remix, Hardhat, and Foundry.

Prerequisites

Before deploying, make sure you have the following ready:

  • MetaMask or another wallet with a private key you control. Never use your main wallet for development — create a dedicated development wallet.
  • Test ETH for testnet deployment. Get free Sepolia ETH from faucets like sepoliafaucet.com or the Alchemy Sepolia Faucet.
  • Node.js 18+ installed (required for Hardhat). Not needed for Remix or Foundry.
  • An RPC endpoint from providers like Alchemy, Infura, or QuickNode. These let your tools communicate with the Ethereum network.
  • An Etherscan API key (free) for contract verification.

Deployment Tools Comparison

There are three major tools for deploying Solidity contracts. Each has different strengths:

FeatureRemixHardhatFoundry
SetupBrowser-based, no installnpm installcurl install (Rust-based)
LanguageGUI + SolidityJavaScript / TypeScriptSolidity + shell commands
Compile speedModerateModerateVery fast
TestingLimitedMocha/Chai (JS)Solidity tests + fuzzing
Best forBeginners, quick testsFull-stack dAppsProtocol development
VerificationPluginhardhat-verify pluginforge verify-contract

Step 1: Write the Solidity Contract

Let us start with a simple but practical contract that demonstrates key Solidity features. This is a basic storage contract with an owner and event emissions:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @title SimpleStorage
/// @notice A basic contract for storing and retrieving a value
contract SimpleStorage {
    address public owner;
    uint256 private storedValue;

    event ValueChanged(address indexed updater, uint256 newValue);

    modifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }

    constructor(uint256 initialValue) {
        owner = msg.sender;
        storedValue = initialValue;
        emit ValueChanged(msg.sender, initialValue);
    }

    function set(uint256 newValue) external onlyOwner {
        storedValue = newValue;
        emit ValueChanged(msg.sender, newValue);
    }

    function get() external view returns (uint256) {
        return storedValue;
    }
}

This contract includes a constructor argument (initialValue), an access control modifier, events, and both write and read functions — covering the most common patterns you will encounter in real contracts.

Step 2a: Deploy with Hardhat

Hardhat is the most popular Ethereum development framework. Initialize a project and install dependencies:

mkdir my-contract && cd my-contract
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npx hardhat init
# Choose "Create a TypeScript project"

Save your contract as contracts/SimpleStorage.sol. Then configure your network in hardhat.config.ts:

import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";

const config: HardhatUserConfig = {
  solidity: "0.8.20",
  networks: {
    sepolia: {
      url: process.env.SEPOLIA_RPC_URL || "",
      accounts: process.env.PRIVATE_KEY
        ? [process.env.PRIVATE_KEY]
        : [],
    },
  },
  etherscan: {
    apiKey: process.env.ETHERSCAN_API_KEY || "",
  },
};

export default config;

Create a deployment script at scripts/deploy.ts:

import { ethers } from "hardhat";

async function main() {
  const initialValue = 42;
  const factory = await ethers.getContractFactory("SimpleStorage");
  const contract = await factory.deploy(initialValue);
  await contract.waitForDeployment();

  const address = await contract.getAddress();
  console.log("SimpleStorage deployed to:", address);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Compile and deploy:

# Compile
npx hardhat compile

# Deploy to Sepolia testnet
npx hardhat run scripts/deploy.ts --network sepolia

Step 2b: Deploy with Foundry

Foundry is a blazing-fast Rust-based toolkit favored by protocol developers. Install it and initialize a project:

# Install Foundry
curl -L https://foundry.paradigm.xyz | bash
foundryup

# Create a new project
forge init my-contract
cd my-contract

Save your contract in src/SimpleStorage.sol. Then compile and deploy with a single command:

# Compile
forge build

# Deploy to Sepolia testnet
forge create src/SimpleStorage.sol:SimpleStorage \
  --rpc-url $SEPOLIA_RPC_URL \
  --private-key $PRIVATE_KEY \
  --constructor-args 42

# Output:
# Deployer: 0xYourAddress
# Deployed to: 0xContractAddress
# Transaction hash: 0xTxHash

Foundry also supports deployment scripts written in Solidity for more complex deployment workflows:

// 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 {
        vm.startBroadcast();
        SimpleStorage store = new SimpleStorage(42);
        console.log("Deployed to:", address(store));
        vm.stopBroadcast();
    }
}

// Run with:
// forge script script/Deploy.s.sol --rpc-url $SEPOLIA_RPC_URL
//   --private-key $PRIVATE_KEY --broadcast

Step 3: Verify on Etherscan

Verification publishes your source code on Etherscan, letting anyone read and audit it. This builds trust and lets users interact with your contract directly through Etherscan's UI.

Verify with Hardhat

# Verify with constructor arguments
npx hardhat verify --network sepolia \
  0xYourContractAddress 42

Verify with Foundry

forge verify-contract 0xYourContractAddress \
  src/SimpleStorage.sol:SimpleStorage \
  --chain sepolia \
  --etherscan-api-key $ETHERSCAN_API_KEY \
  --constructor-args $(cast abi-encode "constructor(uint256)" 42)

After verification, navigate to your contract address on Etherscan. You will see a green checkmark on the "Contract" tab, and the "Read Contract" and "Write Contract" tabs become available for direct interaction.

Step 4: Deploying to Mainnet

Once your contract works correctly on the testnet, you can deploy to mainnet. This involves real money, so take extra precautions:

  • Security audit: For contracts handling user funds, get a professional audit from firms like OpenZeppelin, Trail of Bits, or Cantina. At minimum, use automated tools like Slither and Mythril.
  • Gas estimation: Use eth_estimateGas or Hardhat's gas reporter to estimate deployment costs. Check current gas prices and deploy during low-traffic periods (typically weekends or early UTC mornings) to save money.
  • Private key security: Never hardcode private keys. Use environment variables, hardware wallets (via Ledger/Trezor integration), or a secure key management service.
  • Contract size limit: Ethereum contracts cannot exceed 24,576 bytes (24 KB) of deployed bytecode per EIP-170. If your contract is too large, split it into libraries or use the diamond pattern (EIP-2535).
  • Immutability awareness: Once deployed, you cannot change the bytecode. If you need upgradability, implement a proxy pattern before deployment.

Warning: Deploying to mainnet costs real ETH. A simple contract costs approximately $50–$500 depending on gas prices and contract complexity. Always deploy to a testnet first and thoroughly test every function.

Quick Deploy with Remix (Browser-Based)

Remix is the fastest way to deploy a contract without installing anything. It is ideal for beginners and quick prototyping:

  1. Navigate to remix.ethereum.org in your browser.
  2. Create a new file in the "contracts" folder and paste your Solidity code.
  3. Open the "Solidity Compiler" tab (left sidebar) and click "Compile." Ensure the compiler version matches your pragma statement.
  4. Switch to the "Deploy & Run" tab. Under "Environment," select "Injected Provider - MetaMask" to connect your wallet.
  5. Enter constructor arguments (e.g., 42), then click "Deploy."
  6. Confirm the transaction in MetaMask. Once mined, your contract address appears in the "Deployed Contracts" section.

Pre-Deployment Checklist

Before deploying to any network, run through this checklist:

  • All tests pass with 100% coverage of critical paths
  • No compiler warnings (treat warnings as errors)
  • Reentrancy guards on all external-calling functions
  • Access control on sensitive functions (onlyOwner, etc.)
  • Events emitted for all state changes
  • Constructor arguments double-checked
  • Gas optimizations reviewed (storage packing, calldata vs memory)
  • Static analysis run (Slither: slither .)
  • Testnet deployment verified and tested end-to-end
  • Private key stored securely (not in code or git)

Frequently Asked Questions

How much does it cost to deploy a smart contract?

Deployment cost depends on the contract size, the network, and current gas prices. A simple contract on Ethereum mainnet typically costs $50–$500 in gas fees. On L2 networks like Arbitrum or Base, the same deployment costs $0.50–$5. On testnets like Sepolia, deployment is free since test ETH has no real value.

What is the difference between Hardhat and Foundry?

Hardhat uses JavaScript/TypeScript for scripts and tests, making it accessible to web developers. Foundry uses Solidity for tests and Rust-based tooling, offering faster compilation and execution. Hardhat has a larger plugin ecosystem, while Foundry is preferred for gas optimization and fuzz testing. Both are production-grade.

Do I need to verify my smart contract on Etherscan?

Verification is not required but strongly recommended. Verified contracts show their source code on Etherscan, allowing users to read the code and interact with functions directly. Unverified contracts display only bytecode, which reduces trust and makes integration harder.

Should I deploy to a testnet first?

Always deploy to a testnet first. Testnets like Sepolia replicate the Ethereum environment with free test ETH, letting you verify your contract works correctly before spending real money. Test all functions, edge cases, and integrations on the testnet before deploying to mainnet.

Can I update a smart contract after deployment?

Standard smart contracts are immutable once deployed. However, you can use proxy patterns (like UUPS or Transparent Proxy) that separate the contract logic from its storage, allowing you to deploy updated logic while preserving the state and address. This adds complexity and should be planned from the start.

Debug Your Deployed Contracts

After deploying, use our Calldata Decoder to inspect transaction data, the ABI Encoder / Decoder to encode function calls, or the Keccak256 Hash Generator to compute function selectors.

Related Guides and Tools