Glossary · Blockchain development

Smart contracts

Published 24 May 2025 · Updated 2 Nov 2026 · 16 min read

A smart contract is a program stored at a blockchain address that runs when a transaction or another contract calls it. Every node executing it must get the same result. Its code sets the available actions, while its stored data records the current state of the program.

  • A smart contract runs only when called, and the caller pays gas.
  • Deployed Ethereum contract code stays fixed, but a proxy can replace the logic behind it.
  • CertiK's 2024 and 2025 reports attribute most losses to phishing, keys and supply chains, not code bugs.

What a smart contract is and is not

Nick Szabo described a vending machine in 1996: coins go in, and fixed rules determine which product comes out. Blockchain smart contracts apply programmed rules to digital records and actions. Szabo argued that the till should hold less value than the cost of breaking the machine. The definitions below trace the term from Szabo's 1994 proposal through the 2013 Ethereum white paper, NIST's October 2018 overview, and later legal definitions.

SourceDateDefinition, in its own wordsWhat it adds
Nick SzaboDate1994Definition, in its own words“A smart contract is a computerized transaction protocol that executes the terms of a contract.”What it addsCoins the term. The goal is fewer exceptions and less need for trusted intermediaries
Nick SzaboDate1996Definition, in its own words“A canonical real-life example, which we might consider to be the primitive ancestor of smart contracts, is the humble vending machine.”What it addsThe till should hold less than it would cost to break the machine
Ethereum white paperDate2013Definition, in its own wordsContracts are “more like "autonomous agents" that live inside of the Ethereum execution environment, always executing a specific piece of code when "poked" by a message or transaction”What it addsCode runs only when a transaction or a message calls it
NIST IR 8202DateOctober 2018Definition, in its own words“A smart contract is a collection of code and data (sometimes referred to as functions and state) that is deployed using cryptographically signed transactions on the blockchain network”What it addsExecution must be deterministic, so every node reaches the same result
ethereum.orgDateUpdated 25 February 2026Definition, in its own words“A "smart contract" is simply a program that runs on the Ethereum blockchain.”What it addsA contract is a type of account, with a balance, code and state
Law Commission of England and WalesDate25 November 2021Definition, in its own wordsSmart contracts are “computer programs which run automatically, in whole or in part, without the need for human intervention.”What it addsA smart legal contract is one specific type, and current law can support it
EU Data Act, Article 2(39)DateApplies from 12 September 2025Definition, in its own words“'smart contract' means a computer program used for the automated execution of an agreement or part thereof, using a sequence of electronic data records and ensuring their integrity and the accuracy of their chronological ordering”What it addsArticle 36 adds duties for vendors of contracts that execute data sharing agreements

Ethereum's developer documentation, updated 25 February 2026, describes a smart contract as an account containing code and state. It executes only when called. Deterministic execution means every executing node reaches the same result from the same inputs. A contract cannot fetch off-chain facts itself. Those arrive through signed transactions or an oracle. Contracts are not deletable by default, and interactions are irreversible. NIST IR 8202, published in October 2018, also makes clear that these programs need not perform financial functions.

The smart contract label does not make a program a legal agreement, an independent actor or a guarantee of correct behavior. A smart contract can follow flawed instructions exactly. Its logic may be replaceable, and visibility depends on the platform. Daml on Canton restricts what each party sees. Many contracts publish no verified source code. Ethereum's nontechnical documentation, updated 1 July 2026, calls blind signing a major risk: a user approves an interaction without understanding its effect.

How a smart contract runs on Ethereum

The escrow below holds ether for a buyer, a seller and an arbiter. It moves through funded, released and refunded states. Follow each transaction to compare the caller, function, result, event or custom error selector, gas used, and remaining balance. These are local measurements, not Ethereum mainnet averages.

