Solidity is the main language for writing smart contracts on Ethereum and every EVM-compatible chain. Good Solidity development is less about syntax than about designing code that holds other people's money, cannot easily be changed after deployment, and runs in an adversarial environment where every function is public to attackers.
This page focuses on the language and the craft: what modern Solidity code looks like, the toolchain professionals use, and how to tell strong work from weak. For project-level planning, see the guides to smart contract development and Ethereum smart contracts.
What makes Solidity different from ordinary programming
- Code is (usually) permanent. Once deployed, bytecode cannot be edited. Upgrades require a proxy pattern decided in advance, and that pattern brings its own risks.
- Every operation costs gas. Storage writes are far more expensive than memory or computation, so data layout is a design decision, not an afterthought.
- Everything is public. "Private" variables are only private to other contracts; anyone can read storage directly. Pending transactions are visible in the mempool, which enables front-running.
- External calls hand over control. When your contract calls another contract, that contract can call back into yours before your function finishes. This is the root of reentrancy bugs.
- Determinism is mandatory. No randomness, no network calls, no floating point. Off-chain data arrives through oracles.
Modern Solidity features worth knowing
The 0.8.x line changed day-to-day Solidity significantly. If a codebase or candidate still relies on older habits, that is a signal.
| Feature | Why it matters |
|---|---|
| Checked arithmetic by default (0.8.0+) | Overflow and underflow revert automatically, making SafeMath unnecessary. unchecked blocks opt out where safe for gas savings. |
| Custom errors | revert InsufficientBalance(available, required) is cheaper than revert strings and gives tooling structured data. |
| Immutable and constant variables | Values baked into bytecode avoid storage reads entirely. |
Transient storage (tstore/tload, EIP-1153) | Storage cleared at the end of each transaction, useful for cheap reentrancy locks and per-transaction context on chains that support it. |
| User-defined value types and operators | Typed wrappers (for example a Price type) catch unit-mixing mistakes at compile time. |
| The via-IR pipeline | An alternative compilation path that can optimize more aggressively and relieves "stack too deep" errors, at the cost of slower builds. |
Always pin a specific compiler version for production and check the official Solidity documentation and its list of known compiler bugs for the version you use.
The professional toolchain
- Foundry (forge, cast, anvil): tests written in Solidity, fast execution, built-in fuzzing and invariant testing, mainnet forking. Now the default for many security-focused teams.
- Hardhat: JavaScript and TypeScript based, with a large plugin ecosystem; common where the front-end team also maintains deployment scripts.
- OpenZeppelin Contracts: audited implementations of ERC-20, ERC-721, ERC-1155, ERC-4626, access control and upgradeable proxies. Reuse them instead of writing your own token logic.
- Static analysis such as Slither, plus linters, run in CI on every pull request.
- Formal methods and symbolic execution for high-value invariants in DeFi protocols.
Security patterns every Solidity developer should apply
- Checks-effects-interactions. Validate inputs, update your own state, and only then make external calls. Add a reentrancy guard on functions that move value.
- Explicit access control. Use role-based permissions, and put privileged roles behind a multisig and a timelock.
- Pull over push payments. Let users withdraw rather than looping over recipients and sending to each.
- Bounded loops. Never iterate over an array that users can grow without limit; it becomes a denial-of-service vector.
- Oracle hygiene. Do not use a single DEX spot price as an oracle; it can be manipulated within one transaction using flash loans.
- Safe token handling. Use SafeERC20 wrappers, since some tokens do not return booleans, charge fees on transfer or rebase balances.
- Invariant tests. State the properties that must always hold (total supply equals sum of balances, a vault never owes more than it holds) and fuzz against them.
Even with all of this, an independent smart contract audit is standard before mainnet for anything that custodies value.
Gas optimization, kept in proportion
Packing storage variables into shared slots, caching storage reads in memory, using calldata for read-only arrays and emitting events instead of storing history all make real differences. Inline assembly can save more, but every line of assembly bypasses the compiler's safety checks and makes audits harder. On layer 2 networks, where execution is cheap and data posting dominates costs, readability usually wins over micro-optimization.
How to evaluate Solidity developers or a vendor
- Ask for public code: deployed and verified contracts, or repositories with tests. Read the tests, not just the contracts.
- Ask how they would make a specific contract upgradeable, and what could go wrong (storage collisions, uninitialized implementations).
- Ask them to explain a past exploit, such as a reentrancy or oracle manipulation incident, and how they would have prevented it.
- Check whether they reach for OpenZeppelin or write everything from scratch. The second is a red flag for standard functionality.
- Confirm they budget time for fuzzing, invariant tests and an external audit.
Specialized work, such as DeFi protocol contracts or token launches on Ethereum, deserves developers with directly relevant experience.
Frequently asked questions
Is Solidity only for Ethereum?
No. It compiles to EVM bytecode, so the same contracts run on Ethereum layer 2s, BNB Smart Chain, Polygon PoS, Avalanche C-Chain and other EVM chains, with minor differences in opcodes and gas behavior that you must test for.
Should I use Solidity or Vyper?
Solidity has the larger ecosystem, more auditors and more libraries. Vyper is deliberately simpler and is used by some major protocols. For most teams, Solidity's tooling and talent pool make it the practical default.
How long does it take to learn Solidity?
An experienced developer can write working contracts within weeks. Writing contracts safe enough to hold significant value takes much longer, mostly spent studying exploits, testing techniques and the EVM itself.
Can I fix a bug after deployment?
Only if you planned for it with an upgradeable proxy or a migration path. Otherwise you deploy a new contract and ask users to move, which is slow and erodes trust.