Skip to content
RED SAGSkills

Blockchain Developer interview questions and answers

30 questions interviewers really ask, with what a strong answer covers. Read them, then test yourself in a timed mock interview.

Start a Blockchain Developer mock interview

Easy questions easy

1. Explain what a blockchain is to someone who is not technical.

A strong answer covers:

  • Shared ledger copied across many computers
  • Blocks linked by cryptographic hashes make history tamper-evident
  • Consensus decides which transactions are added
  • No single owner; rules enforced by code
  • Gives a simple real-world example
Show model answer

A blockchain is like a shared notebook of transactions that thousands of computers around the world keep identical copies of. Every few seconds, a new page, called a block, is added with a batch of transactions. Each page includes a fingerprint, called a hash, of the previous page, so if someone tries to change an old entry, all later fingerprints stop matching and everyone can see the tampering. The computers follow a set of rules, called consensus, to agree on which new page is valid; Ethereum uses proof of stake, where validators lock up ETH and lose some if they cheat. Because no single company owns the notebook, people who do not trust each other can still agree on who owns what. Smart contracts are small programs stored in this notebook that run automatically, like a vending machine that pays out when conditions are met.

2. What is a smart contract, and what are its main limitations?

A strong answer covers:

  • Program deployed on a blockchain that runs deterministically
  • Holds state and funds and executes when called by transactions
  • Code is public and usually immutable
  • Cannot access off-chain data directly; needs oracles
  • Every operation costs gas; storage is expensive
Show model answer

A smart contract is a program deployed to a blockchain at its own address. It stores state, like token balances, and can hold funds. It runs only when someone sends a transaction or another contract calls it, and every node executes it and must get the same result, so it is deterministic. Its main strength is that the rules are public and enforced by the network, so no one can secretly change them. The limitations come from the same design. Code is usually immutable, so bugs are hard to fix unless you planned an upgrade path. Every operation costs gas, and storing data on chain is expensive. Contracts cannot call web APIs or know real-world facts like a stock price; they need oracles. They cannot run on a timer by themselves either; someone must trigger them. And because code is public, attackers can study it.

3. What is the ERC-20 standard and what are its key functions and events?

A strong answer covers:

  • Standard interface for fungible tokens
  • balanceOf, totalSupply, transfer
  • approve, allowance, transferFrom for delegated spending
  • Transfer and Approval events
  • Why standards matter for wallets and exchanges
Show model answer

ERC-20 is a standard interface for fungible tokens, meaning every unit is the same, like rupees or shares. Because every token follows the same interface, wallets, exchanges and DeFi apps can support any new token without custom code. The key read functions are totalSupply, balanceOf(account) and allowance(owner, spender), plus optional name, symbol and decimals. transfer(to, amount) moves tokens from the caller. For contracts to spend tokens on a user's behalf, the user calls approve(spender, amount), and the spender later calls transferFrom(from, to, amount). The standard events are Transfer and Approval, which wallets and indexers use to track balances. In practice I build tokens on OpenZeppelin's audited implementation. I also handle non-standard tokens carefully, for example ones that do not return a bool, using SafeERC20.

4. What is the difference between proof of work and proof of stake?

A strong answer covers:

  • Both are consensus mechanisms to agree on blocks
  • Proof of work: miners spend computation and electricity
  • Proof of stake: validators lock tokens and are chosen to propose and attest
  • Slashing penalises misbehaviour in proof of stake
  • Ethereum moved to proof of stake in 2022, using far less energy
Show model answer

Both are ways for a decentralised network to agree on the next block without a central authority. In proof of work, used by Bitcoin, miners compete to solve a hard computational puzzle. The winner adds the block and earns a reward. Security comes from cost: rewriting history would require more computing power than the rest of the network, but it uses a lot of electricity. In proof of stake, which Ethereum switched to at the Merge in 2022, validators lock up cryptocurrency as a stake, 32 ETH per validator on Ethereum. The protocol selects validators to propose blocks and others attest to them. If a validator acts dishonestly, for example by signing two conflicting blocks, part of its stake is slashed. Proof of stake uses over 99% less energy and enables faster finality, though it brings its own questions about stake concentration.