Figure 1 · Lab run, solc 0.8.38 on Osaka rulesFollow an escrow paymentChoose release, refund or rejection, then step through calls to see permissions, errors, events, fees and balances change.
Scenario
Calls in orderSeller: bob, an ordinary account
Calls in orderSeller: bob, an ordinary account
Calls in orderSeller: a contract whose receive function reverts
Step 1 of 10Succeeds
Caller
alice
Call
new Escrow(bob, carol, deadline)
Result
Contract created, 1,841 bytes of runtime code
Gas used
451,276
The code deposit alone is 200 gas per byte, 368,200 gas, 81.6% of the deployment.AwaitingDepositFundedReleasedRefunded
Escrow balance
0 ETH
Events so far
No events yet
Step 2 of 10Reverts
Caller
bob
Call
deposit() with 1 ETH
Error
NotBuyer(bob)
Error selector
0x0eed4964
Gas used
21,359
Only the buyer may deposit. The revert returns bob's ether, but not the gas.AwaitingDepositFundedReleasedRefunded
Escrow balance
0 ETH
Events so far
No events yet
Step 3 of 10Reverts
Caller
alice
Call
deposit() with 0 ETH
Error
ZeroDeposit()
Error selector
0x56316e87
Gas used
23,504
An empty deposit is rejected.AwaitingDepositFundedReleasedRefunded
Escrow balance
0 ETH
Events so far
No events yet
Step 4 of 10Succeeds
Caller
alice
Call
deposit() with 1 ETH
Result
Deposited(alice, 1 ETH)
Gas used
67,137
The escrow now holds the ether and records the amount.AwaitingDepositFundedReleasedRefunded
Escrow balance
1 ETH
Events so far
Deposited(alice, 1 ETH)
Step 5 of 10Reverts
Caller
alice
Call
deposit() with 1 ETH again
Error
WrongState(1)
Error selector
0xe37d4aeb
Gas used
23,668
State 1 is Funded, so a second deposit is refused.AwaitingDepositFundedReleasedRefunded
Escrow balance
1 ETH
Events so far
Deposited(alice, 1 ETH)
Step 6 of 10Reverts
Caller
bob
Call
release()
Error
NotAuthorized(bob)
Error selector
0x4a0bfec1
Gas used
21,374
Only the buyer or the arbiter may release the money.AwaitingDepositFundedReleasedRefunded
Escrow balance
1 ETH
Events so far
Deposited(alice, 1 ETH)
Step 7 of 10Reverts
Caller
alice
Call
refund() before the deadline
Error
TooEarly(1800000000, 1800604800)
Error selector
0xe58369d1
Gas used
23,598
The buyer may refund only after the deadline. The error carries both timestamps.AwaitingDepositFundedReleasedRefunded
Escrow balance
1 ETH
Events so far
Deposited(alice, 1 ETH)
Step 8 of 10Succeeds
Caller
carol
Call
release()
Result
Released(bob, 1 ETH)
Gas used
39,492
The arbiter releases. The state changes before the ether leaves, and bob receives 1 ETH.AwaitingDepositFundedReleasedRefunded
Escrow balance
0 ETH
Events so far
Deposited(alice, 1 ETH), Released(bob, 1 ETH)
Step 9 of 10Reverts
Caller
alice
Call
release() again
Error
WrongState(2)
Error selector
0xe37d4aeb
Gas used
23,653
State 2 is Released. Nothing is left to release.AwaitingDepositFundedReleasedRefunded
Escrow balance
0 ETH
Events so far
Deposited(alice, 1 ETH), Released(bob, 1 ETH)
Step 10 of 10Reverts
Caller
carol
Call
refund()
Error
WrongState(2)
Error selector
0xe37d4aeb
Gas used
23,632
A refund after the release is refused as well.AwaitingDepositFundedReleasedRefunded
Escrow balance
0 ETH
Events so far
Deposited(alice, 1 ETH), Released(bob, 1 ETH)
Step 1 of 3Succeeds
Caller
alice
Call
deposit() with 1 ETH
Result
Deposited(alice, 1 ETH)
Gas used
67,137
A second escrow with the same rules is funded.AwaitingDepositFundedReleasedRefunded
Escrow balance
1 ETH
Events so far
Deposited(alice, 1 ETH)
Step 2 of 3No transaction
Caller
Nobody
Call
Seven days pass
Result
No transaction
Gas used
None
The contract does nothing on its own. It waits until someone calls it.AwaitingDepositFundedReleasedRefunded
Escrow balance
1 ETH
Events so far
Deposited(alice, 1 ETH)
Step 3 of 3Succeeds
Caller
alice
Call
refund() after the deadline
Result
Refunded(alice, 1 ETH)
Gas used
37,044
After the deadline the buyer can take the ether back without the arbiter.AwaitingDepositFundedReleasedRefunded
Escrow balance
0 ETH
Events so far
Deposited(alice, 1 ETH), Refunded(alice, 1 ETH)
Step 1 of 3Succeeds
Caller
alice
Call
deposit() with 1 ETH
Result
Deposited(alice, 1 ETH)
Gas used
67,137
This time the seller is a contract that refuses incoming ether.AwaitingDepositFundedReleasedRefunded
Escrow balance
1 ETH
Events so far
Deposited(alice, 1 ETH)
Step 2 of 3Reverts
Caller
carol
Call
release()
Error
TransferFailed()
Error selector
0x90b8ec18
Gas used
38,298
The transfer fails, so the whole call reverts. The state change to Released is undone and the escrow keeps 1 ETH.AwaitingDepositFundedReleasedRefunded
Escrow balance
1 ETH
Events so far
Deposited(alice, 1 ETH)
Step 3 of 3Succeeds
Caller
carol
Call
refund()
Result
Refunded(alice, 1 ETH)
Gas used
39,480
The arbiter may refund before the deadline, so alice gets the ether back.AwaitingDepositFundedReleasedRefunded
Escrow balance
0 ETH
Events so far
Deposited(alice, 1 ETH), Refunded(alice, 1 ETH)

Wrong callers and invalid states revert. After alice deposits 1 ETH, carol releases it to bob, leaving the escrow in its Released state.

After the seven-day deadline, alice can recover the deposit. The successful refund returns 1 ETH and changes the escrow state to Refunded.

The seller rejects ETH, so release reverts without changing the Funded state. The escrow keeps 1 ETH until carol refunds alice.

A small escrow ran with solc 0.8.38 on a local EVM using Osaka rules on 2 November 2026.

