Opcode Summary
| Property | Value |
|---|---|
| Opcode | 0x3C |
| Mnemonic | EXTCODECOPY |
| Gas | access_cost + 3 × ceil(len / 32) + mem_expansion |
| Stack Input | addr, dstOst, ost, len |
| Stack Output | (none) |
| Behavior | Copies len bytes of the bytecode at address addr starting at offset ost into memory at position dstOst. Source bytes beyond the code boundary are zero-padded. access_cost is 100 (warm) or 2,600 (cold, EIP-2929). |
Threat Surface
EXTCODECOPY is the EVM’s primary mechanism for reading another contract’s deployed bytecode into memory. It combines cross-account state access with variable-length memory writes, creating a threat surface that spans gas economics, code introspection reliability, and verification soundness.
The threat surface centers on three properties:
-
Triple dynamic gas component. EXTCODECOPY’s cost has three variable parts: the access cost (cold vs. warm, a 26× difference), the per-word copy cost (3 gas per 32-byte word), and the quadratic memory expansion cost. An attacker who controls the target address, the destination offset, or the copy length can manipulate all three components independently. Cold access to a never-touched address costs 2,600 gas (EIP-2929), large copy lengths multiply the word cost, and a high destination offset triggers quadratic memory expansion. Combined, these allow gas griefing attacks far more potent than most single opcodes.
-
Code introspection for verification is inherently unreliable. EXTCODECOPY is commonly used to inspect another contract’s bytecode for auditing, verification, or clone detection. But the bytecode at an address is not a stable identity: proxy contracts return the proxy’s minimal bytecode (not the implementation’s logic), metamorphic contracts can change their code at a fixed address, and destroyed contracts return empty bytes. Any verification system that trusts EXTCODECOPY output as proof of what a contract “does” is building on an unreliable foundation.
-
Used in code hash verification and clone patterns. EXTCODECOPY followed by KECCAK256 is the canonical pattern for on-chain bytecode hash verification (before EXTCODEHASH existed, and still used when partial bytecode comparison is needed). It’s also central to EIP-1167 minimal proxy clone detection and ERC-7760 upgradeable proxy verification. Flaws in how this pattern handles edge cases — empty contracts, constructor-time addresses, proxy indirection — propagate into broken security assumptions across the ecosystem.
Smart Contract Threats
T1: Gas Griefing via Cold Access + Large Copy + Memory Expansion (High)
EXTCODECOPY combines three independent gas amplifiers in a single opcode:
-
Cold access (2,600 gas): If the target address hasn’t been accessed in the current transaction, EIP-2929 charges 2,600 gas instead of 100. An attacker can force cold access by targeting addresses that the caller hasn’t previously touched.
-
Per-word copy cost (3 × ceil(len / 32)): The copy cost scales linearly with the amount of bytecode requested. Copying the maximum contract size (24,576 bytes per EIP-170) costs 3 × 768 = 2,304 gas in copy fees alone.
-
Quadratic memory expansion: Writing to a high memory offset triggers memory expansion with cost
(words² / 512) + (3 × words). A destination offset of 0x100000 (~1 MB) would cost billions of gas in expansion, far exceeding any block gas limit.
When a contract accepts user-supplied parameters for EXTCODECOPY — such as a “code inspector” function that takes an address, offset, and length — an attacker can construct inputs that maximize all three cost components. In meta-transaction or relayer contexts where the gas payer differs from the parameter supplier, this becomes a direct financial attack.
// Vulnerable: user controls all three gas amplifiers
function copyExternalCode(address target, uint256 dstOst, uint256 ost, uint256 len)
external view returns (bytes memory)
{
bytes memory result = new bytes(len);
assembly {
extcodecopy(target, add(result, 0x20), ost, len)
}
return result;
}Why it matters: Unlike CODECOPY (which reads only the executing contract’s code), EXTCODECOPY adds the cold access cost, making it ~26× more expensive on first access. In loops iterating over multiple addresses, each new target incurs the cold penalty.
T2: Misleading Code Inspection Behind Proxies (High)
EXTCODECOPY reads the bytecode stored at the target address. For proxy contracts, this returns the proxy’s minimal forwarding bytecode — not the implementation contract’s logic. This creates a fundamental gap between what EXTCODECOPY reveals and what the contract actually does:
-
EIP-1167 minimal proxies return ~45 bytes of forwarding code. EXTCODECOPY shows only the DELEGATECALL stub, revealing nothing about the implementation’s logic, access control, or vulnerabilities.
-
Transparent proxies (EIP-1967) return the proxy’s dispatch logic and admin functions. The actual business logic lives at a separate implementation address stored in a specific storage slot. EXTCODECOPY of the proxy address reveals the proxy framework, not the protocol logic.
-
Diamond proxies (EIP-2535) route different function selectors to different facet contracts. EXTCODECOPY of the diamond address shows only the dispatch table, not any facet’s code.
On-chain verification systems that use extcodecopy(target, ...) followed by keccak256 to verify a contract’s identity will match the proxy’s hash, not the implementation’s. If the implementation is upgraded (even legitimately), the proxy’s code hash remains unchanged, masking the logic change from any EXTCODECOPY-based monitor.
Why it matters: Over 54% of Ethereum contracts are proxies (per 2024 Proxion research). Any on-chain security tool that relies on EXTCODECOPY to understand what a contract does will be wrong for the majority of contracts.
T3: Bytecode Verification Bypass via Metamorphic Contracts (Critical)
Metamorphic contracts exploit CREATE2 + SELFDESTRUCT (pre-Dencun) to deploy different code at the same address. EXTCODECOPY faithfully returns whatever bytecode is currently deployed, meaning:
- Pre-destruction: EXTCODECOPY returns the benign audited code. Verification passes.
- Post-destruction: EXTCODECOPY returns zero bytes (no code). Address appears empty.
- Post-redeployment: EXTCODECOPY returns the new malicious code. Address is the same, code is different.
Any system that performed an EXTCODECOPY-based verification at time T1 and trusts the result at time T2 is vulnerable. This includes:
- Governance proposals that verify proposal contract code at submission time but execute at a later block
- Token approval flows where a user inspects a contract’s code before approving, but the code changes between inspection and the approval’s use
- On-chain registries that hash bytecode via EXTCODECOPY during registration and assume the hash remains valid
The Tornado Cash governance attack (May 2023) demonstrated this in production: the proposal contract passed verification, was destroyed, and was redeployed with malicious code at the same address.
Why it matters: EXTCODECOPY’s output is a point-in-time snapshot, not a permanent guarantee. Post-Dencun (EIP-6780), SELFDESTRUCT can only clear code in the same transaction as deployment, significantly limiting this pattern on L1 — but L2s and alt-EVMs that haven’t adopted EIP-6780 remain fully vulnerable.
T4: Zero-Padding of Destroyed or Empty Contracts (Medium)
When EXTCODECOPY targets an address with no code — an EOA, a destroyed contract, or a not-yet-deployed CREATE2 address — all requested bytes are zero-padded. This produces a valid memory write of all zeros, with no revert or error signal.
Contracts that use EXTCODECOPY for verification and check the result without first confirming the target has code (EXTCODESIZE > 0) can be misled:
-
Hash of zeros:
keccak256of zero bytes produces a deterministic hash (keccak256("")for length 0, or a hash of zero-filled buffer for length > 0). If a verification system compares against a precomputed hash without length validation, a destroyed contract may produce an unexpected match or bypass. -
Post-SELFDESTRUCT same-transaction: Within the same transaction as a SELFDESTRUCT (even pre-Dencun), EXTCODECOPY still returns the original code — code is not cleared until the transaction ends. This creates a TOCTOU window where EXTCODECOPY returns stale data within the same transaction.
-
Constructor-time targets: If a contract is mid-construction (its constructor is still executing), EXTCODECOPY targeting that address returns zero bytes. Systems that verify code during the constructor’s execution see nothing.
Why it matters: Silent zero-padding means EXTCODECOPY never fails, even for invalid targets. Callers must explicitly check EXTCODESIZE before trusting the result.
T5: On-Chain Code Analysis Limitations (Medium)
Contracts that use EXTCODECOPY for on-chain bytecode analysis face fundamental limitations:
-
Opcode scanning is gas-prohibitive. Scanning the full bytecode of a 24 KB contract (maximum size) costs at minimum 2,604 gas (access + copy) plus quadratic memory expansion. Analyzing multiple contracts in a single transaction quickly exhausts the gas limit, making comprehensive on-chain code analysis impractical.
-
Bytecode doesn’t reveal runtime behavior. EXTCODECOPY returns raw bytecode, but a contract’s behavior depends on storage, calldata, and execution context. Two contracts with identical bytecode but different storage have entirely different behavior. On-chain bytecode comparison can detect code identity but not behavioral equivalence.
-
Compiler metadata confounds comparison. Solidity appends CBOR-encoded metadata (compiler version, source hash, settings) to deployed bytecode. Two contracts compiled from identical source with different compiler settings produce different bytecode. EXTCODECOPY-based comparisons must strip metadata to avoid false negatives.
-
Immutable variables are inlined. Solidity
immutablevalues are patched directly into bytecode by the compiler. Two instances of the same contract with different constructor arguments have different bytecode, making EXTCODECOPY-based identity checks fail unless the immutable regions are masked.
Why it matters: On-chain code analysis via EXTCODECOPY appears powerful but is fraught with false positives and false negatives. Security decisions based on bytecode comparison without understanding these limitations lead to brittle systems.
Protocol-Level Threats
P1: Cold Access DoS Amplification (Medium)
EXTCODECOPY was a key opcode exploited during the 2016 Shanghai DoS attack on Ethereum. Before EIP-150 (Tangerine Whistle), EXTCODECOPY cost only 20 gas but required an expensive disk read to fetch the target contract’s bytecode from the state trie. Attackers crafted transactions that invoked EXTCODECOPY thousands of times targeting different addresses, each requiring a separate disk seek. Blocks took 20-60 seconds to validate instead of the normal ~1 second.
EIP-150 raised EXTCODECOPY’s base cost from 20 to 700 gas. EIP-2929 (Berlin, 2021) further refined pricing: 2,600 gas for the first (cold) access to an address in a transaction, 100 gas for subsequent (warm) accesses. Despite these increases, EXTCODECOPY remains one of the more expensive opcodes per invocation, and a transaction that targets many cold addresses in sequence still has an outsized impact on node I/O.
P2: Consensus Safety — Zero-Padding Boundary (Low)
When ost + len exceeds the target contract’s code size, the EVM zero-pads the out-of-bounds region. All client implementations must agree on the boundary between real code bytes and zero padding. While no known consensus bug has occurred from EXTCODECOPY specifically, the combination of cross-account state reads and variable-length zero-padding is a non-trivial implementation surface, particularly for:
- zkEVM circuits: EXTCODECOPY requires proving the target’s bytecode against the state trie, handling variable-length copies, and inserting correct zero-padding — all within a ZK circuit.
- L2 EVM equivalence: L2s claiming EVM equivalence must replicate exact zero-padding behavior, including for addresses with no code.
P3: State Size and Verkle Tree Implications (Low)
EXTCODECOPY reads bytecode from the state trie, which stores contract code under the account’s code hash. Under the current Merkle Patricia Trie structure, this requires fetching the code from the database, which can be up to 24 KB per contract. The upcoming Verkle tree migration (EIP-6800) will store code in 31-byte chunks across multiple tree leaves, fundamentally changing the I/O pattern of EXTCODECOPY. Gas costs may need further adjustment to reflect the new access pattern.
Edge Cases
| Edge Case | Behavior | Security Implication |
|---|---|---|
| Target is an EOA (no code) | All len bytes are zero-padded | No revert; callers must check EXTCODESIZE first to avoid operating on meaningless zeros |
| Target is mid-construction | Returns zero bytes (no code deployed yet) | Verification during constructor returns false negatives; code appears absent |
ost + len exceeds target’s code size | Out-of-bounds bytes are zero-padded | Partial code + trailing zeros may break hash comparisons or bytecode parsers |
len = 0 | No-op; no memory write, no memory expansion | Safe, but callers expecting side effects (memory writes) may have logic errors |
Target was SELFDESTRUCTed (same tx, pre-Dencun) | Returns original code (state not yet committed) | TOCTOU: code appears present but will be gone after transaction completes |
Target was SELFDESTRUCTed (different tx, pre-Dencun) | Returns zero bytes | Address looks empty; cached verification from before destruction is stale |
| Target is an EIP-1167 minimal proxy | Returns the ~45-byte proxy stub | Reveals the forwarding pattern and implementation address, but not the implementation’s logic |
| Target with EIP-7702 delegated EOA | Returns the delegated bytecode | EOA with delegation appears as a contract; bytecode inspection shows delegation code, not “EOA” |
| Cold vs. warm access | Cold: 2,600 gas; warm: 100 gas | First access to any address in a transaction is 26× more expensive; gas estimation must account for access order |
dstOst triggers massive memory expansion | Quadratic cost, likely OOG revert | Griefing vector if destination offset is user-influenced |
Real-World Exploits
Exploit 1: Ethereum Shanghai DoS Attack — Network Crippled (September 2016)
Root cause: EXTCODECOPY (and EXTCODESIZE) were dramatically underpriced at 20 gas, while each invocation required an expensive disk read to fetch the target’s bytecode from the state trie.
Details: In September 2016, attackers crafted transactions that called EXTCODESIZE and EXTCODECOPY tens of thousands of times per transaction, targeting different contract addresses. Each call forced a separate disk seek to load the target’s code from LevelDB storage. At 20 gas per call, a transaction could execute ~50,000 EXTCODE* operations within the gas limit, requiring ~50,000 disk I/O operations. Block validation times ballooned from ~1 second to 20-60 seconds. The attack reduced block production rate by 2-3× and caused widespread node instability, though no consensus failure occurred.
EXTCODECOPY’s role: EXTCODECOPY was one of the two primary opcodes used in the attack (alongside EXTCODESIZE). Its low gas cost relative to the actual I/O cost — fetching up to 24 KB of bytecode per call from disk — made it a more potent DoS vector than EXTCODESIZE (which only reads a length value). The attacker exploited the opcode’s I/O amplification: 20 gas in, kilobytes of disk I/O out.
Impact: Network throughput degraded for over a month. Emergency response included EIP-150 (Tangerine Whistle, October 2016) which raised EXTCODECOPY cost from 20 to 700 gas, EIP-161 (Spurious Dragon, November 2016) which cleaned up empty accounts, and ultimately EIP-2929 (Berlin, 2021) which introduced the cold/warm access framework.
References:
- Ethereum Foundation: Transaction Spam Attack Next Steps
- ethos.dev: Ethereum’s Shanghai Attacks
- EIP-150: Gas Cost Changes for IO-Heavy Operations
Exploit 2: Tornado Cash Governance Takeover — $2.17M via Metamorphic Code Swap (May 2023)
Root cause: Governance system verified proposal contract code at submission time but executed it at a later block. The attacker used a metamorphic contract pattern to swap the code between verification and execution.
Details: The attacker deployed a benign proposal contract via a factory using CREATE2 → CREATE chain. The factory’s CREATE2 address was deterministic, and the factory’s nonce determined the proposal’s CREATE address. The community verified the proposal’s code (via EXTCODECOPY equivalent — reading and hashing the bytecode) and voted to approve it.
After the vote passed, the attacker called SELFDESTRUCT on both the proposal contract and its factory, clearing their code. The attacker then redeployed the factory to the same CREATE2 address (nonce reset to 0), which then deployed a malicious proposal contract at the exact same CREATE address. The governance system still recognized the address as approved and executed the malicious code with full governance privileges.
The malicious code granted the attacker 1.2 million fake TORN governance votes (vs. ~700K legitimate votes), giving complete DAO control. The attacker drained 483,000 TORN tokens ($2.17M), sold ~470,000 TORN for ETH, and laundered 572 ETH through Tornado Cash itself.
EXTCODECOPY’s role: The governance system’s verification was fundamentally an EXTCODECOPY-based operation — reading the proposal’s bytecode and confirming it matched expectations. But EXTCODECOPY returns a point-in-time snapshot. Between verification and execution, the code was destroyed and replaced. Any EXTCODECOPY-based check that ran during the governance vote would have returned the benign code; the same check at execution time would have returned the malicious code.
Impact: $2.17M stolen; complete governance takeover of a major DeFi protocol. Demonstrated that on-chain code verification is meaningless if not repeated at execution time.
References:
- Composable Security: Understanding the Tornado Cash Governance Attack
- pcaversaccio: Tornado Cash Exploit PoC
- Halborn: The Tornado Cash Hack Explained
Exploit 3: EIP-1884 Gas Repricing Debate — EXTCODECOPY as Storage Arbitrage (2019)
Root cause: After EIP-150 raised EXTCODECOPY’s cost to 700 gas, it became cheaper to store data in contract bytecode and read it via EXTCODECOPY than to use SLOAD (which cost 200 gas per 32-byte slot but required separate slots for each value).
Details: Developers discovered that deploying a “data contract” with sequential bytes as bytecode and reading it via EXTCODECOPY was cheaper than storing the same data in storage slots and reading via SLOAD. For large sequential data reads (e.g., lookup tables, configuration arrays), the per-byte cost of EXTCODECOPY was ~22 gas (700 base + 3 per word, amortized), while SLOAD cost 200 gas per 32-byte slot (~6.25 gas/byte but with no amortization benefit for sequential access).
EIP-1884 (Istanbul, December 2019) addressed this by increasing SLOAD from 200 to 800 gas and BALANCE from 400 to 700 gas. EIP-2929 (Berlin, 2021) further refined EXTCODECOPY pricing with the cold/warm framework, adding a 2,600 gas cold access penalty that eliminated the arbitrage for first-access reads.
EXTCODECOPY’s role: The opcode was the read mechanism for the “code-as-storage” pattern. Its relatively low per-word cost (3 gas per 32 bytes) compared to SLOAD’s per-slot cost created an economic incentive to externalize data into bytecode, distorting the intended gas market.
Impact: No direct financial exploit, but the mispricing created perverse incentives that complicated state management and necessitated multiple hard fork gas adjustments. The pattern demonstrated that EXTCODECOPY’s gas model must account for its full I/O cost, not just computation.
References:
- EIP-1884: Repricing for Trie-Size-Dependent Opcodes
- EIP-2929: Gas Cost Increases for State Access Opcodes
- GitHub: EIP1884 EXTCODECOPY Pricing Discussion
Attack Scenarios
Scenario A: Cold Access Gas Griefing via Multi-Address EXTCODECOPY
contract CodeVerifier {
// Relayer pays gas for meta-transactions
function verifyContracts(address[] calldata targets, bytes32[] calldata expectedHashes)
external view returns (bool)
{
for (uint256 i = 0; i < targets.length; i++) {
uint256 size;
assembly { size := extcodesize(targets[i]) }
bytes memory code = new bytes(size);
assembly {
// Each unique address costs 2,600 gas (cold access)
// 100 addresses = 260,000 gas just in access costs
// Plus copy costs and memory expansion for each
extcodecopy(targets[i], add(code, 0x20), 0, size)
}
require(keccak256(code) == expectedHashes[i], "hash mismatch");
}
return true;
}
}
// Attack: Attacker submits 100 addresses of large contracts (24 KB each)
// Gas cost: 100 × (2,600 access + 2,304 copy + memory expansion)
// Relayer pays ~500,000+ gas; attacker pays nothingScenario B: Proxy Bytecode Inspection Misleading On-Chain Verification
contract OnChainAudit {
mapping(bytes32 => bool) public approvedCodeHashes;
function approveContract(address target) external onlyAdmin {
bytes32 codeHash;
uint256 size;
assembly {
size := extcodesize(target)
let ptr := mload(0x40)
extcodecopy(target, ptr, 0, size)
codeHash := keccak256(ptr, size)
}
// BUG: If target is a proxy, this hashes the proxy bytecode,
// not the implementation. Implementation can be silently upgraded.
approvedCodeHashes[codeHash] = true;
}
function isApproved(address target) external view returns (bool) {
bytes32 codeHash;
uint256 size;
assembly {
size := extcodesize(target)
let ptr := mload(0x40)
extcodecopy(target, ptr, 0, size)
codeHash := keccak256(ptr, size)
}
// Proxy code hash stays the same even after implementation upgrade
// Malicious implementation passes this check
return approvedCodeHashes[codeHash];
}
}Scenario C: Metamorphic Contract Bypassing EXTCODECOPY Verification
```mermaid
sequenceDiagram
participant Attacker
participant Factory
participant Contract (Address X)
participant Verifier
Attacker->>Factory: deploy benign contract via CREATE2
Factory->>Contract (Address X): init code deploys benign runtime
Verifier->>Contract (Address X): EXTCODECOPY + KECCAK256
Note right of Verifier: Hash matches expected value ✓
Attacker->>Contract (Address X): selfdestruct()
Note right of Contract (Address X): Code cleared
Attacker->>Factory: update runtime source to malicious code
Attacker->>Factory: redeploy to Address X via CREATE2 (same salt)
Factory->>Contract (Address X): init code deploys MALICIOUS runtime
Verifier->>Contract (Address X): EXTCODECOPY + KECCAK256
Note right of Verifier: Hash is DIFFERENT but verifier<br/>cached the old approval
Note right of Verifier: Stale approval allows malicious execution
### Scenario D: Zero-Padding Exploitation on Destroyed Contract
```solidity
contract TokenGate {
bytes32 public constant REQUIRED_HASH = 0xabc...;
function gatedAction(address target) external {
uint256 size;
assembly { size := extcodesize(target) }
// BUG: No check that size > 0
// If target was destroyed, size = 0, code is empty
bytes memory code = new bytes(size);
assembly {
extcodecopy(target, add(code, 0x20), 0, size)
}
// keccak256 of empty bytes = keccak256("") = 0xc5d2...
// If REQUIRED_HASH was accidentally set to keccak256("")
// (e.g., uninitialized or computed from empty deployment),
// destroyed contracts pass the gate
require(keccak256(code) == REQUIRED_HASH, "invalid code");
_execute(target);
}
}
Mitigations
| Threat | Mitigation | Implementation |
|---|---|---|
| T1: Cold access gas griefing | Bound the number of addresses and total copy length | require(targets.length <= MAX_TARGETS) and cap individual copy sizes; pre-warm addresses via ACCESS_LIST transactions (EIP-2930) |
| T1: Memory expansion griefing | Validate destination offset and length before assembly | require(dstOst + len <= MAX_MEMORY_SIZE) before any extcodecopy call |
| T2: Proxy code inspection | Resolve implementation address before inspection | For EIP-1967 proxies, read the implementation slot (0x360894...) via SLOAD then EXTCODECOPY the implementation address |
| T2: Code identity via proxy | Use EXTCODEHASH on the implementation, not the proxy | Combine proxy detection (match known proxy bytecode patterns) with implementation resolution |
| T3: Metamorphic code swap | Verify code hash at execution time, not just approval time | require(target.codehash == expectedHash) immediately before every interaction, not just at registration |
| T3: Post-Dencun L1 protection | Rely on EIP-6780 restricting SELFDESTRUCT | On L1, metamorphic patterns are largely broken post-Dencun; verify L2s have adopted equivalent protections |
| T4: Zero-padding false matches | Always check EXTCODESIZE before EXTCODECOPY | require(extcodesize(target) > 0) before copying; reject empty targets explicitly |
| T4: Constructor timing | Don’t verify contracts mid-deployment | Wait for deployment transaction to be confirmed before running EXTCODECOPY-based verification |
| T5: On-chain analysis limitations | Strip compiler metadata before comparison | Mask the last N bytes (CBOR metadata) and immutable regions when comparing bytecode hashes |
| General: Gas cost surprises | Use access lists (EIP-2930) to pre-warm addresses | Include target addresses in the transaction’s access list to convert cold (2,600) to warm (100) access costs |
Compiler/EIP-Based Protections
- EIP-150 (Tangerine Whistle, 2016): Raised EXTCODECOPY cost from 20 to 700 gas, directly addressing the Shanghai DoS attack vector.
- EIP-2929 (Berlin, 2021): Introduced cold/warm access framework. EXTCODECOPY charges 2,600 gas for first access to an address, 100 for subsequent. Eliminates the I/O cost arbitrage that enabled the Shanghai attack.
- EIP-2930 (Berlin, 2021): Optional access lists allow transactions to pre-declare addresses they will touch, converting cold accesses to warm at a lower cost (1,900 gas per address in the list vs. 2,600 at runtime).
- EIP-6780 (Dencun, 2024): SELFDESTRUCT only clears code in the same transaction as deployment. This breaks the metamorphic contract pattern on L1, making EXTCODECOPY-based verification more reliable since code at an address can no longer be silently swapped between transactions.
- EXTCODEHASH (EIP-1052, Constantinople, 2019): Provides a gas-efficient alternative to EXTCODECOPY + KECCAK256 for code identity checks. Costs only the access charge (100/2,600) with no copy or memory expansion cost.
Severity Summary
| Threat ID | Category | Severity | Likelihood | Real-World Precedent |
|---|---|---|---|---|
| T1 | Smart Contract | High | Medium | Shanghai DoS (2016), relayer gas griefing |
| T2 | Smart Contract | High | High | 54% of contracts are proxies; universal applicability |
| T3 | Smart Contract | Critical | Medium (Low post-Dencun on L1) | Tornado Cash governance ($2.17M, 2023) |
| T4 | Smart Contract | Medium | Medium | Constructor edge cases, post-SELFDESTRUCT zero-padding |
| T5 | Smart Contract | Medium | Low | Compiler metadata false negatives, immutable variable mismatches |
| P1 | Protocol | Medium | Low (mitigated) | Shanghai DoS attack (2016), required 3 hard forks to fully address |
| P2 | Protocol | Low | Low | zkEVM audit findings on zero-padding circuits |
| P3 | Protocol | Low | Low | Verkle tree migration planning |
Related Opcodes
| Opcode | Relationship |
|---|---|
| EXTCODESIZE (0x3B) | Returns the byte length of code at an address. Typically used before EXTCODECOPY to allocate the correct memory buffer. Shares the same cold/warm access cost and the same vulnerability to constructor-time and post-SELFDESTRUCT edge cases. |
| EXTCODEHASH (0x3F) | Returns keccak256 of an address’s code. Gas-efficient alternative to EXTCODECOPY + KECCAK256 for code identity checks — no copy cost or memory expansion. Preferred for simple hash verification. |
| CODECOPY (0x39) | Copies the executing contract’s own bytecode to memory. Self-referential variant of EXTCODECOPY with no cross-account access cost. Shares the memory expansion griefing vector and zero-padding semantics. |
| CALLDATACOPY (0x37) | Copies calldata to memory with identical zero-padding semantics. Shares the memory expansion cost model and the per-word copy charge. No cross-account access cost. |