5. Describe the basic architecture of a decentralised application (dApp).

A strong answer covers:

  • Smart contracts hold core logic and state on chain
  • Frontend connects through a wallet
  • RPC provider to read chain data and send transactions
  • Indexer for querying historical events
  • Off-chain storage such as IPFS for large files
Show model answer

At the core are smart contracts deployed on a chain, holding the critical state and rules, like balances or NFT ownership. Users interact through a web or mobile frontend, usually React with libraries like wagmi and viem. The frontend connects to the user's wallet, such as MetaMask or a smart wallet, which signs transactions so the app never sees private keys. Reads and transaction submissions go through an RPC provider like a node service or the wallet itself. Since reading history directly from the chain is slow, an indexer such as The Graph or a custom service listens to contract events and stores them in a database for fast queries, like "all trades by this user". Large files, like NFT images, live off chain on IPFS or Arweave, with only their hash or link on chain. Some apps also have a normal backend for things like notifications.

6. How does a crypto wallet work, and what is the role of the private key and the seed phrase?

A strong answer covers:

  • A wallet manages keys; funds live on the chain, not in the wallet
  • The private key signs transactions; the address is derived from the public key
  • The seed phrase is a backup from which all keys are derived (HD wallets)
  • Whoever has the key or phrase controls the funds, so it must never be shared
  • Hardware wallets and smart contract wallets add safety
Show model answer

A wallet does not actually hold coins. The balances live on the blockchain, and the wallet manages the keys that control them. The private key is a secret number used to sign transactions, and the public key and address are derived from it, so anyone can check a signature without knowing the secret. Most wallets are hierarchical deterministic: a 12 or 24 word seed phrase generates every private key, so the phrase is the real backup. Anyone who gets the phrase or a key can move all the funds, and there is no reset button, so it must never be typed into websites or shared with support. For larger amounts I recommend a hardware wallet, which signs inside a separate device, or a smart account with a multisig, spending limits and recovery options.

7. How are transaction fees calculated on Ethereum today?

A strong answer covers:

  • Fee = gas used times the price per gas
  • Base fee set by the protocol and burned; it rises and falls with demand
  • Priority fee (tip) goes to the validator
  • Users set max fee and max priority fee; unused amount is not charged
  • L2s charge a small execution fee plus a share of L1 data cost
Show model answer

Every operation in the EVM costs a fixed amount of gas, and the fee is the gas used multiplied by the price per gas. Since EIP-1559, the price has two parts. The base fee is set by the protocol for each block, goes up when blocks are more than half full and down when they are emptier, and it is burned. The priority fee, or tip, goes to the validator and helps you get included faster. In a transaction you set a gas limit, a max fee per gas and a max priority fee; you pay base fee plus tip, never more than the max, and you only pay for gas actually used. On Layer 2s fees are much lower: you pay for L2 execution plus your share of the cost of posting data to Ethereum, which is cheap now that rollups use blobs.

8. Compare ERC-20, ERC-721 and ERC-1155. When would you use each?

A strong answer covers:

  • ERC-20 is for fungible tokens where every unit is the same
  • ERC-721 is for unique tokens, each ID has one owner
  • ERC-1155 handles many token types, fungible and non-fungible, in one contract
  • ERC-1155 supports batch transfers and saves gas
  • Safe transfer callbacks in 721 and 1155 need care
Show model answer

ERC-20 is the standard for fungible tokens, where every unit is interchangeable, like a stablecoin or governance token. It defines balanceOf, transfer, approve, transferFrom and allowance, plus Transfer and Approval events. ERC-721 is for non-fungible tokens: each token ID is unique and has exactly one owner, which suits art, tickets or property deeds, and it has metadata through tokenURI. ERC-1155 is a multi-token standard where one contract manages many IDs, and each ID can have any supply, so a game can have gold coins and unique swords together. It supports batch transfers and balance queries, which cuts gas a lot. I pick ERC-20 for currencies, ERC-721 when each item is truly unique and needs wide marketplace support, and ERC-1155 for games or editions. With 721 and 1155, I remember that safe transfers call the receiver, which is a reentrancy point.