Compiling Solidity source or Vyper produces EVM bytecode and a JSON ABI describing functions. Calls start with a 4-byte function selector. A deployment transaction sends initcode without a recipient, not to the zero address. Initcode returns runtime code. CREATE uses creator and nonce, while CREATE2 uses creator, salt and initcode. Ethereum mainnet limits on 2 November 2026 are 24,576 runtime bytes and 49,152 initcode bytes. In the 2 November 2026 lab, storing code accounts for 81.6% of deployment gas.

Each transaction incurs gas costs, even when a revert undoes state changes. An event writes a log that contracts cannot read. STATICCALL invokes a view function without allowing state changes. DELEGATECALL runs another contract's code against the caller's storage. Since Dencun on 13 March 2024, SELFDESTRUCT deletes code only within its creating transaction under EIP-6780. ERC-4337 uses bundlers and EntryPoint, with optional paymasters covering fees. EIP-7702 has let accounts with keys set code since Pectra on 7 May 2025. The EVM cannot access networks or files.

Ethereum's changing contract rules

An EVM compiler target is tied to network rules, not just a language version. The timeline tracks changes to calls, deployment and deletion. EIP-170 and EIP-3860 set different limits for runtime code and initcode. Ethereum's current mainnet rules on 2 November 2026 follow Fusaka, activated on 3 December 2025.

Mainnet dateUpgradeWhat changed for contractsProposal
14 March 2016UpgradeHomesteadWhat changed for contractsDELEGATECALL runs another contract's code in the caller's storageProposalEIP-7
20 July 2016UpgradeDAO fork, block 1,920,000What changed for contractsMoved The DAO's ether to a contract with one function, withdrawProposalIrregular state change
22 November 2016UpgradeSpurious DragonWhat changed for contractsRuntime code capped at 24,576 bytesProposalEIP-170
16 October 2017UpgradeByzantiumWhat changed for contractsREVERT stops a call and undoes its changes. STATICCALL forbids state changesProposalEIP-140, EIP-214
28 February 2019UpgradeConstantinopleWhat changed for contractsCREATE2 fixes a contract's address from a salt before it existsProposalEIP-1014
5 August 2021UpgradeLondonWhat changed for contractsNew code may not start with the byte 0xEFProposalEIP-3541
12 April 2023UpgradeShapellaWhat changed for contractsInitcode capped at 49,152 bytes and charged per 32-byte wordProposalEIP-3860
13 March 2024UpgradeDencunWhat changed for contractsSELFDESTRUCT deletes code only in the creating transaction. Transient storage addedProposalEIP-6780, EIP-1153
7 May 2025UpgradePectraWhat changed for contractsAccounts with keys can set code through a delegationProposalEIP-7702
3 December 2025UpgradeFusakaWhat changed for contractsThe rules mainnet runs on 2 November 2026ProposalEIP-7607
No mainnet dateUpgradeGlamsterdamWhat changed for contracts64 KiB code limit, logs for ETH transfers, new state creation pricing. On Sepolia since 6 October 2026ProposalEIP-7954, EIP-7708, EIP-8037

EIP-7954 raises runtime code to 65,536 bytes and initcode to 131,072 bytes, or 64 KiB and 128 KiB. This Ethereum improvement proposal is scheduled for Glamsterdam, live on Sepolia since 6 October 2026. On 22 October 2026, the Ethereum Foundation said core developers had postponed Glamsterdam on the Hoodi testnet to investigate EIP-8037 interactions with mainnet smart contracts. There is no mainnet date on 2 November 2026. Solidity's unreleased 0.8.39 already warns about these limits when targeting the Amsterdam EVM version.

Immutable code and upgradeable smart contracts

Keeping an address does not mean keeping its behavior. An Ethereum smart contract can route calls to replaceable logic while retaining stored data. The figure compares four storage-box designs and their permissions. Its deployment and call costs come from the same local lab, rather than from a survey of mainnet contracts.

