Smart contract development is writing programs that run on a blockchain, hold value and cannot be quietly patched once deployed. The code itself is often short. The discipline around it, specification, testing, review, deployment and key management, is what separates contracts that hold funds safely from the ones that appear in exploit post-mortems.
This guide covers the whole lifecycle across chains. If you are working only on Ethereum, the Ethereum smart contract guide goes deeper on EVM specifics, and the Solidity development page focuses on the language itself.
What a smart contract can and cannot do
A contract is code plus storage at an address. Anyone can call its public functions; it executes deterministically on every node, and its state changes only through transactions. That gives you three properties ordinary backends cannot easily offer: shared state nobody can edit unilaterally, programmable custody of assets, and composability with other contracts.
The limits matter just as much. Contracts cannot fetch web data on their own; they need oracles. They cannot keep secrets, since all storage is public even if marked private. They cannot schedule themselves; something must send a transaction. And every computation costs gas, so heavy logic belongs off-chain with only verification on-chain.
Languages and platforms
| Language | Runs on | Notes |
|---|---|---|
| Solidity | Ethereum and all EVM chains and rollups | By far the largest ecosystem, audited libraries (OpenZeppelin, Solady), most auditors |
| Vyper | EVM | Python-like, deliberately restrictive; used by Curve and others |
| Rust (Anchor framework) | Solana | Account-based programming model; different bug classes such as missing account checks |
| Rust (CosmWasm, ink!, Stylus) | Cosmos chains, Polkadot ecosystem, Arbitrum Stylus | WebAssembly-based contracts |
| Move | Sui, Aptos | Resource-oriented types that make assets hard to duplicate or lose by accident |
| Kotlin/Java, Go | Corda, Hyperledger Fabric | Permissioned enterprise ledgers; see Hyperledger development |
Pick the chain for its users and liquidity, then accept its language. Auditor availability is a practical tiebreaker: there are far more experienced reviewers for Solidity than for newer environments.
The EVM toolchain in 2026
- Foundry: forge for compiling and testing in Solidity, built-in fuzzing and invariant testing, cast for chain interaction, anvil for local and forked nodes. The default for most security-focused teams.
- Hardhat: JavaScript and TypeScript-centric, strong plugin ecosystem, Ignition for deployments. Popular with teams whose frontend and scripts are in TypeScript. Many projects use both.
- Libraries: OpenZeppelin Contracts for standards (ERC-20, ERC-721, ERC-1155, ERC-4626, AccessControl, upgradeable proxies); Solady when gas optimization justifies more complex code.
- Static analysis: Slither and Aderyn catch common issues on every commit.
- Client libraries: viem and ethers for scripts and frontends.
The development process
- Write a specification. Plain-language description of every actor, every function, every state transition, and the invariants that must always hold, for example "total shares times price per share never exceeds assets held". Auditors and fuzzers both need this.
- Design the architecture. Decide contract boundaries, which standards to implement, where oracles come in, who holds which roles, and whether the system is upgradeable.
- Implement with standard building blocks. Inherit audited implementations rather than writing token logic yourself. Keep custom code small and readable; cleverness is a liability.
- Test in layers. Unit tests for each function, fuzz tests with random inputs, invariant tests that run random call sequences against the spec's invariants, and fork tests against real mainnet state for integrations with other protocols.
- Review internally. Line-by-line peer review plus static analysis. Fix everything you can before paying an auditor to find it.
- Audit externally. Freeze a commit and send it to one or more independent firms or an audit contest. See the smart contract audit guide for how that process works.
- Deploy with scripts, not by hand. Scripted, reproducible deployments; deterministic addresses via CREATE2 where useful; verify source on block explorers and Sourcify; transfer ownership to a multisig immediately.
- Monitor and respond. Alerts on unusual events and balance changes, a documented incident plan, pause functions where appropriate, and a bug bounty for ongoing review.
Upgradeability: options and trade-offs
Deployed bytecode cannot change, so upgradeable systems use a proxy that delegates calls to a replaceable implementation. Storage stays in the proxy; logic moves.
- Transparent proxy: upgrade logic sits in the proxy and a separate admin. Simple to reason about, slightly more gas per call.
- UUPS (ERC-1822 style): upgrade logic lives in the implementation. Cheaper calls, but upgrading to an implementation that omits the upgrade function bricks all future upgrades.
- Beacon proxy: many proxies point to one beacon, so you upgrade many instances at once; useful for factories.
- Diamond (EIP-2535): multiple implementation "facets" behind one address. Powerful for large systems, complex to audit.
Proxies standardize their storage slots under EIP-1967. The classic upgrade bugs are storage layout collisions between versions and implementations left uninitialized, which attackers can initialize and take over. Upgradeability also means users must trust whoever controls upgrades, so pair it with a multisig and a timelock long enough for users to exit if they disagree.
Security: the bug classes that keep recurring
- Reentrancy: calling out to another contract before updating your own state. Use checks-effects-interactions and reentrancy guards; watch for read-only reentrancy across protocols.
- Access control: missing modifiers on admin functions, or initializers callable by anyone.
- Oracle and price manipulation: reading spot prices from a single pool that a flash loan can move within one transaction. Use robust oracles and time-weighted prices.
- Rounding and precision: division before multiplication, and the ERC-4626 vault inflation attack where the first depositor manipulates share price.
- Signature issues: replay across chains or contracts, missing nonces, malleable signatures. Use EIP-712 typed data with domain separation.
- Unsafe external calls and token assumptions: tokens that do not return booleans, fee-on-transfer and rebasing tokens.
- MEV and front-running: transactions with no slippage limits or deadlines get sandwiched.
Solidity 0.8 and later revert on integer overflow by default, which removed one historic class of bugs, but unchecked blocks bring it back if used carelessly. The article on why audits matter covers well-known exploits in more detail.
Gas and performance
Storage writes dominate gas costs, so the biggest wins come from data layout: packing variables into slots, avoiding unbounded loops over storage, and emitting events instead of storing data only read off-chain. Ethereum's Dencun upgrade added transient storage (EIP-1153), which makes patterns like reentrancy locks cheaper. On rollups, calldata and blob costs matter more than execution, so compact inputs help. Do not trade readability for small savings in code that holds significant value.
Keys, roles and operational security
Many large losses came not from contract bugs but from compromised keys. Separate roles so that no single key can do everything: a deployer that is discarded after setup, an owner or admin role held by a multisig such as Safe with signers on separate hardware wallets, a narrowly scoped operator role for routine actions, and a guardian that can only pause. Put upgrades and parameter changes behind a timelock so users can see them coming. Rehearse the incident plan: who can pause, how signers are reached at night, and how you communicate with users.
Cost and timeline drivers
| Project type | Typical engineering effort (assumptions) |
|---|---|
| Standard token (ERC-20/721/1155) from audited libraries | Days to two weeks for one engineer, including tests and deployment scripts |
| Staking, vesting or escrow contracts | 3–6 weeks for one senior engineer |
| DeFi protocol with novel mechanics (lending, AMM variant, vaults) | 3–6 months for two or three engineers, multiple audits |
| Cross-chain systems | Longer still; bridge and messaging security is its own specialty |
Audit cost scales with lines of code and complexity, and good firms book up weeks ahead. Budget time for fixes and a re-review. For DeFi-specific considerations, see DeFi smart contract development.
What to ask any developer or team
- Show me your test suite: does it include fuzz and invariant tests, and what is the branch coverage?
- Which invariants did you define, and how are they tested?
- Who holds deployer and admin keys at each stage, and when do they move to our multisig?
- How is the system upgraded or paused, and who can do it?
- Which external protocols and oracles do we depend on, and what happens if one fails?
Frequently asked questions
How long does it take to develop a smart contract?
A standard token using audited libraries can be ready in days. A custom protocol with novel economics takes months including audits. The testing and review phases often take as long as writing the code.
Foundry or Hardhat?
Foundry is faster and lets you write tests and fuzzing in Solidity, which most security-minded teams prefer. Hardhat fits teams that live in TypeScript and want its plugin ecosystem. Using Foundry for tests and Hardhat or scripts for deployment is common.
Can a smart contract be changed after deployment?
The bytecode at an address cannot change. Systems can be designed to be upgradeable through proxies, or to be migrated to a new contract. Either way, whoever controls the upgrade can change the rules, so that power should be constrained by a multisig and timelock.
Is an audit enough to make a contract safe?
No. Audits are time-boxed reviews that reduce risk. Strong internal testing, formal specification of invariants, multiple reviews for high-value code, bug bounties and live monitoring together give much better protection.
Should I write my own token contract?
Almost never from scratch. Inherit from audited implementations such as OpenZeppelin and add only the custom behavior you need. Most token exploits come from hand-written transfer or minting logic.
What does "verified" mean on a block explorer?
It means the published source code compiles to exactly the bytecode deployed at the address, so anyone can read what the contract does. Always verify; unverified contracts are a red flag to users and integrators.