9. What are Layer 2 networks, and why are they important for Ethereum?

A strong answer covers:

  • Ethereum L1 has limited throughput and higher fees
  • L2 rollups execute transactions off L1 and post data and proofs to L1
  • Optimistic rollups use fraud proofs and a challenge window; ZK rollups use validity proofs
  • Security is inherited from Ethereum, with some trust in sequencers and upgrade keys
  • Blobs (EIP-4844) made L2 data much cheaper
Show model answer

Ethereum mainnet is very secure but can only process a limited number of transactions, so fees rise when demand is high. Layer 2 networks, mostly rollups like Arbitrum, Optimism, Base, zkSync and Starknet, run transactions on their own chain and post compressed data back to Ethereum. Optimistic rollups assume batches are valid and allow anyone to submit a fraud proof during a challenge window, which is why withdrawals take about a week. ZK rollups post a validity proof, so withdrawals can be faster once the proof is verified. Because the data and proofs live on Ethereum, users get much of its security at a fraction of the cost, especially since blobs were added. The trade-offs are trust in the sequencer for ordering and liveness, upgrade keys held by teams, and bridging between chains.

10. What are events in Solidity, and how do you use them in a real project?

A strong answer covers:

  • Events write logs to the transaction receipt, cheaper than storage
  • Contracts cannot read logs; they are for off-chain consumers
  • Up to three indexed parameters can be filtered efficiently
  • Frontends and indexers like The Graph use them for history and updates
  • Emit an event for every important state change
Show model answer

An event is a way for a contract to write a log entry into the transaction receipt, for example Transfer(from, to, amount). Logs are much cheaper than storage, but contracts cannot read them, so they are meant for off-chain tools. Up to three parameters can be marked indexed, which puts them in topics so nodes can filter by them, such as all transfers to one address. In a real project I emit an event for every important state change, like deposits, withdrawals, role changes and parameter updates. The frontend listens to them for live updates, and an indexer such as a subgraph or a custom service stores them in a database for history and analytics. Good events are also important for monitoring and incident response, because alerts usually watch for unusual events.

Medium questions medium

1. What is a reentrancy attack and how do you prevent it?

A strong answer covers:

  • External call gives control to another contract before state is updated
  • Attacker re-enters and repeats the action, such as withdrawing again
  • Checks-effects-interactions pattern
  • Reentrancy guards (nonReentrant)
  • Pull payments and awareness of cross-function and read-only reentrancy
Show model answer

Reentrancy happens when a contract makes an external call, for example sending ETH, before it finishes updating its own state. The receiver is a contract whose receive or fallback function calls back into the original function. Because the balance has not been set to zero yet, the attacker withdraws again and again. This was the cause of The DAO hack in 2016. The main defence is the checks-effects-interactions pattern: first validate inputs, then update state, and only then make external calls. I also add a reentrancy guard like OpenZeppelin's nonReentrant, or the transient storage version, on functions that move funds. Reentrancy can also cross functions, where the attacker re-enters a different function that shares state, or be read-only, where another protocol reads a stale view. Pull payment patterns and careful handling of token callbacks, like ERC-777 or ERC-721 safeTransfer hooks, also reduce risk.

2. What techniques do you use to reduce gas costs in Solidity contracts?

A strong answer covers:

  • Minimise storage reads and writes; cache in memory
  • Pack storage variables
  • Use calldata, immutable and constant
  • Custom errors instead of revert strings
  • Avoid unbounded loops; use events instead of storage for logs
  • Measure with gas reports and do not sacrifice security
Show model answer

Storage is the most expensive part, so I start there. I read a storage variable once into a local variable instead of multiple times, and write only when the value actually changes. I pack small variables like uint64 and bool into the same 32-byte slot and order struct fields to fit. Values that never change are constant or immutable, which avoids storage reads. External function parameters use calldata instead of memory. I use custom errors instead of long revert strings, and emit events for data that only off-chain apps need instead of storing it. I avoid unbounded loops and use mappings over arrays when I do not need iteration. In tight loops with safe bounds, unchecked increments help a little. I always measure with forge snapshot or gas reports, and never trade away readability or security for tiny savings, especially since Layer 2 execution is already cheap.