Figure 2 · Lab run, OpenZeppelin Contracts 5.7.0Compare four upgrade designsChoose a pattern and action to compare deployment costs, permissions, repeated initialization, incompatible logic and the effects of a damaged storage layout.
Pattern
Try
Immutable
Contracts
Box
Deploy gas
90,199
Runtime bytes
170
First write, overwrite
43,513 and 26,413
Proxy overhead
None
ERC-1967 slot set
None
Who can upgrade
Nobody. A new version needs a new address and a migration
Transparent proxy
Contracts
BoxV1, TransparentUpgradeableProxy and the ProxyAdmin it creates
Deploy gas
202,907 plus 678,738
Runtime bytes
Implementation 692, proxy 1,059
First write, overwrite
48,471 and 31,371
Proxy overhead
4,958 per overwrite
ERC-1967 slot set
Implementation and admin
Who can upgrade
The owner of the ProxyAdmin
UUPS proxy
Contracts
BoxUUPSV1 and ERC1967Proxy
Deploy gas
547,645 plus 175,615
Runtime bytes
Implementation 2,167, proxy 163
First write, overwrite
48,394 and 31,294
Proxy overhead
4,881 per overwrite
ERC-1967 slot set
Implementation only
Who can upgrade
Whoever _authorizeUpgrade allows, here the owner
Beacon proxy
Contracts
BoxV1, UpgradeableBeacon and one BeaconProxy per instance
Deploy gas
202,907 plus 249,340, then 211,741 per proxy
Runtime bytes
Beacon 644, proxy 283
First write, overwrite
51,482 and 34,382
Proxy overhead
7,969 per overwrite
ERC-1967 slot set
Beacon only
Who can upgrade
The beacon owner, for every proxy at once
Upgrade as the ownerNot applicableBox has no upgrade function or initializer. Its constructor ran once, at deployment.
Upgrade as a strangerNot applicableBox has no upgrade function or initializer. Its constructor ran once, at deployment.
Initialize againNot applicableBox has no upgrade function or initializer. Its constructor ran once, at deployment.
Swap the storage layoutNot applicableBox has no upgrade function or initializer. Its constructor ran once, at deployment.
Use a non-UUPS implementationNot applicableBox has no upgrade function or initializer. Its constructor ran once, at deployment.
Initialize the bare implementationNot applicableBox has no upgrade function or initializer. Its constructor ran once, at deployment.
Upgrade as the ownerSucceeds
Result
Upgraded(BoxV2)
Gas used
37,794
The value 43 and the owner stay. The new increment() function works, and value() then returns 44.
Upgrade as a strangerReverts
Result
OwnableUnauthorizedAccount(bob)
Error selector
0x118cdaa7
Gas used
24,983
Sent to the ProxyAdmin. Sent to the proxy, the call goes to the implementation and reverts with empty data (26,823 gas).
Initialize againReverts
Result
InvalidInitialization()
Error selector
0xf92ee8a9
Gas used
29,106
The initializer already ran through the proxy, so a second call fails.
Swap the storage layoutSucceeds
Result
Upgraded(BoxV2BadLayout)
Gas used
37,794
The upgrade succeeds, but value() now returns alice's address read as a number: 247512291986854564435551364600938690683113101007.
Use a non-UUPS implementationNot applicableThe transparent proxy keeps its upgrade logic in the proxy, so the implementation needs no UUPS function.
Initialize the bare implementationNot applicableNot run for this pattern.
Upgrade as the ownerSucceeds
Result
Upgraded(BoxUUPSV2)
Gas used
37,491
The value 43 stays and version() returns uups-v2.
Upgrade as a strangerReverts
Result
NotOwner(bob)
Error selector
0x245aecd3
Gas used
29,719
The check lives in the implementation's _authorizeUpgrade, which rejects the caller.
Initialize againNot applicableNot run for this pattern.
Swap the storage layoutNot applicableNot run for this pattern.
Use a non-UUPS implementationReverts
Result
ERC1967InvalidImplementation(0x3d39...BA17)
Error selector
0x4c9c8ce3
Gas used
32,696
The proxy refuses code that could not upgrade again, so the upgrade path is not lost.
Initialize the bare implementationReverts
Result
InvalidInitialization()
Error selector
0xf92ee8a9
Gas used
24,156
_disableInitializers in the constructor locks the bare implementation. Without it, a stranger became its owner (67,950 gas), but could not upgrade through it (UUPSUnauthorizedCallContext, 22,750 gas).
Upgrade as the ownerSucceeds
Result
Upgraded(BoxV2) on the beacon
Gas used
32,702
One transaction moves both proxies. Proxy 1 keeps 43, proxy 2 keeps 7, and both run v2.
Upgrade as a strangerReverts
Result
OwnableUnauthorizedAccount(bob)
Error selector
0x118cdaa7
Gas used
23,887
Only the beacon owner can change the implementation.
Initialize againNot applicableNot run for this pattern.
Swap the storage layoutNot applicableNot run for this pattern.
Use a non-UUPS implementationNot applicableNot run for this pattern.
Initialize the bare implementationNot applicableNot run for this pattern.

The fixed version deploys for 90,199 gas and cannot upgrade. Replacing it requires a new address and migration, rather than an admin action.

Only the ProxyAdmin owner can upgrade this proxy. Stored values survive a compatible change, but swapping variable order succeeds and corrupts how the value is read.

The implementation authorizes upgrades. Unauthorized callers and non-UUPS replacements revert, while an owner-approved change preserves the stored value in this lab.

One authorized beacon change updates both proxies, preserving their separate values of 43 and 7. A stranger's attempt reverts without replacing their logic.

The same local lab uses OpenZeppelin Contracts 5.7.0, with gas measured on 2 November 2026.

Ethereum documentation calls deployed code immutable by design, but distinguishes upgradeability from mutability. Migration, data separation, strategy contracts and proxies change how later behavior is reached. ERC-1967 reserves slots for implementation, admin and beacon addresses. UUPS puts upgrade logic in the implementation. A beacon changes several proxies in one transaction. ERC-2535 diamonds distribute functions across facets, while ERC-7201 defines namespaced storage. In OpenZeppelin Contracts 5.7.0, checked on 2 November 2026, the stated recommendation shifts toward UUPS. None of these mechanisms edits the original deployed code.

