An Ethereum smart contract is a self-executing program that encodes agreement terms directly in code and runs on the Ethereum blockchain without requiring a trusted intermediary. For developers shipping their first production contract, the path from a Solidity file on disk to a verified address on mainnet involves a specific toolchain, a disciplined test pass, and a non-negotiable security review. Skip any of those steps and a bug becomes permanent the moment the deployment transaction is mined. Adjacent deployment contexts are covered in AI in financial services and VR training for enterprises.
What Ethereum Smart Contracts Are and How They Execute
Ethereum smart contracts compile from Solidity (or Vyper) source into EVM bytecode, which is stored at a deterministic contract address derived from the deployer account and nonce. When a transaction calls one of the contract's functions, every node in the network runs the same bytecode against the same input through the Ethereum Virtual Machine, reaches the same result, and updates the global state trie. A proof-of-stake validator selected for the slot bundles the transaction into a block, and finality follows roughly two epochs later under Ethereum's Gasper consensus. The proof-of-stake validator set replaced miners after The Merge in September 2022, which cut network energy consumption by an estimated 99.95 percent according to the Ethereum Foundation. State changes persist forever once finalised, which is why on-chain logic carries a different risk profile than ordinary server code: no operator can roll back a buggy write, patch a contract in place, or selectively apply the rules to one counterparty.
What Is Blockchain: A Beginner's Guide covers the underlying ledger model. The Ethereum Foundation's smart contracts reference defines the execution model in full.
The EVM Execution Model
The Ethereum Virtual Machine is a stack-based virtual machine with a 256-bit word size. Every opcode has a fixed gas cost defined in the Ethereum Yellow Paper, and SSTORE (writing a 32-byte word to contract storage) is the most expensive common operation at 20,000 gas for a fresh slot. Contract storage, account balances, and nonces live in a Merkle Patricia trie, which lets light clients verify state inclusion without holding the full database. Determinism is the design point: identical input plus identical state must produce identical output on every node, otherwise consensus breaks.
Immutability and the Upgrade Constraint
Once deployed, the bytecode at a contract address cannot be modified. This is a censorship-resistance guarantee for users and a permanent constraint for developers. Fixing a bug requires deploying a new contract and migrating state, or wiring the original deployment behind a proxy upgrade pattern from day one. The trade-off is governance: a proxy that can swap implementations also introduces a privileged role capable of changing the rules. The security section returns to this design choice.
Setting Up Your Ethereum Development Environment