3. How do you test a smart contract before deploying it to mainnet?

A strong answer covers:

  • Unit tests for every function and revert path
  • Fuzz and invariant tests
  • Mainnet fork tests with real integrations
  • Static analysis tools like Slither
  • Testnet deployment, audits and bug bounties
  • Deployment scripts and verification tested too
Show model answer

I write unit tests in Foundry for every function, including all revert paths and access control, checking events and balances. Then fuzz tests that run functions with random inputs, and invariant tests that call random sequences of functions and check properties that must always hold, such as total shares matching total deposits or the protocol never owing more than it holds. For integrations with tokens, oracles or DEXs, I run fork tests against a mainnet snapshot so the behaviour is real, including odd tokens like fee-on-transfer ones. I run static analysis with Slither and check coverage. Deployment scripts are tested on a fork and a public testnet like Sepolia, and contracts are verified on the explorer. For anything holding real value, an external audit, sometimes formal verification of key properties, and a bug bounty come before launch, plus monitoring and a pause plan after.

4. How do upgradeable smart contracts work, and what are the trade-offs?

A strong answer covers:

  • Proxy holds state and delegates calls to an implementation
  • Transparent, UUPS and beacon proxy patterns
  • Initializer functions instead of constructors
  • Storage layout compatibility rules
  • Trust and governance trade-off: who can upgrade
  • Timelocks and multisig for upgrades
Show model answer

An upgradeable contract is split in two. Users talk to a proxy contract, which holds all the state and funds and forwards each call using delegatecall to an implementation contract that contains the logic. To upgrade, an admin points the proxy to a new implementation, and the state stays in place. Common patterns are Transparent proxies, where upgrade logic sits in the proxy, UUPS, where it sits in the implementation and is cheaper, and Beacon proxies for many proxies sharing one implementation. Because constructors do not run in the proxy context, we use initializer functions protected against being called twice. New versions must keep the storage layout, only appending variables, or use namespaced storage. The big trade-off is trust: upgradeability lets you fix bugs but also lets whoever holds the key change the rules. So I put upgrades behind a multisig and timelock, so users can see and react to changes.

5. What is a blockchain oracle and how do you use price data safely?

A strong answer covers:

  • Contracts cannot fetch off-chain data themselves
  • Oracles bring data such as prices on chain
  • Decentralised oracle networks like Chainlink
  • Check staleness, decimals and sequencer uptime on L2
  • Avoid spot prices from a single pool; use TWAP or multiple sources
  • Circuit breakers for extreme values
Show model answer

Smart contracts cannot call external APIs, so anything from the outside world, like the ETH/USD price, must be pushed on chain by an oracle. Decentralised oracle networks such as Chainlink, or pull-based ones like Pyth, aggregate data from many sources and node operators. When I use a price feed, I check that the answer is positive, that updatedAt is recent compared with the feed's heartbeat so I am not using stale data, and I handle decimals correctly since feeds and tokens use different decimals. On Layer 2 networks I also check the sequencer uptime feed, because prices can be stale after an outage. I never use the spot price from a single DEX pool, because flash loans can move it in one transaction; if I need an on-chain price, I use a time-weighted average over enough time. For critical protocols I add sanity bounds and a fallback or pause when prices move unrealistically.

6. How do signature-based approvals like ERC-2612 permit and Permit2 work, and what are their risks?

A strong answer covers:

  • User signs an EIP-712 message off chain instead of sending approve
  • Contract verifies signature, nonce and deadline, then sets allowance or transfers
  • Saves a transaction and improves UX
  • Phishing: signatures look harmless but can give away funds
  • Use short deadlines, exact amounts, and handle front-run permit calls gracefully
Show model answer