Users must trust whoever controls future logic. A multi-signature wallet or DAO governance process can distribute upgrade authority. A timelock gives users time to exit but delays urgent fixes. Initialization needs checks, and existing variable order and types must stay unchanged. The 2 November 2026 lab accepts a reordered layout, then reads an owner's address as a number. An uninitialized implementation also admits a stranger as owner. That takeover does not affect the UUPS proxy in this run, because its context check rejects the attempted upgrade.

Smart contracts beyond Ethereum

Different networks give smart contracts different execution and data models. The table compares those mechanics, not platform quality. It includes Solidity and Vyper on Ethereum, Cairo on Starknet, Plutus on Cardano, Bitcoin Script, Clarity on Stacks and Rust through CosmWasm. Release details are a snapshot checked on 2 November 2026.

PlatformLanguagesHow code and state workLatest release seen, 2 November 2026
Ethereum and EVM chainsLanguagesSolidity, Vyper, YulHow code and state workA contract account keeps code and state at one address. Proxies can point it to new logicLatest release seen, 2 November 2026Solidity 0.8.38, 22 October 2026. Vyper 0.4.3, 19 June 2025
SolanaLanguagesRust with Anchor, Pinocchio or native RustHow code and state workPrograms are stateless and keep data in separate accounts. Upgradeable while an upgrade authority is setLatest release seen, 2 November 2026Anchor 1.2.2, 22 October 2026
AptosLanguagesMoveHow code and state workAssets are typed resources stored under accounts. Abilities decide if a value can be copied or droppedLatest release seen, 2 November 2026Docs updated 5 October 2026
SuiLanguagesMoveHow code and state workStorage is a set of objects with unique IDs. A published package is immutableLatest release seen, 2 November 2026mainnet-v1.82.3, 21 October 2026
StarknetLanguagesCairoHow code and state workProvable programs on an L2 verified over Ethereum. Every account is a contractLatest release seen, 2 November 2026Cairo 2.21.0, latest GitHub release
CantonLanguagesDamlHow code and state workEach party's validator runs the contract and sees only its part of a transaction. Contracts are archived, not editedLatest release seen, 2 November 2026Canton 3.7.1, 30 October 2026
CardanoLanguagesPlutus, AikenHow code and state workValidators return true or false for a transaction that spends an output, and the result can be checked before sendingLatest release seen, 2 November 2026Aiken 1.1.24, 26 September 2026
StacksLanguagesClarityHow code and state workInterpreted exactly as written, with reentrancy disallowed. Contracts can read Bitcoin stateLatest release seen, 2 November 2026stacks-core 4.0.5, 13 October 2026
BitcoinLanguagesScript, TapscriptHow code and state workA script locks an output and the spender supplies data that satisfies it. No loopsLatest release seen, 2 November 2026Taproot active since block 709,632
TezosLanguagesMichelson, SmartPy, LIGOHow code and state workA strongly typed, stack-based, Turing-complete languageLatest release seen, 2 November 2026Michelson docs updated 15 February 2024
Cosmos chainsLanguagesRust compiled to WasmHow code and state workCosmWasm, a Cosmos SDK module, runs contracts in its own virtual machineLatest release seen, 2 November 2026CosmWasm 3.0.11, 8 October 2026
PolkadotLanguagesink!How code and state workRust contracts for RISC-V and PolkaVM. The project has not been maintained since January 2026Latest release seen, 2 November 2026v6.0.0-beta.1, a pre-release

EVM contracts keep code and state at one address. Solana programs, including those written with Anchor, keep mutable state in separate accounts. Move's typed resources constrain how assets are handled. Sui organizes storage around objects with unique IDs. Cardano and Bitcoin check spending conditions. Canton runs Daml smart contracts, exposing only each party's part of a transaction. Clarity disallows reentrancy at language level. Polkadot's Rust contract language project has not been maintained since January 2026. Token interfaces also differ, including ERC-20 token contracts on Ethereum.

Usage figures measure different things. Etherscan counts 939,820 verified contracts on 2 November 2026. Blockscout records 2,245,546 new contracts in October 2026, without clarifying whether creation by other contracts is included. DefiLlama showed $85.094 billion in total value locked on the evening of 2 November 2026, and $44.41 billion on Ethereum in its chain data. TVL measures deposits in tracked DeFi protocols, not every asset held by a smart contract. These counts therefore describe different slices of activity, rather than a shared measure of adoption.

How smart contracts fail

A valid execution can still produce an unwanted outcome. Examples echo The DAO, Parity, Qubit QBridge and Harvest Finance. Choose a bug class and compare vulnerable and corrected versions, or old and new EVM rules. The contracts are simplified examples, so their balances and costs do not reproduce the original incidents.

