Solidity Interview Questions: Junior, Mid, and Senior Answers Anchored to Real Exploit Postmortems and 2024-2026 Tooling
Solidity interviews in 2026 test three things at once: your command of language internals and EVM mechanics (storage layout, calldata vs memory, function visibility, gas model), your awareness of the named exploits that senior interviewers cite by date and mechanism (DAO, Parity, Wormhole, Ronin, Nomad, Euler, Curve), and your judgment about tooling — specifically whether you can articulate why Foundry displaced Hardhat as the default testing framework and what the Trail of Bits 246-finding audit corpus says about where vulnerabilities actually cluster. This guide organizes questions by career tier and anchors every key answer to the primary sources senior interviewers actually cite.
- What is Solidity, and how does it differ from Vyper, Yul, and Huff?
- Walk me through what happens when you call a Solidity function — from transaction to opcode.
- Explain the difference between storage, memory, and calldata.
- Walk me through the ERC standards every Solidity developer should know.
- What’s the difference between public, external, internal, and private functions in Solidity?
- When do you mark a function payable, view, or pure?
- When would you use a mapping vs an array in Solidity?
- Walk me through the ERC-20 standard — what functions are required?
- What are events for, and what does indexed do?
- How does Solidity compute the storage slot for a value in mapping(address => uint256)?
- Walk me through call vs delegatecall vs staticcall — when do you reach for each?
- You have a function that costs 200K gas. What knobs do you turn to reduce it?
- Walk me through writing a Foundry test with cheatcodes — vm.prank, vm.deal, vm.warp.
- Why use OpenZeppelin’s SafeERC20 wrapper instead of calling ERC-20 functions directly?
- Walk me through the DAO hack mechanism — what made it possible, and what would have prevented it?
- Walk me through both Parity multisig incidents (July + November 2017).
- Walk me through three major bridge hacks and what they reveal about cross-chain architecture.
- Walk me through the Euler Finance attack — what does the donateToReserves exploit teach about health-score logic?
- Walk me through the ERC-4337 account abstraction architecture.
- What’s the most common vulnerability class in real audits — not what the textbooks say?
Solidity Hiring in 2026: What Actually Changed
This guide is for software engineers and Solidity developers targeting Solidity developer, smart contract engineer, or protocol engineer roles at DeFi protocols (Uniswap, Aave, Compound, MakerDAO, Lido, Curve), L2s (Optimism, Arbitrum, zkSync, StarkNet, Polygon zkEVM), audit firms (Trail of Bits, OpenZeppelin, ConsenSys Diligence, Sherlock, Code4rena), or infrastructure companies (Chainlink, Coinbase, Kraken). It is not a “become a blockchain millionaire” bootcamp resource — it assumes you’ve shipped contracts or are actively studying the EVM.
Several structural shifts in the 2024-2026 hiring cycle are worth internalizing before your first screen:
- Foundry is now the default testing framework. Foundry’s Solidity-native
forge testrunner, cheatcodes (vm.prank,vm.deal,vm.warp), and integrated fuzzing are table stakes at most DeFi protocol and audit firm interviews. Saying “Hardhat is standard” is an immediate seniority signal — in the wrong direction. - ERC-4337 account abstraction adoption is real and accelerating. The EntryPoint singleton was deployed March 1, 2023 at the same address across all EVM chains. As of early 2025, over 26 million smart wallets and 170 million UserOperations have been processed. Wallet infrastructure companies probe this architecture specifically.
- EIP-4844 blob transactions launched in March 2024 (Dencun upgrade), enabling L2 rollups to post data as cheap ephemeral blobs rather than calldata. Engineers who haven’t tracked Dencun are behind at L2 teams.
- Audit-as-a-service has matured. Code4rena and Sherlock competitive audit platforms are now legitimate career paths. Senior candidates are expected to know how these incentive structures differ from traditional engagements.
- Solidity 0.8 made SafeMath obsolete. Since version 0.8.0, arithmetic overflow and underflow revert by default. Use
unchecked {}blocks deliberately for performance-critical code where overflow cannot occur. - ZK-rollup skill commands a premium. zkSync, StarkNet, Polygon zkEVM, Scroll, and Linea have created demand for Cairo, Circom, and zk-SNARK fluency above the base Solidity market.
Salary signal (industry-reported aggregator data, hedged): mid-to-senior Solidity developers cluster in the $130K–$280K+ base range in US markets; protocol-team and audit-firm roles typically command a 30–50% premium over generalist blockchain engineering.
What Solidity Interviews Actually Test in 2026
Interviewers at DeFi protocols, audit firms, and infrastructure companies cycle through four question types:
- Solidity language + EVM internals: storage layout and slot derivation, gas model (cold vs warm SLOAD, SSTORE pricing, 63/64 sub-call rule), function visibility, calldata vs memory vs storage, ABI encoding, immutable vs constant.
- ERC standards + DeFi mechanics: ERC-20/721/1155/4626/4337 — not just naming them but knowing the rounding rules in ERC-4626, the UserOperation/Bundler/EntryPoint architecture in ERC-4337, and why AMM spot prices are manipulable but TWAPs are not.
- Named-incident exploit postmortems: Senior interviewers at Trail of Bits, ConsenSys Diligence, and Code4rena expect exact dates, dollar amounts, and mechanism specifics. Approximate answers are scored accordingly.
- Audit tooling + methodology fluency: Foundry’s test architecture, Slither static analysis, Echidna property-based fuzzing, the Trail of Bits 246-finding empirical corpus, and the Checks-Effects-Interactions (CEI) pattern.
What is Solidity, and how does it differ from Vyper, Yul, and Huff?
Concept: Language landscape and compilation target | Difficulty: foundational | Stage: phone screen / intro technical
Solidity is a statically-typed, curly-brace language compiling to EVM bytecode — the dominant smart contract language across Ethereum mainnet and all major L2s. Vyper is a Python-syntax language that prioritizes auditability by removing inheritance and function overloading; it is what Curve Finance used, making the July 2023 Vyper compiler reentrancy bug particularly consequential. Yul is the EVM intermediate representation Solidity uses internally; developers write it inside assembly {} blocks for gas-critical paths. Huff is a macro assembler closer to raw opcodes, used for maximum efficiency in specialized contexts.
What they’re really probing: Whether you understand the compilation pipeline and can reason about tradeoffs between expressiveness, auditability, and gas cost. Mentioning the Curve/Vyper incident signals you track compiler-level risk, not just Solidity bugs.
Walk me through what happens when you call a Solidity function — from transaction to opcode.
Concept: EVM execution model | Difficulty: foundational | Stage: technical screen
When a transaction arrives, the EVM decodes the first 4 bytes of calldata as the function selector — the first 4 bytes of keccak256(functionSignature). The dispatcher compares that selector against a jump table and routes to the matching function body. Input parameters are ABI-decoded from the remaining calldata: static types (uint, bool, address) are inline; dynamic types (bytes, string, dynamic arrays) are represented as an offset. Execution happens on the EVM stack (256-bit words); storage reads cost 2,100 gas cold or 100 gas warm per EIP-2929. The output is ABI-encoded and returned in the return data buffer.
What they’re really probing: Your mental model of ABI encoding and the gas model. Follow-ups probe selector collision, the fallback/receive distinction, and cold-vs-warm storage cost implications.
Explain the difference between storage, memory, and calldata.
Concept: Data location semantics and gas cost | Difficulty: foundational | Stage: technical screen
Storage is persistent on-chain state in 32-byte slots; reads and writes are the most expensive EVM operations. Memory is a non-persistent scratch space scoped to the current call frame, cleared after each external call. Calldata is read-only and references raw input bytes without copying to memory — using calldata instead of memory for external function parameters saves gas, especially for large arrays.
What they’re really probing: Gas awareness and when the calldata-vs-memory choice has a meaningful cost impact. The correct default for external functions that don’t mutate inputs is always calldata.
Walk me through the ERC standards every Solidity developer should know.
Concept: ERC/EIP landscape | Difficulty: foundational | Stage: phone screen / technical screen
The minimum set spans tokens, vaults, account abstraction, and proxy patterns. ERC-20 defines fungible tokens with six mandatory functions and two events. ERC-721 covers NFTs; ERC-1155 handles multi-token (fungible and non-fungible in one contract). ERC-4626 is the tokenized vault standard with four entry points and strict rounding rules that prevent share inflation attacks. ERC-4337 specifies account abstraction via a UserOperation mempool and a singleton EntryPoint deployed March 1, 2023. On the infrastructure side: EIP-1559 (base fee mechanic), EIP-4844 (blob transactions, March 2024), and EIP-7702 (EOA smart-wallet delegation, Pectra May 2025) are the most-probed EIPs at senior screens.
What they’re really probing: Whether you know why each standard exists, not just its name. Explaining ERC-4626 rounding direction in terms of share inflation attack vectors is a senior-tier answer.
ERC Standards & EIPs Reference (2026)
Standard and EIP questions are among the highest-signal junior-to-mid prompts. Interviewers want to know whether you can articulate why each one exists — not just cite the number.
| Standard | What It Defines | When to Use | Common Interview Probe |
|---|---|---|---|
| ERC-20 | Fungible tokens — 6 mandatory functions + 2 events | Any interchangeable token: governance, stablecoins, LP shares | “Why does the allowance double-spend race condition exist and how do you mitigate it?” |
| ERC-721 | Non-fungible tokens — each unique by (contractAddress, tokenId) | Collectibles, certificates, unique in-game assets | “What does safeTransferFrom do that transferFrom doesn’t?” |
| ERC-1155 | Multi-token — fungible + non-fungible in one contract | Games, marketplaces needing batch ops to save gas | “How does safeBatchTransferFrom reduce gas vs separate ERC-721 transfers?” |
| ERC-4626 | Tokenized vault standard — deposit/mint/withdraw/redeem | Yield-bearing vaults: Aave aTokens, Yearn vaults, lending markets | “Why must rounding favor the vault? What attack does incorrect rounding enable?” |
| ERC-4337 | Account abstraction — UserOperation + EntryPoint singleton (Mar 1, 2023) | Smart wallets, sponsored transactions, session keys — 26M+ wallets as of early 2025 | “Walk me through the UserOperation lifecycle from bundler submission to on-chain execution.” |
| ERC-1967 | Standard storage slots for proxy implementation + admin addresses | Any upgradeable proxy — prevents storage collision between proxy and implementation | “What happens if you don’t use EIP-1967 slots in a proxy contract?” |
| ERC-2612 | Permit — EIP-712 off-chain signature for gasless approvals | UX flows that want to avoid a separate approve transaction | “What’s the replay attack risk in a permit signature and how does the deadline mitigate it?” |
| EIP-1559 | Base fee mechanic — baseFee burned + priorityFee to block producer | Understanding gas price prediction; affects MEV and UX design | “Where does the base fee go, and how does it adjust block-to-block?” |
| EIP-4844 | Blob transactions (Dencun, March 2024) — ephemeral data availability for L2s | L2 rollup data posting; blobs are ~18-day TTL, not EVM-accessible | “Why can’t the EVM read blob data directly? What opcode does it expose?” |
| EIP-7702 | EOA smart-wallet delegation — EOAs set code temporarily within a transaction (Pectra, May 2025) | EOA-to-smart-wallet migration without full deployment; superseded EIP-3074 | “How does EIP-7702 differ from ERC-4337? Which requires a new transaction type?” |
Junior-Tier Questions: Language Fundamentals (0–2 Years)
Junior screens probe whether you can write correct Solidity — not just copy-paste from docs. The differentiator at this level is knowing not just what a keyword does but why it exists and what happens when you get it wrong.
What’s the difference between public, external, internal, and private functions in Solidity?
Concept: Function visibility and calldata handling | Difficulty: junior | Stage: technical screen
Solidity’s four visibility specifiers control both who can call a function and how arguments are handled. public functions are callable from anywhere — externally via transaction/call and internally via expression; Solidity generates an automatic getter for public state variables. external functions can only be called from outside the contract (not internally without explicit this.f()); crucially, external functions receive arguments directly from calldata without copying to memory, making them cheaper for functions that accept large arrays. internal functions are callable only from within the contract and derived contracts — the equivalent of a protected method. private restricts access to the defining contract only, excluding derived contracts.
What they’re really probing: Whether you understand the calldata-vs-memory implication of public vs external. Candidates who explain only the access-control dimension but miss the gas difference are scored as mid-junior.
When do you mark a function payable, view, or pure?
Concept: Function state mutability | Difficulty: junior | Stage: technical screen
These mutability specifiers constrain what a function can do to the chain state and are enforced at both compile time and runtime. Mark a function payable when it must accept ETH — without this modifier, the EVM reverts any call that sends value. Mark a function view when it reads from state but does not modify it; view functions can be called off-chain for free (no gas when called externally via eth_call). Mark a function pure when it neither reads from nor writes to state — it depends only on its input parameters. Marking a function pure or view that actually modifies state will cause a compile-time error in modern Solidity.
What they’re really probing: Whether you understand the gas model and the external-call implications. A payable function that fails to reject unwanted ETH is a logic bug; a function incorrectly marked view when it writes state will compile but will revert when called via eth_call.
When would you use a mapping vs an array in Solidity?
Concept: Storage data structure tradeoffs | Difficulty: junior | Stage: technical screen
The choice maps directly to access patterns. Use a mapping when you need O(1) key lookup (balance by address, allowance by owner+spender); mappings are not iterable and every key defaults to zero. Use an array when you need ordered iteration or enumeration — dynamic arrays support push/pop and store elements starting at keccak256(baseSlot). A common pattern is a mapping for O(1) lookup plus a parallel array for enumeration. Deleting from an array’s middle requires swap-and-pop, not in-place removal.
What they’re really probing: Storage layout awareness and iteration cost at scale. Unbounded loops over arrays are a denial-of-service vector when the array can grow without bound.
Walk me through the ERC-20 standard — what functions are required?
Concept: ERC-20 interface + failure modes | Difficulty: junior | Stage: technical screen
The ERC-20 standard defines six mandatory functions: totalSupply, balanceOf, transfer, transferFrom, approve, and allowance, plus two events: Transfer and Approval. The allowance double-spend race condition: if an owner changes allowance from non-zero to non-zero, a spender can front-run and spend both; the mitigation is to reset to zero first. Non-standard behavior: tokens like USDT do not return a boolean on transfer — this is why SafeERC20 exists.
What they’re really probing: Whether you know the failure modes, not just the interface. Mentioning the USDT return-bool issue signals you’ve deployed against heterogeneous token contracts.
What are events for, and what does indexed do?
Concept: Events, logs, and off-chain indexing | Difficulty: junior | Stage: technical screen
Events emit log entries that are stored in the transaction receipt but are not accessible to the EVM at execution time — they are intended for off-chain consumption by dApps, indexers (The Graph, Alchemy), and monitoring systems. Each event generates a log entry with up to three indexed topics (the first topic is always the event signature hash) and a non-indexed data payload. Indexed parameters are Bloom-filter-searchable on the node; non-indexed parameters are cheaper to emit but cannot be filtered. The practical rule: index fields you will filter or search by (addresses, IDs), leave high-cardinality or large data (amounts, strings) non-indexed.
What they’re really probing: Whether you understand the log vs state distinction. Events are not state — they cannot be read by contracts. A candidate who says “I’d check an event in my contract” has a fundamental misunderstanding.
Mid-Tier Questions: EVM Internals and Tooling (2–5 Years)
Mid-level screens expect fluency in EVM gas mechanics, proxy patterns, and the testing toolchain. The differentiation is in specificity — a general answer about gas optimization does not score as well as naming the specific opcodes and EIP numbers involved.
How does Solidity compute the storage slot for a value in mapping(address => uint256)?
Concept: Storage slot derivation | Difficulty: mid | Stage: technical screen
Per the Solidity storage layout spec, the value at key k in a mapping declared at storage slot p is stored at keccak256(h(k) . p), where h(k) pads the key to 32 bytes (for value types) and . denotes concatenation. For a mapping(address => uint256), the address key is left-padded to 32 bytes, concatenated with the 32-byte slot position, then hashed. This derivation is why mappings are not iterable (the slot locations are pseudorandom) and why they cannot be deleted wholesale. Storage layout inheritance follows C3 linearization: base contract variables occupy lower-numbered slots; derived contract variables follow. Senior signal: being able to derive a specific slot from a code path, then verify it with a debugger or cast storage read.
What they’re really probing: Whether you can reason about storage layout for security work — storage collisions in proxy patterns and storage layout verification in upgrades are both based on this derivation.
Walk me through call vs delegatecall vs staticcall — when do you reach for each?
Concept: Low-level call mechanics and proxy patterns | Difficulty: mid | Stage: technical screen
call executes the callee in its own storage context with its own msg.sender and msg.value — the standard way to interact with external contracts. delegatecall executes the callee’s code but in the caller’s storage context, preserving the caller’s msg.sender and msg.value — this is the foundation of proxy patterns (proxy storage, implementation logic). The critical implication: storage layout between proxy and implementation must be compatible; a mismatch corrupts state. staticcall is a read-only call that reverts if the callee attempts any state modification — used for safe oracle queries and view function calls from assembly. Per samczsun, a common sharp edge: using msg.value inside a delegatecall reads the wrong context — the value is not forwarded, so you silently read zero.
What they’re really probing: The proxy pattern prerequisite. Every follow-up will be about UUPS vs Transparent Proxy, storage collisions, or implementation initialization — all of which require this foundation.
You have a function that costs 200K gas. What knobs do you turn to reduce it?
Concept: Gas optimization strategy | Difficulty: mid | Stage: technical screen
Work through the gas model systematically. First, check storage access patterns: cold SLOAD costs 2,100 gas and cold SSTORE (zero→nonzero) costs 20,000 gas per EIP-2929; caching storage variables in memory for functions that read them multiple times is often the single highest-ROI optimization. Second, check calldata vs memory for external function parameters — calldata avoids the copy. Third, pack storage variables: Solidity packs multiple variables smaller than 32 bytes into a single slot if declared contiguously; reordering struct fields to enable packing can eliminate entire SSTORE operations. Fourth, use custom errors instead of require strings — error strings cost 68 gas per byte of calldata. Fifth, consider unchecked arithmetic for loops where overflow cannot occur. Finally, run forge snapshot to baseline and measure each change — optimization without measurement is guesswork.
What they’re really probing: Whether you have a systematic mental model or whether you’ve memorized a list of tips. The ability to prioritize by impact (storage access first) and to measure (forge snapshot) is the senior signal within a mid-tier answer.
Walk me through writing a Foundry test with cheatcodes — vm.prank, vm.deal, vm.warp.
Concept: Foundry testing architecture and cheatcodes | Difficulty: mid | Stage: technical screen
Foundry tests are written in Solidity and extend forge-std/Test.sol. The test contract has access to the vm cheatcode interface, which gives direct control over EVM state: vm.prank(address) sets msg.sendervm.startPrank/vm.stopPrank scope it across multiple calls. vm.deal(address, amount) sets the ETH balance of an address. vm.warp(timestamp) sets block.timestamp. Use vm.expectRevert() before a call to assert it reverts with the expected error selector. Foundry’s fuzzer runs property-based tests when a parameter is declared as an input — testFuzz_deposit(uint256 amount) generates random inputs automatically. Running forge test --gas-report produces a per-function gas usage table, and forge snapshot creates a gas baseline for regression testing.
What they’re really probing: Whether you’ve actually used Foundry vs Hardhat in production. Candidates who describe Hardhat-style JS test setup are immediately flagged as behind the current toolchain at most protocol teams.
Why use OpenZeppelin’s SafeERC20 wrapper instead of calling ERC-20 functions directly?
Concept: ERC-20 non-standard behaviors and safe wrappers | Difficulty: mid | Stage: technical screen
The ERC-20 standard specifies that transfer and approve return a boolean, but several widely-used tokens — most notably USDT and BNB — do not return a boolean at all, or return false on failure instead of reverting. Calling these tokens directly with the typed IERC20 interface causes a revert on the return data decoding step, even when the transfer succeeded. OpenZeppelin’s SafeERC20 wrapper uses a low-level call, checks that the call did not revert, and then checks the return data: if it is empty (non-returning token) the call is treated as successful; if it returns false, the wrapper reverts. Use safeTransfer instead of transfer for any contract that handles arbitrary ERC-20 tokens — protocol contracts that handle user-deposited tokens almost always fall into this category.
What they’re really probing: Whether you’ve run into the USDT return-bool edge case in practice, or whether you’re reasoning about it for the first time. Real protocol engineers have all hit this in production.
Senior-Tier Questions: Exploit Postmortems and Architecture (5+ Years)
Senior screens demand precision. “Walk me through the DAO hack” is not an invitation to say “reentrancy.” It is an invitation to state the exact function name, the exact mechanism, the exact date, the dollar amount, and the architectural lesson. The questions below are calibrated to the specificity level senior interviewers at audit firms and protocol teams expect.
Walk me through the DAO hack mechanism — what made it possible, and what would have prevented it?
Concept: Reentrancy and the CEI pattern | Difficulty: senior | Stage: technical screen / system design
The DAO hack occurred on June 17, 2016. The attacker exploited a reentrancy vulnerability in the splitDAO() function — not withdrawRewardFor() as sometimes incorrectly cited. The function sent ETH to the caller before updating the internal balance mapping. Because the attacker’s contract implemented a fallback function that recursively called splitDAO(), the attacker drained approximately ~$60M (3.6 million ETH) before the balance was ever decremented. The exploit was possible because the contract violated the Checks-Effects-Interactions (CEI) pattern: external calls (Interactions) must always come last, after all state changes (Effects). The incident led directly to the Ethereum hard fork creating ETH and ETC. Prevention: apply CEI strictly, use OpenZeppelin’s ReentrancyGuard (nonReentrant modifier) for any function that sends ETH or calls untrusted external contracts, and consider transient storage (EIP-1153) for cheaper reentrancy locks in Cancun-targeted contracts.
What they’re really probing: Not just that you know about reentrancy, but that you know the exact mechanism, exact function name, and exact corrective pattern. Interviewers at audit firms will stop a candidate who says $50M or names the wrong function.
Walk me through both Parity multisig incidents (July + November 2017).
Concept: Access control and proxy pattern safety | Difficulty: senior | Stage: technical screen
These are two distinct incidents with different mechanisms and different impact types. July 19, 2017 ($30M stolen): Per the OpenZeppelin postmortem, Parity’s library contract exposed a public initWallet() function with no ownership guard. An attacker called it on three deployed multisig wallets, re-initialized ownership to themselves, and drained the funds. Lesson: initialization functions must be guarded against re-execution; proxy pattern implementations must treat unguarded public initializers as a critical vulnerability class. November 6, 2017 (~$150M+ permanently frozen): Per the second OpenZeppelin postmortem, a developer accidentally called initWallet() on the uninitialized singleton library contract itself, claimed ownership, then called kill() — triggering selfdestruct on the shared library. All wallets delegating to that library via delegatecall lost access permanently. No funds were stolen; they are irrecoverable. Lesson: never deploy a shared library contract with a selfdestruct path; initialize all implementation contracts at deployment.
What they’re really probing: The ability to distinguish theft from freezing, to name the exact functions, and to derive the correct architectural lesson for each incident independently.
Walk me through three major bridge hacks and what they reveal about cross-chain architecture.
Concept: Bridge security architecture and validator models | Difficulty: senior | Stage: system design
Three 2022 incidents define the canonical bridge security curriculum. Wormhole (February 2, 2022, $326M): Per Halborn’s analysis, the bug was on the Solana side — guardian signature verification used a deprecated load_instruction_at function that did not validate the instruction came from the System Program; the attacker forged signatures and minted 120,000 wETH without depositing collateral. This is not a Solidity vulnerability — it is a cross-chain architecture lesson: validate the instruction source, not just its format. Ronin (March 23, 2022, $625M): Per the Ronin postmortem, Lazarus Group compromised 5 of 9 validator keys — 4 from Sky Mavis and 1 from the Axie DAO via a legacy allowance never revoked. Multisig threshold security collapses when key custody is centralized. Nomad (August 1, 2022, $190M): Per Immunefi’s analysis, an upgrade set committedRoot to bytes32(0); the validation code treated a zero proof as automatically valid. Once discovered, no technical sophistication was required — hundreds of copycat addresses exploited it simultaneously. Lesson: zero/default values in security-critical state must be invalid sentinels, not default-valid states.
What they’re really probing: Whether you can distinguish infrastructure-layer bugs (Wormhole’s Solana-side) from smart contract bugs (Nomad’s initialization failure) from operational security failures (Ronin’s key custody). Attributing Wormhole to a Solidity bug signals a gap.
Walk me through the Euler Finance attack — what does the donateToReserves exploit teach about health-score logic?
Concept: DeFi invariant testing and health-score vulnerabilities | Difficulty: senior | Stage: technical / system design
On March 13, 2023, Euler Finance lost approximately $197M across DAI, USDC, stETH, and WBTC. Per the Euler Finance root cause analysis, the exploit used EToken::mint to create a leveraged position (up to ~10x) — depositing borrowed funds back as collateral repeatedly. The attacker then called donateToReserves(), which transferred eTokens (deposit receipts) to the reserve but left the corresponding dTokens (debt receipts) in the attacker’s account. This created an unbacked liability: the attacker now had debt without matching collateral, making the position liquidatable below its health threshold. The attacker then liquidated their own unhealthy position at a profit. The attacker subsequently returned most of the funds — a rare positive resolution. The architectural lesson: invariant checks (health factor > 1) must be enforced after every state-changing operation — including functions that appear benign like donations. ERC-4626 vault designs that combine leverage with reserve accounting must model all cross-function interactions, not just the primary deposit/borrow paths. Use Echidna property-based fuzzing to test that health invariants hold across arbitrary sequences of operations.
What they’re really probing: Whether you understand that security invariants must hold across the entire state space, not just across individual function calls in isolation. The “donation as attack vector” frame is the key insight.
Walk me through the ERC-4337 account abstraction architecture.
Concept: Account abstraction architecture | Difficulty: senior | Stage: technical / system design
ERC-4337 introduces a parallel transaction mempool — the UserOperation mempool — that operates without protocol-level changes. A UserOperation is a data structure (not a transaction) that encodes the intent: sender wallet address, call data, gas limits, signature, and paymaster data. Bundlers aggregate UserOperations from the mempool into a single standard transaction and submit it to the EntryPoint singleton contract — deployed at the same deterministic address on all EVM chains on March 1, 2023. The EntryPoint validates each UserOperation by calling the sender wallet’s validateUserOp function (verifying signature and nonce), optionally involving a Paymaster (a contract that sponsors gas on behalf of the user), and executing the operation. As of early 2025, ERC-4337 has accumulated over 26 million deployed smart wallets and over 170 million UserOperations. The EntryPoint singleton concentrates risk: a critical bug in it affects all ERC-4337 wallets globally. Note: EIP-7702 (Pectra, May 2025) provides an alternative path for EOAs to act as smart wallets within a single transaction without the full ERC-4337 infrastructure.
What they’re really probing: Whether you understand why bundlers exist (to aggregate UserOps without protocol changes), how paymasters enable gasless UX, and where the single point of failure sits (the EntryPoint singleton).
What’s the most common vulnerability class in real audits — not what the textbooks say?
Concept: Empirical audit data vs SWC Registry zeitgeist | Difficulty: senior | Stage: technical screen
Per Trail of Bits’ published 246-finding audit corpus, the most common vulnerability class by count is data validation errors at 36% — missing input bounds checks, incorrect type assumptions, unvalidated external inputs. Reentrancy, which every junior interview prep guide lists first, accounts for only 4 of 246 findings (~1.6%). Access control issues account for 10% but carry disproportionate dollar impact because they tend to be complete ownership takeovers. Trail of Bits also found that approximately 78% of high-severity findings are automatically detectable with static analysis and fuzzing — meaning manual human review should focus on the remaining 22%, not on re-running Slither. Important caveat: this corpus is 2019-vintage data — the most recently published TOB audit summary of this format. The data-validation modal class likely persists in 2025-2026 audits, but the precise reentrancy share may differ as the protocol ecosystem has shifted toward more complex DeFi primitives that create new access-control and accounting-logic failure modes. The junior answer “always check reentrancy first” reflects the SWC Registry zeitgeist — a classification system that froze around 2020 — not the statistical distribution senior auditors observe in practice.
What they’re really probing: Whether you’ve read the actual empirical literature on vulnerability distributions or whether you’re reciting the SWC Registry top-10. Citing the specific 246-finding study with correct numbers is one of the sharpest differentiators between a candidate who has done audit-track prep and one who has done only dev-track prep.
Named-Exploit Quick Reference for Interview Recall
Full narratives are in the senior-tier section above. Use this table for cross-reference and rapid recall during prep.
| Incident | Date | Mechanism | Amount | 2-Sentence Mitigation |
|---|---|---|---|---|
| DAO Hack | June 17, 2016 | Reentrancy via splitDAO() recursive call before balance update |
~$60M (3.6M ETH) | Apply Checks-Effects-Interactions pattern: update all state before any external call. Add a nonReentrant modifier to any function that sends ETH or calls untrusted contracts. |
| Parity — July 2017 | July 19, 2017 | Unguarded public initWallet() allowed attacker to reset ownership |
~$30M stolen | Guard all initialization functions with an initializer modifier (runs only once). Treat any unguarded public initializer in a library or implementation as a critical finding. |
| Parity — Nov 2017 | November 6, 2017 | User triggered kill() on singleton library via initWallet() → selfdestruct froze all dependent wallets |
~$150M+ permanently frozen | Never include selfdestruct in a shared library contract. Initialize all implementation contracts at deployment time. |
| Wormhole Bridge | February 2, 2022 | Solana-side guardian signature bypass via deprecated load_instruction_at (NOT a Solidity/EVM bug) |
$326M | Validate the source of every cross-chain instruction, not just its format. Never rely on deprecated verification functions in bridge guardian logic. |
| Ronin Network | March 23, 2022 | 5-of-9 validator keys compromised via social engineering (4 Sky Mavis + 1 Axie DAO legacy key) | $625M | Diversify validator custody across independent organizations; revoke all stale or legacy permissions immediately. Multisig threshold security collapses if key custody is centralized. |
| Nomad Bridge | August 1, 2022 | Upgrade set committedRoot to bytes32(0); zero proof treated as automatically valid |
$190M | Treat zero/default values as invalid sentinels in security-critical state variables with require(value != 0) initialization guards. Test upgrade scripts as rigorously as production code. |
| Euler Finance | March 13, 2023 | donateToReserves() after leveraged position violated health-score invariant |
$197M (returned) | Enforce health-factor invariants after every state-changing operation, including donation functions. Use property-based fuzzing (Echidna) to test invariants across arbitrary operation sequences. |
| Curve / Vyper | July 30, 2023 | Vyper compiler bug (v0.2.15–0.3.0) bypassed @nonreentrant guard (NOT a Solidity issue) |
~$70M | Audit compiler version against known CVEs before deployment. Apply multi-compiler cross-checks and formal verification on critical DeFi infrastructure using older compiler versions. |
Red-Flag Answers (And What They Signal)
Senior interviewers at audit firms and protocol teams use these answer patterns as instant seniority filters. Knowing what not to say is as important as knowing the right answer.
-
“I’d just use SafeMath.”
This answer signals you haven’t tracked Solidity past version 0.7.x. Since Solidity 0.8.0, arithmetic overflow and underflow revert by default — SafeMath is not just unnecessary, it adds gas overhead. The correct answer in 2026 is to useunchecked {}blocks deliberately in performance-critical paths where overflow provably cannot occur. -
“I always start with reentrancy.”
This reveals you’re working from the SWC Registry zeitgeist, not the empirical data. Per the Trail of Bits 246-finding corpus (2019-vintage), reentrancy is only 4 of 246 findings (~1.6%). Data validation errors are the modal class at 36%. Starting with reentrancy is correct for famous historical exploits; it is not the prioritization senior auditors use on real engagements. -
“I’d write the contract from scratch.”
Protocol-level contracts almost always start from OpenZeppelin’s battle-tested library. Writing from scratch for a standard token, access control system, or proxy pattern reintroduces bugs the ecosystem has already discovered and patched. The correct answer is: start from audited primitives, extend deliberately, and audit the extension layer. -
“Hardhat is the standard.”
This is 2022 thinking. By 2024-2026, Foundry is the default at most DeFi protocol teams and audit firms. Calling Hardhat “the standard” signals you haven’t surveyed the current ecosystem. The follow-up will ask about Foundry cheatcodes. -
“I haven’t used Foundry.”
At protocol teams and audit firms, this is an instant flag that requires immediate mitigation. The expected answer is either “I use Foundry daily” or “I’m actively learning Foundry after working in Hardhat” — with specific Foundry features mentioned. The gap between “haven’t used it” and “here’s the cheatcode for block time manipulation” is read as weeks of prep neglect. -
“I’d ask ChatGPT to audit this.”
LLMs surface known patterns but cannot reason about protocol-specific invariants, cross-contract interaction paths, or economic attack surfaces. Audit accountability belongs to the engineer. The correct answer names a methodology: Slither for automated detection, Echidna for invariant fuzzing, manual review of the access control and external call graphs, then a human auditor for the final layer.
Questions to Ask Your Interviewer (2026-Aware)
Asking good reverse questions is itself a senior signal — it demonstrates you understand the engineering and operational context of the role, not just the Solidity syntax. Tailor by employer type.
For DeFi protocol teams (Uniswap, Aave, Compound, MakerDAO, Lido, Curve)
- What percentage of your test suite runs in Foundry vs Hardhat today, and what’s the migration plan?
- How does the team handle invariant testing in production — do you have a running Echidna or Medusa suite, or is fuzzing limited to CI?
- When a security researcher finds a vulnerability via your bug bounty or a Code4rena contest, what’s the incident response process and how long does a patch-to-deploy cycle typically take?
For L2 teams (Optimism, Arbitrum, zkSync, StarkNet)
- After EIP-4844 Dencun, what’s the current fraction of your L2 data costs that come from blobs vs calldata, and how has that changed your sequencer architecture?
- How do you approach formal verification for the proving layer — do you use internal teams or external audit firms?
- What does the upgrade governance process look like for consensus-level changes — multisig, DAO vote, or something else?
For audit firms (Trail of Bits, OpenZeppelin, ConsenSys Diligence, Code4rena, Sherlock)
- What’s the typical split between automated tooling coverage (Slither/Echidna) and manual review time on a standard engagement?
- How does the team handle post-audit monitoring — do you follow up with clients after deployment to see if the findings were addressed correctly?
- What’s the most technically interesting vulnerability class you’ve encountered in the last 12 months that doesn’t get enough coverage in the public discourse?
For infrastructure companies (Chainlink, Coinbase, Kraken)
- How does the team balance Solidity/EVM-native development against the need to support non-EVM chains in the same product surface?
- What’s the security review process for a new oracle feed or network integration — internal red team, external audit, or both?
- How has EIP-4844 affected your data availability assumptions for cross-chain message verification?
6-Week Solidity Interview Prep Roadmap
This roadmap targets software engineers with general programming experience aiming for junior-to-mid Solidity roles, with senior topics layered in weeks 5-6. If you already write Solidity professionally, compress weeks 1-2 into a review day and spend more time on postmortems and audit tooling.
Weeks 1–2: Language Foundations
- Solidity by Example — work through the storage, visibility, events, and proxy sections actively (write the code, don’t read it).
- OpenZeppelin Contracts 5.x docs — read the access control, token, and security sections; understand
ReentrancyGuard,Ownable2Step,AccessControl, andPausable. - Implement ERC-20 and ERC-721 from scratch against the spec, then compare your implementation to OpenZeppelin’s — the diff will highlight every subtle assumption you made.
- Read the ERC-4626 spec and understand the four entry points and the rounding direction rules.
Weeks 3–4: EVM Internals and Tooling
- Work through the Foundry book completely — install Foundry, write a non-trivial test suite using
vm.prank,vm.deal,vm.warp, andvm.expectRevert; run the gas snapshot tool. - Study the storage layout spec until you can derive any mapping or array slot from first principles. Verify your derivation with
cast storageon a local Anvil fork. - Read EIP-2929 (cold/warm SLOAD costs) and EIP-1559 (base fee mechanic) fully — not summaries, the actual EIP text.
- Install Slither (
pip install slither-analyzer) and run it on a real protocol codebase (Uniswap v3, Compound v2, or Aave v3 are all open source). Categorize every finding. - Read the OpenZeppelin proxy docs — understand Transparent, UUPS, and Beacon patterns and their tradeoffs.
Weeks 5–6: Senior Topics — Postmortems, Architecture, and Audit Empirics
- Read every postmortem in the table above in full source form (not summaries). Recall each incident’s date, function name, mechanism, and mitigation without notes.
- Read the Trail of Bits 246-finding corpus post in full. Internalize the data-validation vs reentrancy distribution and the 78% automatic detectability figure.
- Read the ERC-4337 spec and walk through the full UserOperation lifecycle without notes.
- Browse 3-5 past Code4rena contest reports for protocols similar to your target employer; note recurring finding categories.
- Read samczsun’s blog — the
msg.value-in-delegatecallpost and the cross-chain message validation posts are the most interview-relevant.
Where to Go From Here
The Solidity interview landscape has a clear fault line: the questions that separate junior from senior candidates are not harder syntax questions — they are postmortem recall questions and empirical audit-data questions that most candidates simply have not read the source material on. The DAO hack mechanism, the Parity library self-destruct, the Nomad zero-root initialization bug, and the Trail of Bits data-validation finding distribution are the exact points where senior interviewers stop asking follow-ups from candidates who know them and start accelerating interview loops. The 6-week roadmap above covers the primary source material; the postmortem table and standards reference provide the quick-recall layer for the week before your screen. As EIP-7702, ERC-4337 adoption, and L2-native contract patterns mature through 2026, expect the senior tier to shift toward cross-chain invariant reasoning and ZK-circuit integration — but the postmortem and tooling foundation will remain the baseline that every senior screen starts from.