With ERC-2612, the token itself has a permit function. The user signs an EIP-712 message with owner, spender, value, nonce and deadline, and anyone can submit it; the token checks the signature and sets the allowance. A dApp can then call permit and transferFrom in one transaction, so the user skips a separate approve. Permit2 from Uniswap does the same for any ERC-20: the user approves Permit2 once, and later grants spending through signed, expiring permissions. The main risk is phishing, because signing feels free, but a malicious permit can drain tokens, so wallets must show clear details and users should check them. As a developer I use short deadlines and exact amounts, and I wrap the permit call in a try block, since someone can front-run it with the same signature and make my call revert even though the allowance is already set.

7. How do you build a reliable dApp frontend that reads and writes contracts with viem and wagmi?

A strong answer covers:

  • Wallet connection and handling chain and account changes
  • Reads via public client or multicall, cached with React Query
  • Simulate before writing, then wait for the receipt
  • Show clear pending, success and error states, decode revert reasons
  • Type-safe ABIs and correct decimal handling
Show model answer

I use wagmi for wallet connection and React hooks, with viem underneath. Connectors cover browser wallets, WalletConnect and smart wallets, and I handle account and chain changes, prompting the user to switch if they are on the wrong network. For reads I use useReadContract or batched multicall reads, cached through TanStack Query, and I refetch after relevant transactions or events. For writes I first simulate the call, which catches reverts and gives a decoded error before the user signs. Then I send it, show a pending state with the hash, and wait for the receipt before showing success. I import ABIs as const so viem gives full type safety, and I always format amounts with each token's decimals using formatUnits and parseUnits. I also use my own RPC provider with fallbacks instead of relying only on the wallet's.

8. How do chain reorganizations affect a dApp or backend service, and how do you handle them?

A strong answer covers:

  • Recent blocks can be replaced, removing or reordering transactions
  • Use confirmation depth or safe and finalized block tags depending on value
  • Indexers track block hashes and roll back orphaned data
  • Idempotent processing keyed by tx hash and log index
  • Different chains and L2s have different finality rules
Show model answer

A reorg happens when the network switches to a different chain tip, so transactions in the replaced blocks can vanish or move. On Ethereum, reorgs beyond one or two blocks are rare, but finality takes about 13 minutes, and other chains differ. For low-value UI updates I show results after one confirmation. For anything involving money, like crediting deposits, I wait for the safe or finalized block tag, or a confirmation depth suited to the chain. My indexers store each block's hash and parent hash, detect when they no longer match, and roll back events from orphaned blocks before reprocessing. Processing is idempotent, keyed by transaction hash and log index, so replays do not double-count. On L2s I also separate soft confirmation from the sequencer and real finality on L1.

9. How do you design admin control for a protocol so it can be maintained without making users trust a single person?

A strong answer covers:

  • Minimise privileged functions and document them
  • Split powers into roles with least privilege
  • Use multisigs with hardware keys for admin roles
  • Timelocks on upgrades and parameter changes, with events for monitoring
  • Fast emergency pause, and plan for gradual decentralisation
Show model answer

I start by keeping privileged functions to a minimum and listing exactly what each one can do, including worst cases. Then I split powers into roles with least privilege: a pauser, a parameter manager, an upgrader, and no single key that can do everything. Each role is held by a multisig like Safe, with signers on hardware wallets, and important roles need several approvals. Upgrades and risky parameter changes go through a timelock, often 48 hours or more, and emit events, so users and monitors can react or exit. An emergency pause can act quickly, but it should only stop actions, not move funds. Bounds checks on parameters limit damage even from valid admins. Over time, control can move to on-chain governance with voting delays and quorum, and some parts can be made immutable.

10. Walk me through how you deploy a set of contracts to mainnet safely.

A strong answer covers:

  • Scripted, repeatable deployments (Foundry scripts) tested on a fork and testnet
  • Pinned compiler version, settings and EVM version per chain
  • Secure keys: hardware wallet or multisig, not a plain env file
  • Verify source on explorers and check deployed config with a script
  • Hand ownership to multisig, record addresses, set up monitoring
Show model answer