Figure 3 · Five bug classes, dated incidents and lab replaysReplay five contract failuresSwitch bug classes and versions to compare the matched incident, remaining balances, execution costs and whether the attempted action succeeds.
Bug class
Version
The real incidentJune 2016The DAOA recursive call into splitDAO drained ether before balances were updated. The chain forked on 20 July 2016.
Loss reported
Over 3.6 million ETH
OWASP category
SC05:2025 Reentrancy, SC08:2026 Reentrancy Attacks
ethereum.org, Hacking Distributed
The real incident19 July 2017Parity multisigWallets forwarded unknown calls to a library by DELEGATECALL, so anyone could call initWallet and become an owner.
Loss reported
153,037 ETH from three wallets
OWASP category
SC01:2025 Improper Access Control, SC01:2026 Access Control Vulnerabilities
OpenZeppelin
The real incident6 November 2017Parity librarySomeone initialized the shared library itself and called kill 51 minutes later. Every wallet that used it froze.
Loss reported
Estimates of over 500,000 ETH frozen
OWASP category
SC01:2025 Improper Access Control
OpenZeppelin, Etherscan blocks 4,501,736 and 4,501,969
The real incident27 to 28 January 2022Qubit QBridgeA deposit with token address 0 did not fail, so the bridge credited deposits that never arrived.
Loss reported
$80 million
OWASP category
SC06:2025 Unchecked External Calls, SC06:2026 Unchecked External Calls
Qubit's report, DeFiHackLabs
The real incident26 October 2020Harvest FinanceFlash loans moved Curve stablecoin prices, so vault shares paid out more than they were worth.
Loss reported
$33.8 million
OWASP category
SC02:2025 Price Oracle Manipulation, SC03:2026 Price Oracle Manipulation
rekt.news, DeFiHackLabs
Lab replayFund drainedA fund holds 10 ETH of other users' money. The attacker deposits 1 ETH and withdraws from a contract that calls back into withdraw.
Fund before and after
10 ETH, then 0 ETH
Attacker contract after
11 ETH
Re-entries
10
Gas
175,033
Lab replayFund keeps 10 ETHA fund holds 10 ETH of other users' money. The attacker deposits 1 ETH and withdraws from a contract that calls back into withdraw.
Fund before and after
10 ETH, then 10 ETH
Attacker contract after
1 ETH, its own deposit
Re-entries
1, which sends 0
Gas
89,912
Lab replayRe-entry revertsA fund holds 10 ETH of other users' money. The attacker deposits 1 ETH and withdraws from a contract that calls back into withdraw.
Fund before and after
10 ETH, then 11 ETH
Attacker contract after
0 ETH, its deposit stays in the fund
Re-entries
0, the call reverts
Gas
111,298
Lab replayWallet drainedA wallet forwards unknown calls to a library. In the vulnerable version the wallet holds 9 ETH and initWallet has no check.
initWallet(attacker)
Succeeds, 29,577 gas
Owner after
attacker
execute(attacker, 9 ETH)
Succeeds, 33,776 gas
Wallet after
0 ETH
Lab replayCall revertsA wallet forwards unknown calls to a library. In the vulnerable version the wallet holds 9 ETH and initWallet has no check.
initWallet(attacker)
Reverts with AlreadyInitialized()
Selector
0x0dc149f0
Gas
26,709
Owner after
alice, unchanged
Lab replayWallet frozenThe attacker calls initWallet on the library (43,904 gas) and then kill (28,921 gas). Then a wallet bound to that library pays out 1 ETH.
Library code after kill
0 bytes, deleted
Wallet pays 1 ETH
Succeeds but moves nothing, 24,408 gas
Wallet after
5 ETH
Payee receives
0 ETH
Lab replayWallet still worksThe attacker calls initWallet on the library (43,904 gas) and then kill (28,921 gas). Then a wallet bound to that library pays out 1 ETH.
Library code after kill
665 bytes, kept under EIP-6780
Wallet pays 1 ETH
Succeeds, 36,276 gas
Wallet after
4 ETH
Payee receives
1 ETH
Lab replayBalance lostThe vulnerable fund pays with send and the fixed one with call. The depositor is a contract that rejects ether at first and accepts it later.
Withdraw while it rejects
Send fails silently, 34,239 gas
Recorded balance after
0 ETH
Fund keeps
1 ETH
Retry after it accepts
Pays nothing, 29,429 gas
Lab replayPaid on retryThe vulnerable fund pays with send and the fixed one with call. The depositor is a contract that rejects ether at first and accepts it later.
Withdraw while it rejects
Reverts with SendFailed(), 39,152 gas
Recorded balance after
1 ETH
Fund keeps
1 ETH
Retry after it accepts
Pays 1 ETH, 34,116 gas
Lab replayLender loses ETHA pool holds 1,000 tokens and 100 ETH, 0.1 ETH per token. A lender values collateral at the pool's price and lends 50%.
Attacker buys with 100 ETH
Spot price rises to 0.4 ETH
Borrowed against 100 tokens
20 ETH
Lender after
80 ETH plus collateral worth 10 ETH
Gas, one transaction
161,809
Lab replayLimit holdsA pool holds 1,000 tokens and 100 ETH, 0.1 ETH per token. A lender values collateral at the pool's price and lends 50%.
Collateral value
100 tokens at 0.1 ETH, 10 ETH
Borrow limit at 50%
5 ETH
Lender after
Owed 5 ETH against 10 ETH
Basis
Page team calculation, not a lab run

Sending before updating balances permits repeated withdrawals. Updating first preserves other users' 10 ETH, while a reentrancy guard makes the re-entering call revert.

