A working definition
NIST's glossary defines a smart contract as a collection of code and data, sometimes called functions and state, deployed using cryptographically signed transactions on a blockchain network. All nodes must derive the same result when it runs.[1]
Ethereum.org adds the practical view: a contract is a type of account. It has an address and a balance and can receive transactions, but it is controlled by its code rather than by a private key.[2]
The word contract is loose. Most smart contracts are not legal agreements. They are programs that enforce rules such as who may withdraw funds, at what price a swap happens, or how many tokens exist.
The best-known uses are tokens, exchanges and lending. An ERC-20 token is one contract keeping a table of balances; an exchange is a contract that holds two tokens and prices swaps by formula; a lending market is a contract that tracks deposits, loans and collateral ratios.
How execution works on Ethereum
Contracts on Ethereum are usually written in Solidity or Vyper and compiled before deployment so the Ethereum Virtual Machine can run them. Ethereum.org describes a contract as code (its functions) and data (its state) that reside at a specific address on the chain.[2]
When your transaction calls a function, every validating node runs the same bytecode against the same state. Each operation costs gas, which caps how much work a transaction can do and stops infinite loops from halting the network.[4]
Because every node must reach the same answer, contracts cannot fetch data from offchain sources on their own. Ethereum.org says this is by design, and that price data and other outside facts arrive through oracles.[2]
Contracts can also call other contracts in the same transaction. A single swap may touch a router, two token contracts and a pool. If the transaction runs out of gas partway, the EVM reverts all changes but the gas provided is still consumed.[4]
See also: Chainlink · Pyth Network · Glossary: gas fee
Worked example: a simple storage contract
The Solidity documentation opens with a contract that stores one unsigned integer and exposes set and get functions. Calling get is free when done as a read through a node. Calling set changes state, so it is a transaction that costs gas.[3]
Compare it with the cheapest transaction possible. A plain ETH transfer uses 21,000 gas; at a 10 gwei base fee plus a 2 gwei tip that costs 252,000 gwei, or 0.000252 ETH. A contract call needs more gas, because every storage write and computation step adds to the total.[4]
The Solidity docs note that storage is costly to read and even more costly to initialise and modify, which is why well-written contracts pack values together and avoid writing data they do not need.[3]
Deploying the contract costs far more than calling it; ethereum.org notes that gas costs for deployment are far higher than for a simple transfer. That one-time cost is paid by the deployer, while each set call is paid by whoever sends it.[2]
The same logic explains why many apps keep large data off chain and store only a hash or a pointer in the contract.
Hard limits written into the protocol
Contracts are not unlimited. The protocol sets caps, and they shape how large applications are built.
| Limit | Value | Defined in |
|---|---|---|
| Maximum deployed code size | 24,576 bytes | EIP-170 |
| Maximum initcode size at creation | 49,152 bytes | EIP-3860 |
| Gas for a plain ETH transfer | 21,000 gas | ethereum.org gas docs |
See also: OpenZeppelin
Why the size cap matters
EIP-170, created in 2016, set MAX_CODE_SIZE to 24,576 bytes because a call costs constant gas while loading the code costs nodes work in proportion to its size. Ethereum.org notes that large contracts get around the cap with patterns such as the Diamond pattern that split logic across contracts.[5]
EIP-3860 later limited initcode, the code run once at deployment, to 49,152 bytes, which is twice the runtime cap, and added a charge of 2 gas for each 32-byte chunk of it.[6]
For users, the relevant effect is the proxy pattern. A proxy lets a team swap the logic behind a fixed address, which fixes bugs but also means the code you audited yesterday may not be the code that runs today.
Splitting logic has a cost of its own: every call between contracts adds gas, and more contracts mean more addresses users must verify.
Where smart contracts fail
Code is hard to change once deployed. Ethereum.org notes that contracts are public, work like open APIs that anyone can call, and that interactions with them are irreversible, so a single logic error is visible to every attacker at once.[2]
The common failure types are well known: reentrancy, faulty access control, price manipulation through thin oracle feeds, and unlimited token approvals that outlive the need for them. Audits and formal tests reduce the risk but do not remove it.
Access control is the second classic failure. A function meant only for the owner that lacks the check becomes a function anyone can call.
From source code to a live address
The life cycle has four steps. A developer writes the contract in Solidity, compiles it into EVM bytecode and an ABI, deploys it with a transaction that carries the bytecode, and then publishes the source so explorers can verify it. The Solidity docs call the ABI the standard way to interact with contracts in the Ethereum ecosystem.[7]
The ABI, or application binary interface, is described in JSON as an array of function, event and error descriptions. Wallets and frontends use it to encode your call into the data field of a transaction and to decode the result, because the encoding is not self-describing. Without the ABI, a contract is still callable, but the wallet can only show raw hex.[7]
Deployment is a transaction sent with no recipient. The Solidity docs explain that the new contract's address is derived from the creator's address and nonce, and that the output of the creation code is stored as the contract's code. That stored runtime code is the part bound by the 24,576-byte cap.[3]
Testing happens before any of this touches mainnet. Teams run unit tests on a local chain, then deploy to a public testnet, where test ETH from a faucet pays for gas and mistakes cost nothing.
After launch, the only ways to change behavior are the ones written into the code: parameters an owner may set, a pause function, or a proxy upgrade. Anything else requires deploying a new contract and asking users to move.
See also: Hardhat · Foundry · Remix · Crypto faucets
The bottom line
A smart contract is only as trustworthy as its code and its upgrade keys. Before using one with real money, confirm three things on a block explorer: the source is verified, an audit covers that exact version, and any admin or upgrade role is held by a multisig or timelock rather than one address.
Educational content, not financial advice. Crypto assets are volatile; do your own research.