I write the deployment as a Foundry script so it is repeatable and reviewed like normal code. I first run it against a mainnet fork, then on a testnet, and check the results with post-deployment tests that read every important setting. The compiler version, optimizer runs and EVM version are pinned, and checked per target chain. For the real deployment I sign with a hardware wallet or a dedicated deployer key, never a key sitting in a plain env file, and I use CREATE2 if I want the same addresses across chains. Right after deploying I verify the source on Etherscan or the chain's explorer, run a script that checks owners, roles and parameters, and transfer ownership to the multisig or timelock. Finally I publish the addresses, store the deployment artifacts in the repo, and turn on monitoring and alerts.

Hard questions hard

1. You are designing a new lending protocol. What are the biggest security risks and how do you address them?

A strong answer covers:

  • Oracle manipulation and stale prices
  • Liquidation design and bad debt during fast price moves
  • Reentrancy and unusual token behaviour
  • Access control, upgrade and governance risk
  • Rounding and precision errors
  • Testing, audits, monitoring, caps and pause mechanisms
Show model answer

The biggest risk is pricing. I use robust oracles like Chainlink with staleness and deviation checks, never single-pool spot prices, because flash loans make manipulation cheap. Next is solvency: liquidations must work even when prices move fast or gas is high, so I set conservative collateral factors, liquidation bonuses that attract liquidators, and supply and borrow caps per asset, especially for illiquid tokens, to limit bad debt. I follow checks-effects-interactions with reentrancy guards and treat every token as potentially strange: fee-on-transfer, rebasing, callbacks or missing return values, often by allow-listing assets. Rounding always favours the protocol, and I watch for share inflation attacks. Admin and upgrade powers sit behind a multisig and timelock with limited scopes. Testing includes invariant fuzzing, such as total borrows never exceeding deposits, plus multiple audits and a bug bounty. After launch, real-time monitoring and a guardian pause help contain incidents.

2. Why have cross-chain bridges been a common target for large hacks, and how would you design a safer cross-chain system?

A strong answer covers:

  • Bridges hold large pooled funds and rely on message verification
  • Weak validator sets or key compromise
  • Bugs in message verification logic
  • Prefer native or proof-based verification, or burn-and-mint designs
  • Rate limits, delays, monitoring and defence in depth
Show model answer

Bridges usually lock assets on one chain and mint wrapped versions on another, so the lock contract holds huge pooled funds, making it a prime target. Security depends entirely on how the destination chain verifies that a deposit really happened. Many hacks came from small validator sets or multisigs where attackers stole enough keys, like the Ronin hack, or from bugs in signature and message verification, like Wormhole and Nomad. For a safer design, I would prefer verification that does not depend on a few trusted signers: the canonical rollup bridge for L2s, light clients or ZK proofs of source-chain state, or established messaging protocols with independent verifier networks. Where possible, use burn-and-mint for tokens we control, which avoids a single big honeypot. I would add rate limits per time window, delays for large transfers, independent monitoring with automatic pause, and audits focused on message encoding and replay protection.

3. What is MEV, and how would you protect your protocol's users from harmful MEV?

A strong answer covers:

  • Maximal extractable value from ordering, including or excluding transactions
  • Harmful forms: sandwich attacks, front-running
  • Benign forms: arbitrage, liquidations
  • Private orderflow and MEV-protected RPCs
  • Protocol designs: slippage limits, batch auctions, commit-reveal, intent-based systems
Show model answer

MEV, maximal extractable value, is profit that block builders and searchers can make by choosing which transactions go into a block and in what order. Some MEV is useful, like arbitrage that keeps prices aligned across DEXs and liquidations that keep lenders solvent. Harmful MEV hurts users directly, mainly sandwich attacks, where a bot buys before a user's swap and sells after it, and front-running of valuable actions like NFT mints. To protect users, I set sensible default slippage and deadlines in the frontend and route transactions through private or MEV-protected RPCs, so they skip the public mempool. At the protocol level, designs like batch auctions with a uniform clearing price, as CoW Protocol uses, intent-based systems where solvers compete, or commit-reveal schemes for actions that should not be visible early, remove the advantage of ordering. Some protocols also redistribute MEV back to users through order flow auctions.