An unchecked initializer gives a stranger ownership through the wallet's delegated call. The corrected version rejects initialization and keeps the existing ownership.

The toy Parity library loses code under older rules. Under EIP-6780, the code remains and the wallet can still pay 1 ETH.

Ignoring a failed payment erases the depositor's balance without paying. Checking the result reverts instead, preserving the balance for a later attempt.

The toy lender trusts a price manipulated within one transaction. It lends 20 ETH against collateral worth 10 ETH once the price returns.

Toy contracts, not original code, are replayed in the same lab, with incident dates drawn from public sources.

The OWASP Smart Contract Top 10 for 2025 covers access control, price oracle manipulation, logic errors, reentrancy and other failure classes. The 2026 edition, current on 2 November 2026, uses 2025 incidents and survey data. It adds arithmetic errors and proxy and upgradeability vulnerabilities, while dropping insecure randomness and denial of service. The SWC registry is no longer actively maintained, with no new entries since 2020. Its content entered the EEA EthTrust Security Levels specification.

The DAO's recursive withdrawal drained over 3.6 million ETH in June 2016. Ethereum's fork on 20 July 2016 moved funds into a withdrawal contract. Parity's 19 July 2017 incident exposed unchecked initialization through DELEGATECALL. Its 6 November 2017 library deletion froze dependent wallets. Under today's EIP-6780 rules, the same kill would leave the library's code in place. Curve pools lost over $50 million on 30 July 2023 (LlamaRisk). A Vyper compiler bug defeated reentrancy protection, so correct source code cannot rule out compiler faults.

Crypto hack losses, 2023 to 2025

The table keeps each provider's totals separate. These are crypto theft figures, not a count of losses caused only by smart contract bugs. Chainalysis values stolen funds in dollars at the time of theft. Reporting windows and later revisions also matter, so a yearly total needs its source and publication date.

YearChainalysis, funds stolenCertiK, lossesCostliest vector named
2023Chainalysis, funds stolen$1.7 billion stolen, January 2024 reportCertiK, losses$1.84 billion across 751 incidentsCostliest vector namedCertiK: private key compromises, $880.9 million in 47 incidents
2024Chainalysis, funds stolen$2.2 billion across 303 incidents, December 2024 reportCertiK, losses$2.36 billion across 760 incidents, restated as $2.45 billion in December 2025Costliest vector namedChainalysis: private key compromises, 43.8% of stolen funds. CertiK: phishing, $1.05 billion
2025Chainalysis, funds stolenOver $3.4 billion to early December, Bybit $1.5 billion of itCertiK, losses$3.35 billionCostliest vector namedCertiK: supply chain attacks, $1.45 billion in 2 incidents

Sources use different methods and revise earlier years. Chainalysis attributes 43.8% of 2024 theft value to private key compromises. From CertiK's original figures, phishing and compromised keys account for 80.6% of 2024 losses. Supply chain attacks and phishing account for 64.8% in 2025. These calculations attribute most losses to causes other than smart contract code bugs. Chainalysis' December 2025 report includes Bybit's $1.5 billion theft. CertiK's 2025 report uses a separate supply chain category, so labels should stay attached to sources.

Checks before and after deployment

Review covers the published program and the people who can control it. Different checks answer different questions, from matching deployed code to testing proposed changes. They also have limits. A smart contract audit needs to distinguish evidence about code behavior from evidence about permissions, signatures and the handling of future upgrades.

Verify published source

Source verification matches published code to deployed code. Etherscan contract verification and Sourcify expose this evidence. Partial matches ignore metadata hashes, while exact matches establish byte-for-byte equality, not freedom from flaws.

Test varied inputs

Foundry's Forge fuzzes test functions that take parameters, 256 runs by default in v1.8.6. Echidna is a fuzzer, and Halmos provides symbolic testing for EVM contracts. Fuzzing exercises inputs beyond handpicked cases.

Specify intended behavior

Formal verification can prove agreement with a specification, not that the specification is correct. Certora, KEVM and Solidity's SMTChecker support this work. An incomplete specification can still leave incorrect business assumptions untested.

Read audit limits

Audits are time-boxed, and their findings are not comprehensive. Reports and bug bounty programs provide evidence, not a guarantee. Neither removes the responsibility to write high-quality code or address the flaws found.

Map change authority

Identify who holds the admin key and which actions need multiple signatures. A timelock adds an exit window but slows emergency changes. Check ownership, initialization permissions and storage compatibility together before changing logic.

Review keys and signing

A leaked key can expose all associated funds, according to Immunefi's Q1 2025 report. Review who signs and what they approve. Blind signing authorizes an interaction without understanding its effect.

Smart contracts and the law

Programmed execution and legal enforceability answer different questions. A smart contract can perform part of an agreement without replacing the surrounding legal framework. The sources below address different jurisdictions and different uses. Their definitions should not be read as a universal rule for every blockchain program or every agreement expressed partly through code.