Ethereum smart contracts begin with one of two practical entry points. Remix IDE runs entirely in the browser, compiles Solidity in-process, and deploys to an in-memory EVM with a single button click; it is the fastest path for a first contract and remains the default teaching tool. For anything destined for mainnet, the Hardhat development environment is the production-grade choice: a Node.js framework with scripted deployments, a local mainnet fork, plugin support for Etherscan verification, and a Mocha-based test runner. The Hardhat development environment also integrates cleanly with hardware wallets and CI pipelines, which matters once a Solidity smart contract starts handling real value. Foundry, written in Rust, is a credible alternative favored by security researchers for its fuzzing speed, though Hardhat retains the larger documentation surface.
Truffle reached end-of-life in late 2023 and should not be used for new work. The official Hardhat documentation is the canonical setup reference. The Remix IDE team publishes parallel guides for browser-only workflows when a local Node.js install is impractical.
- Install Node.js 18 or later from nodejs.org.
- Initialise a new project with
npm init -y. - Install Hardhat as a dev dependency:
npm install --save-dev hardhat. - Run
npx hardhat initand select the TypeScript project template. - Install the OpenZeppelin library:
npm install @openzeppelin/contracts. - Create a
.envfile with placeholders forINFURA_API_KEYandDEPLOYER_PRIVATE_KEY, and add it to.gitignorebefore the first commit.
Never commit a deployer private key, even to a private repository. Use dotenv for local development and a managed secret store (AWS Secrets Manager, GCP Secret Manager, or a hardware wallet via Frame) for any production signing.
Writing Your First Solidity Smart Contract
A Solidity smart contract starts with two header lines: an SPDX license identifier and a pragma directive pinning the compiler version. Pinning matters because the Solidity team ships breaking changes across minor versions, and the bytecode the Ethereum Virtual Machine ultimately runs depends on the exact compiler used. The most instructive first Solidity smart contract is an ERC-20 token built on top of the OpenZeppelin library, which provides audited base implementations of every common token standard. The OpenZeppelin library distributes these contracts under MIT license through npm. Inheriting from @openzeppelin/contracts/token/ERC20/ERC20.sol gives you transfer, approval, and allowance behavior for free; the constructor mints an initial supply to the deployer.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract TechshookedToken is ERC20 {
constructor(uint256 initialSupply) ERC20("Techshooked", "TSK") {
_mint(msg.sender, initialSupply);
}
}For a comparison of how the same contract would execute on a different chain, see Ethereum vs Solana: A Comprehensive Comparison. The OpenZeppelin Contracts documentation covers every base contract and the security assumptions baked into each.
Contract Structure and Visibility Modifiers
State variables live in contract storage and cost gas to write; local variables live in memory or on the stack and cost almost nothing. Four visibility modifiers control function access: public and external are callable by any address, while internal and private restrict calls to the contract itself and its inheritors. The view and pure modifiers signal that a function reads (or does not touch) state respectively; both are free when called off-chain through a JSON-RPC eth_call, since no transaction is mined. Use them aggressively for read accessors.
Events and the ABI
Events emit logs to the transaction receipt rather than to contract storage, which makes them roughly an order of magnitude cheaper than an equivalent SSTORE. Off-chain indexers such as The Graph and Alchemy subscribe to those logs to power application UIs without polling state. ABI encoding is the bridge between the two worlds: the Solidity compiler emits a JSON ABI describing each function's input and output types, and frontends use that ABI to encode calldata before signing a transaction. Without correct ABI encoding, the EVM cannot route the call to the right function selector, and the transaction reverts.
Compiling and Testing Before Deployment
Ethereum smart contracts must be compiled before they can run anywhere, including a local test network. Running npx hardhat compile invokes the Solidity compiler version declared in hardhat.config.ts and writes JSON artifacts containing the ABI and bytecode into /artifacts. Version drift is the most common silent failure: a contract written for 0.8.24 will not produce the same bytecode under 0.8.20, and a mismatched compiler will reject pragma constraints with a confusing error. Pin the version in config, not only in the pragma.
Hardhat ships a Mocha plus Chai test runner that executes against a local in-process Ethereum Virtual Machine fork. Three test categories belong in every project: unit tests that exercise individual function behavior, integration tests that cover interactions between two or more contracts, and fork tests that replay against a live mainnet state snapshot via an Alchemy or Infura RPC endpoint. Fork tests catch regressions that pure unit tests miss, such as price-oracle behavior during a real-world liquidity event.
Three patterns deliver most of the available gas optimization. Use calldata instead of memory for read-only function inputs, which avoids a memory copy. Pack struct fields so that variables smaller than 32 bytes share a storage slot. Cache repeated storage reads in a local variable inside the function body; each fresh SLOAD after the first costs 100 gas under EIP-2929 warm-access pricing, while a stack read is effectively free. These three changes alone routinely cut function gas by 20 to 40 percent on a first draft, and disciplined gas optimization carries directly into lower user transaction fees once the contract is live.
| Tool | Primary Use | Language | Mainnet Fork | Notes |
|---|---|---|---|---|
| Hardhat | Local dev plus testing | JavaScript / TypeScript | Yes, via Alchemy or Infura | Most documented; default for tutorials |
| Foundry | Testing plus fuzzing | Rust (test DSL in Solidity) | Yes | Fastest test runner; growing adoption |
| Remix IDE | Prototyping plus quick deploy | Browser (no install) | No | Best for learning, not for production CI |
Deploying Smart Contracts on Ethereum
Ethereum smart contracts ship in two phases: a testnet deployment that proves the code works against a public network, then a mainnet deployment once tests and audits are clean. Sepolia is the current canonical Ethereum testnet after the Goerli deprecation in 2023; Holesky is reserved for proof-of-stake validator testing and infrastructure work, not application contracts. To prepare a testnet deployment, add a Sepolia network entry to hardhat.config.ts with an RPC URL and the deployer wallet, then request Sepolia ETH from the Alchemy or Chainlink faucet.
Run npx hardhat run scripts/deploy.ts --network sepolia and capture the contract address from the deployment receipt. Verify the source on Sepolia Etherscan using the Hardhat Etherscan plugin: npx hardhat verify --network sepolia <address> <constructor-args>. Verification publishes the source and ABI, which lets any user read and write to the contract directly from the Etherscan UI. For gas estimation ahead of mainnet, log gasUsed from the local deployment receipt and multiply by the current base fee returned from eth_gasPrice; that gives a conservative ceiling for the smart contract deployment cost in ETH.
For context on how production teams structure these workflows at scale, our piece on Real World Examples Of Enterprise Blockchain documents the operational patterns enterprise deployers adopt around multisig governance and change control.
- Smart contract security audit complete: Slither static analysis clean, plus a manual review for any contract holding over $10,000 in total value locked.
- All unit, integration, and fork tests passing against a current mainnet fork.
- Gas estimate inside budget at the current base fee, with a 2x headroom for fee spikes.
- Contract verified on Etherscan immediately after the mainnet transaction confirms.
- A Gnosis Safe multisig wallet set as the owner for any contract with admin functions, with no single EOA holding upgrade rights.
- Emergency pause mechanism (OpenZeppelin
Pausable) deployed and tested on Sepolia first.
Security, Auditing, and the Proxy Upgrade Pattern
Ethereum smart contracts holding user funds are a permanent target, and the on-chain logic that secures them carries no rollback button. Three vulnerability classes account for the majority of public exploits. Reentrancy vulnerability sits at the top of the list: an external call made before a state update lets the called contract re-enter the original function and drain balances before the accounting catches up. The canonical reentrancy vulnerability incident is the 2016 DAO hack, which moved roughly 3.6 million ETH and triggered the Ethereum fork that produced ETC. The Checks-Effects-Interactions pattern is the standard mitigation: validate inputs, update state, then make external calls last. OpenZeppelin's ReentrancyGuard modifier is the belt-and-braces backup.
Integer overflow and underflow are the second class. Solidity 0.8 and later add built-in arithmetic checks that revert on overflow, which closes the door on the older SafeMath idiom for new code. Access control errors round out the top three: missing onlyOwner modifiers on privileged functions, or the use of tx.origin instead of msg.sender for permission checks. The tx.origin mistake is particularly dangerous, since a phishing contract can satisfy the check on behalf of a legitimate user. The IEEE survey on smart contract vulnerabilities cataloges the full taxonomy, and NIST SP 800-193 frames the deterministic execution properties that the EVM inherits at the platform layer.
Running Slither and Mythril Before Mainnet
Two open-source tools belong in every smart contract security audit. Slither, maintained by Trail of Bits, installs with pip install slither-analyzer and runs as slither . from the project root; it emits a severity-ranked list of detector findings in seconds. Mythril, from ConsenSys, runs symbolic execution against bytecode and surfaces deeper invariant violations: myth analyze contracts/MyContract.sol. Both tools produce false positives, which means each finding requires a developer review rather than an automatic build break. A clean Slither pass is necessary, not sufficient; for contracts with material TVL, treat a full smart contract security audit from a firm such as Trail of Bits, OpenZeppelin, or Code4rena as a mandatory pre-mainnet gate.
The Proxy Upgrade Pattern
The proxy upgrade pattern resolves the immutability constraint by separating storage from logic. A thin proxy contract holds the state and forwards every call via delegatecall to a replaceable implementation contract; upgrading the system means deploying a new implementation and pointing the proxy at its address. OpenZeppelin ships two production-grade variants. The Transparent Proxy routes admin calls and user calls through different code paths to avoid function-selector collisions. The UUPS (Universal Upgradeable Proxy Standard, EIP-1822) puts the upgrade logic in the implementation itself, which keeps the proxy smaller and cheaper. Storage collisions are the silent killer in any proxy upgradeability, and EIP-1967's reserved storage slots convention is the standard defense; every OpenZeppelin upgradeable contract follows it by default. Even with a clean proxy design, the on-chain logic that decides who can trigger an upgrade deserves the same scrutiny as the implementation it controls.
For a look at how DeFi protocols structure governance around upgradeable contracts, see Uniswap vs Compound vs Aave: A Comprehensive Comparison, which documents the multisig and timelock patterns each protocol uses to gate proxy upgrades. Adjacent reading on automation in financial systems lives in AI In Finance Applications.
Further reading
- Ethereum Foundation: Smart Contracts Introduction
- Ethereum Yellow Paper (formal EVM specification)
- OpenZeppelin Contracts Documentation
- Hardhat Documentation
- IEEE Transactions on Dependable and Secure Computing: Smart Contract Vulnerability Survey
- Crypto Wallet Types Explained: Custodial, Non-Custodial, Hot, Cold, and AI-Agent Wallets
- Gemini AI Tool Review: A Developer's Guide
- How to Choose an AR SDK: ARKit vs ARCore vs AR Foundation
Frequently Asked Questions
What is a smart contract on Ethereum?
A smart contract on Ethereum is a self-executing program stored at a deterministic address on the Ethereum blockchain, running on the Ethereum Virtual Machine. Once deployed, it executes its code exactly as written whenever a transaction triggers one of its functions, with no operator able to halt or alter execution.
How do I deploy a smart contract on Ethereum?
Smart contract deployment on Ethereum follows two phases: first deploy to a testnet (Sepolia is the current canonical testnet) to verify behavior and estimate gas costs, then deploy to mainnet once testing passes. Use Hardhat with the Etherscan plugin for automated source verification after mainnet deployment.
Can I update a deployed smart contract on Ethereum?
Ethereum smart contracts are immutable once deployed: the bytecode at a contract address cannot change. The standard workaround is the upgrade pattern, where a proxy contract forwards calls via delegatecall to a replaceable implementation contract. OpenZeppelin's UUPS and Transparent Proxy patterns are the two production-grade implementations.