4. How do you prepare a codebase for a security audit, and what happens after the audit?

A strong answer covers:

  • Freeze scope and code, with clear documentation and specifications
  • High test coverage including fuzz and invariant tests
  • Run static analysis and fix known issues first
  • Explain trust assumptions, roles and known risks
  • Fix findings, get fixes reviewed, publish report
  • Ongoing security: bug bounty, monitoring, incident plan
Show model answer

An audit is far more effective when auditors spend time on deep logic instead of basics. Before it starts, I freeze a specific commit and define the scope clearly. I write documentation: what the protocol does, architecture diagrams, every role and its powers, external dependencies, trust assumptions, and the invariants that must always hold. Test coverage should be high, with fuzz and invariant tests that encode those properties. I run Slither and other tools and fix obvious issues first, plus an internal review. During the audit, the team answers questions quickly. Afterwards, we fix each finding or document why we accept the risk, and the auditors review the fixes, since fixes can introduce new bugs. The final report is published. An audit does not guarantee safety, so we also launch with caps, set up a bug bounty such as on Immunefi, real-time monitoring, and an incident response plan with pause controls.

5. How do you decide what logic and data should live on chain versus off chain in a dApp?

A strong answer covers:

  • On chain for trust-critical state, ownership and value transfer
  • Off chain for large data, private data and heavy computation
  • Use hashes, Merkle roots or proofs to link off-chain data to on-chain state
  • Cost, latency, privacy and censorship trade-offs
  • Layer 2s, data availability and ZK proofs as middle grounds
Show model answer

I ask what users need to trust without trusting us. Ownership of assets, balances, and rules about moving value belong on chain, because that is the reason to use a blockchain. Large data like images, documents or game assets goes off chain on IPFS or Arweave, with only a content hash on chain so anyone can verify it has not changed. Private data, like personal details, should never be on a public chain at all, because it is permanent. Heavy computation, such as matching orders or calculating airdrops, can run off chain, with results committed as a Merkle root so users claim with a proof, or verified with a ZK proof. Things like search and analytics come from an indexer. Cost and latency also matter: Layer 2 rollups and blob data have made on-chain writes much cheaper, which moves the line. Each off-chain piece adds a trust or availability assumption, which I document clearly for users.

6. Your protocol is being exploited right now on mainnet. What do you do in the first hour and afterwards?

A strong answer covers:

  • Confirm the attack and pause affected contracts using emergency roles
  • Have a prepared incident plan, war room and owners
  • Consider whitehat rescue of remaining funds, beware of front-running
  • Contact security partners, exchanges, bridges and stablecoin issuers to freeze
  • Communicate clearly, then do root cause analysis, fixes, audit and post-mortem
Show model answer

First I confirm what is happening from monitoring and transactions, and open a war room with the people and keys we need, following our prepared incident plan. If the contracts have a pause or guard role, I use it at once to stop further losses, and I disable affected frontend actions. If funds remain at risk and cannot be paused, we consider a whitehat rescue, using a private transaction route so the attacker or bots cannot front-run us. In parallel we contact security groups such as SEAL 911, and exchanges, bridges and stablecoin issuers who may freeze stolen funds. We post a short, honest status update and keep it updated. Afterwards we trace the root cause, fix it with tests, get the fix reviewed or audited, and publish a full post-mortem with a recovery or compensation plan.

7. How does EIP-7702 change assumptions that smart contracts and dApps make about EOAs, and what would you change in your code?

A strong answer covers:

  • EOAs can delegate to contract code, gaining batching, sponsorship and custom logic
  • tx.origin == msg.sender and extcodesize checks no longer prove an EOA
  • Delegated EOAs can receive callbacks and reenter
  • Malicious delegation targets are a phishing risk; storage collisions between delegates
  • Design for smart accounts: ERC-1271 signatures, no EOA-only assumptions
Show model answer