The Law Commission of England and Wales published its advice to government on 25 November 2021. It treats a smart legal contract as a specific type of smart contract, rather than treating every program as an agreement. Its conclusion is jurisdiction-specific: the existing framework in England and Wales can facilitate and support smart legal contracts without statutory reform. That finding does not say that code eliminates courts or external enforcement. Automated performance and legal obligations remain distinct parts of the discussion.

The EU Data Act applies from 12 September 2025. Article 2(39) defines smart contracts as programs automating agreements through electronic records with integrity and accurate chronological ordering. Article 36 covers vendors and third-party deployers of smart contracts for data sharing agreements, not every blockchain program. It requires access controls, safe termination and interruption, archiving, continuity and agreement consistency. Internal functions must stop, interrupt or reset operation. Vendors assess conformity and issue an EU declaration. Recital 104 excludes contracts used only internally.

Building and reviewing smart contracts

PixelPlex is a software engineering company founded in 2007, with blockchain work dating to 2013. We treat smart contract development as a set of linked decisions about permissions, change, costs and release evidence. The same questions also shape a review of an existing system. The five areas below show what needs to be settled, rather than assuming that writing code settles the design.

PixelPlex case icon

In the 2025 Coindepo case, we built an ERC-20 ecosystem covering token, vesting, reserve and sale distribution contracts. The implementation used audited OpenZeppelin contracts, and CertiK audited the ecosystem. The case records mainnet deployment of the tokenomics infrastructure in three weeks. That is a dated project result, not a promised schedule for another system.

  1. Scope and data

    We select reliable oracle sources early. Defining intended smart contract actions also makes clear which decisions depend on ledger state and which require information supplied from outside it.
  2. Change and control

    We plan upgradeability from the start, including storage layout and initialization rules. The design also identifies who can authorize changes and how that authority reaches the running contract.
  3. Execution and storage costs

    We decide gas costs at design time rather than leaving them until the end. Deployment choices and storage layout belong in that discussion, alongside the intended call paths.
  4. Review and findings

    Our smart contract audit reviews the whole codebase line by line and reports root causes with step-by-step mitigation. We also review contracts already live on mainnet.
  5. Verified production release

    We handle deployment as a production release, moving from test network validation to mainnet with verified bytecode. Verified bytecode shows what was deployed, not that it behaves correctly.

Discuss the decisions behind your contract design

We can discuss the scope of a build or review, including permissions, upgrade plans and the evidence needed for deployment.

Discuss your project

Smart contracts FAQ

What is a smart contract in simple terms?

A smart contract is a program stored at an address on a blockchain. It runs only when a transaction or another contract calls it. Every node executing it gets the same result. The caller pays gas. The program cannot read off-chain facts on its own.

Is a smart contract always legally binding?

A smart contract is not automatically a legal contract. The Law Commission's advice of 25 November 2021 identifies smart legal contracts as one specific type. It found that the law of England and Wales can support them without statutory reform. From 12 September 2025, EU Data Act Article 36 requires access controls, termination functions and conformity assessments for vendors of data-sharing smart contracts.

Can smart contracts be changed or deleted after deployment?

Code at an Ethereum address cannot be edited after deployment, but a proxy can point the address to new logic. Since Dencun on 13 March 2024, SELFDESTRUCT deletes code only within its creating transaction. Otherwise, it transfers ether without deleting code, storage or the account. Solana has a different control: revoking the program's upgrade authority makes its code immutable.

How much gas does deployment and use cost?

Gas for a smart contract depends on its code and the call. The 2 November 2026 escrow lab used 451,276 gas for deployment and 67,137 for a deposit. Those totals include base costs of 53,000 and 21,000 gas. Using block 26,329,574's base fee and that day's ETH price gives about $0.14 and $0.021, excluding priority fees. These are example calculations, not mainnet averages.

Can a smart contract use real-world data?

A smart contract cannot fetch real-world data on its own. A signed transaction or an oracle can provide those inputs. Price data also introduces manipulation risks. Harvest Finance's 26 October 2020 incident used flash loans to move pool prices before withdrawing vault shares. The reported loss was $33.8 million, illustrating that receiving data does not establish its reliability.

Which languages are smart contracts written in?

Smart contracts are written in languages tied to their platform. Ethereum supports Solidity and Vyper, with Yul used inside Solidity's intermediate representation pipeline. Solana uses Rust with Anchor, while Aptos and Sui use Move. Starknet uses Cairo, and Canton uses Daml. Cardano uses Plutus and Aiken, while Stacks uses Clarity. These choices describe different compilation and execution paths, not a ranking of languages.

What are the most common smart contract vulnerabilities?

The most common smart contract vulnerabilities in OWASP's 2026 list are access control failures, business logic errors, price oracle manipulation and flash loan-facilitated attacks. Others include input validation failures, unchecked external calls, arithmetic errors, reentrancy, integer overflow and proxy and upgradeability vulnerabilities. The edition, current on 2 November 2026, uses 2025 incidents and survey data.
Sources (35)

Get updates about blockchain, technologies and our company

We will process the personal data you provide in accordance with our Privacy policy. You can unsubscribe or change your preferences at any time by clicking the link in any email.

Follow us on social networks and don't miss the latest tech news

  • facebook
  • X
  • linkedin
  • instagram
Stay tuned and add value to your feed