EIP-7702, live since the Pectra upgrade, lets an EOA sign an authorization that sets its code to point at a contract. Its address and key stay the same, but it can now batch calls, have gas sponsored and run custom validation. That breaks old assumptions. A check like msg.sender == tx.origin no longer means the caller cannot run contract logic, and code size checks fail too. A delegated EOA can also have receive hooks, so transfers to "users" can reenter. In my code I remove any security logic based on EOA detection and rely on proper reentrancy protection and economic limits instead. I support ERC-1271 signature checks alongside ECDSA, since accounts may validate signatures through code. For wallet-facing features, delegation targets are very powerful, so users should only delegate to audited code, and switching delegates needs care with storage layout.

8. How do you make sure an upgrade to a proxy-based contract does not corrupt storage or break the system?

A strong answer covers:

  • Understand delegatecall: storage lives in the proxy, layout must stay compatible
  • Never reorder, remove or change types of existing variables; append only or use ERC-7201 namespaces
  • Automated layout checks (OpenZeppelin upgrades plugin, forge inspect diff)
  • Initializers and reinitializer versions, disable initializers on implementation
  • Fork tests of the upgrade on real state, timelock and multisig execution
Show model answer

Because a proxy delegatecalls into the implementation, all state lives in the proxy's storage, and the new implementation must read the same slots in the same way. So I never reorder, remove or change the type of existing variables, including in parent contracts. New state is appended, or better, kept in ERC-7201 namespaced structs as OpenZeppelin v5 does. I check this automatically: the OpenZeppelin upgrades plugin or a forge inspect storage layout diff in CI fails if the layout is incompatible. Any new setup runs in a reinitializer with a new version number, and the implementation's constructor calls _disableInitializers. Before going live I run the upgrade on a mainnet fork against real state and run the full test suite, including invariants, on the upgraded system. The upgrade itself goes through a multisig and timelock, with a clear rollback plan.

9. You are building an ERC-4626 yield vault. What design and security issues do you consider?

A strong answer covers:

  • Share price inflation or donation attack on empty vaults; virtual shares or seed deposit
  • Rounding always in favour of the vault
  • Accurate totalAssets that cannot be manipulated, including strategy losses
  • Token quirks: fee-on-transfer, rebasing, decimals
  • Strategy risk, withdrawal limits, reentrancy, and invariant tests
Show model answer

The first issue is the inflation or donation attack: an attacker deposits 1 wei, donates a large amount, and later depositors receive zero shares. I use OpenZeppelin's virtual shares with a decimals offset, and also seed the vault with an initial deposit. Rounding must always favour the vault, so deposit and redeem round down for the user, and mint and withdraw round up. totalAssets must be accurate and hard to manipulate: if it reads a pool spot price or a balance anyone can donate to, the share price can be pushed. I handle strategy losses honestly instead of hiding them, and set withdrawal limits through maxWithdraw when liquidity is locked. I decide which tokens are supported and block fee-on-transfer or rebasing ones if not handled. Finally I add reentrancy guards around external strategy calls and write invariant tests, such as "shares never become worth more than the assets backing them".

10. Your team wants to deploy the same protocol to five EVM chains, including L2s. What can go wrong and how do you prepare?

A strong answer covers:

  • EVM version and opcode differences (PUSH0, transient storage), precompiles
  • Different block times, block.number meaning, gas pricing and L1 data fees
  • Sequencer downtime, oracle availability and sequencer uptime feeds
  • Token differences: bridged versions, decimals, addresses
  • Cross-chain messaging trust, consistent addresses, per-chain testing and monitoring
Show model answer

Chains that look EVM-compatible still differ. Some lag on opcodes like PUSH0, MCOPY or transient storage, so I set the EVM version per chain and test deployments on each. Block times and block.number differ, so time logic uses block.timestamp. Gas pricing differs too: L2s add an L1 data fee, which changes gas trade-offs. Oracles may not exist for every asset, and on rollups I check the sequencer uptime feed and plan for sequencer outages, such as giving users grace periods before liquidations. Tokens differ: USDC may be native on one chain and a bridged version on another, with different addresses and sometimes decimals, so I keep a per-chain config that is reviewed and tested. If chains talk to each other, I validate the source chain and sender of every message. I use CREATE2 for consistent addresses, run fork tests per chain, and set up monitoring on all of them.