Skip to content

Blockchain Developer interview questions

100 real questions with model answers and explanations for Middle candidates.

See a Blockchain Developer resume example

Practice with flashcards

Spaced repetition · Hunter Pass

Questions

reentrancy

Single-function reentrancy repeats the same entry point, while cross-function reentrancy enters a different function that depends on the same unfinished state.

  • In single-function reentrancy, a callback invokes a function such as withdraw again before its balance update is complete.
  • In cross-function reentrancy, the callback reaches another entry point such as transfer or borrow that reads the same balance or limit.
  • Locking only withdraw does not protect transfer if both functions rely on an invariant that the first call has not restored.
  • The security boundary is the shared invariant across all external entry points, not the body of one function in isolation.

Why interviewers ask this: The interviewer is checking whether the candidate can reason about shared state beyond the classic recursive-withdrawal example.

reentrancy

Cross-contract reentrancy follows a callback into another contract, while read-only reentrancy exposes a transient value without directly writing the vulnerable state.

  • In a cross-contract path, contract A calls an external contract that calls contract B, and B reads or changes state coupled to A.
  • A mutex in A does not automatically protect B because each contract has separate storage and may enforce a shared invariant differently.
  • In read-only reentrancy, a callback queries a view such as getPrice while one accounting field is updated and another is still stale.
  • The view modifier prevents state writes through that function, but it does not guarantee that the value represents a consistent point in the transaction.

Why interviewers ask this: The interviewer wants to see whether the candidate understands that reentrancy can cross contract boundaries and corrupt observations as well as writes.

reentrancy

Checks-effects-interactions reduces the callback window and ReentrancyGuard blocks reentry into guarded paths, but neither is a blanket proof of safety.

  • CEI validates inputs first, commits all related accounting effects next, and performs external calls only after the invariant is restored.
  • ReentrancyGuard uses a storage mutex that rejects a second entry into a function protected by the same guard during the first call.
  • CEI is incomplete if one field is updated while related fields or state in another contract remain inconsistent before the interaction.
  • A guard does not cover unguarded entry points, another contract's storage, or inconsistent values exposed by view functions.

Why interviewers ask this: The interviewer is evaluating whether the candidate treats common defenses as scoped mechanisms rather than universal reentrancy fixes.

tokensreentrancyhooks

An ERC-777 transfer can execute sender or recipient hook code, so a token operation is also an external control transfer.

  • The tokensToSend hook runs before token balances change, while tokensReceived runs after they change, giving hooks distinct callback points.
  • A recipient hook can call the integrating protocol again before that protocol finishes its own deposit, withdrawal, or reward accounting.
  • A contract accepting arbitrary ERC-20-compatible assets cannot assume transfer or transferFrom has no callback behavior.
  • SafeERC20 handles return-value and call compatibility, but it does not block hooks, so CEI and appropriately scoped reentrancy guards are still needed.

Why interviewers ask this: The interviewer is checking whether the candidate recognizes token transfers as potentially adversarial external calls rather than simple balance updates.

oraclesdefivulnerabilities

A DEX spot price reflects current pool reserves, which an attacker can distort temporarily with liquidity borrowed for one transaction.

  • A flash loan supplies enough capital to trade heavily against a pool without requiring the attacker to hold that capital beforehand.
  • If a protocol reads the shifted reserve ratio before the attacker reverses the trade, it can misprice collateral, shares, or swaps.
  • The attacker can unwind the position and repay within the same transaction, so profit only needs to exceed price impact, swap fees, gas, and the flash-loan fee.
  • A single instantaneous quote from one pool is therefore weaker than a time-weighted or independent aggregate with liquidity and deviation checks.

Why interviewers ask this: The interviewer is testing whether the candidate can connect atomic liquidity, mutable AMM reserves, and unsafe oracle consumption.

A TWAP averages prices over a window, forcing an attacker to sustain distortion over time at the cost of slower response to real market moves.

  • Manipulating several observations or blocks usually requires more capital and exposes the attacker to arbitrage between blocks.
  • A longer window raises that burden but makes the oracle lag legitimate rapid price changes.
  • Attack cost also depends on pool depth, observation spacing, and whether the attacker can influence consecutive blocks, not only on window length.
  • A thin or inactive source can remain cheap to manipulate, because averaging low-quality observations does not create liquidity.

Why interviewers ask this: The interviewer wants the candidate to explain both the economic protection of time averaging and its latency and liquidity limits.

A sandwich attacker trades before and after a pending swap to capture the price movement that the victim permits through slippage tolerance.

  • The attacker sees the victim's swap in the public mempool and submits a front-running trade in the same direction to move the pool price.
  • The victim then executes at a worse price as long as the result still satisfies minAmountOut or maxAmountIn.
  • The attacker reverses the first trade after the victim and keeps the captured price difference minus pool fees and transaction costs.
  • Looser slippage creates more extractable room, while tighter limits can force a revert and private order flow can hide the transaction from public searchers.

Why interviewers ask this: The interviewer is checking whether the candidate understands transaction ordering, AMM price impact, and slippage as the attacker's constraint.

system-design

A role's administrator can grant or revoke that role, so control of the admin role can be more powerful than direct membership in the managed role.

  • In OpenZeppelin AccessControl, DEFAULT_ADMIN_ROLE administers all roles by default and is also its own administrator.
  • Compromising one default-admin key can therefore grant minting, upgrading, pausing, or treasury roles even if those operational keys remain safe.
  • Self-administered roles, admin cycles, and missing revocation paths can make privilege removal or recovery unexpectedly difficult.
  • Separating operational roles from a multisig or timelocked administrator and using a two-step admin transfer reduces single-key escalation risk.

Why interviewers ask this: The interviewer is evaluating whether the candidate follows the full privilege graph instead of reviewing only modifiers on business functions.

delegation

delegatecall runs another contract's bytecode against the caller's address, balance, and storage while preserving the caller's current msg.sender and msg.value.

  • Storage reads and writes use raw slots in the calling contract, so proxy and implementation layouts must remain compatible.
  • address(this) is the caller, and external calls made by the delegated code originate from the caller with its authority and assets.
  • Untrusted or incorrectly upgraded code can overwrite ownership or implementation slots, change arbitrary state, or transfer assets held by the caller.
  • delegatecall provides code reuse rather than isolation, so validating the implementation and protecting initialization and upgrades are part of its security boundary.

Why interviewers ask this: The interviewer is checking whether the candidate understands why delegatecall-based proxies inherit both storage-layout and implementation-trust risks.

A signature can be replayed whenever the contract verifies the signed data but does not bind it to one use and one execution domain.

  • Same-contract replay occurs when an accepted payload can be submitted repeatedly because it lacks a nonce or consumed-digest check.
  • Cross-chain replay occurs when the same signature is valid on another deployment because the signed domain omits the chain identifier.
  • The contract must verify and consume a signer-specific nonce or digest before external calls, with a deadline when authorization should expire.
  • An EIP-712 domain containing chainId and verifyingContract separates chains and contracts, while the signed struct binds the intended action, recipient, and amount.

Why interviewers ask this: The interviewer is testing whether the candidate knows that signature validity alone does not provide uniqueness or domain separation.

proxyupgradeabilitydelegation

The proxy forwards a call with delegatecall so the implementation's code runs against the proxy's state.

  • The proxy fallback passes the original calldata to the implementation address and does not make a normal external call.
  • During execution, address(this), storage, and the ETH balance belong to the proxy, so state writes change proxy slots.
  • delegatecall preserves the original msg.sender and msg.value, which lets implementation access control see the actual caller.
  • The proxy returns the implementation's returndata or bubbles its revert data back to the caller.

Why interviewers ask this: The interviewer is checking whether you understand that an implementation supplies code while the proxy supplies identity, storage, balance, and call context.

storageupgradeability

EIP-1967 gives proxy metadata standardized storage locations that are unlikely to collide with implementation variables.

  • The implementation slot is bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1), and OpenZeppelin uses that exact constant.
  • The standard similarly defines separate slots for the proxy admin and beacon, although a proxy pattern may not use all three.
  • Block explorers and clients can read these known slots with eth_getStorageAt without relying on a proxy getter.

Why interviewers ask this: A strong answer connects deterministic slots to collision avoidance, interoperability, and inspection rather than treating EIP-1967 as an upgrade algorithm.

A transparent proxy chooses between upgrade dispatch and implementation fallback according to the caller.

  • Every non-admin call is delegated to the implementation, even when its selector equals upgradeToAndCall.
  • The admin may invoke only upgradeToAndCall; any other admin call reverts with ProxyDeniedAdminAccess instead of reaching the implementation.
  • In OpenZeppelin 5.x, each proxy deploys a dedicated ProxyAdmin whose initial owner is supplied to the proxy constructor.

Why interviewers ask this: The interviewer is evaluating whether you know the sender-based transparency rule and the current OpenZeppelin 5.x ProxyAdmin arrangement.

UUPS keeps upgrade logic in the implementation and uses proxiableUUID to reject implementations that target an incompatible storage slot.

  • upgradeToAndCall runs through the proxy under onlyProxy, which checks delegatecall context and that the proxy currently points to this implementation.
  • The implementation's _authorizeUpgrade override must approve the caller before any implementation slot is changed.
  • The upgrade calls proxiableUUID on the new implementation and requires the EIP-1967 implementation slot; proxiableUUID itself uses notDelegated to prevent calls through a proxy.

Why interviewers ask this: A strong answer separates caller authorization from the ERC-1822 compatibility check and knows where UUPS upgrade code executes.

Transparent proxies place upgrade dispatch in the proxy, while UUPS proxies place it in every upgradeable implementation.

  • TransparentUpgradeableProxy deploys a dedicated ProxyAdmin and includes sender-based dispatch, which costs more deployment gas than a plain ERC1967Proxy.
  • UUPS leaves the proxy small, but each new implementation must retain correct upgrade authorization and ERC-1822 compatibility.
  • Transparent dispatch prevents its admin from accidentally calling implementation functions, while UUPS has no separate admin fallback rule.

Why interviewers ask this: The interviewer is checking whether you can compare code location, deployment cost, operational interface, and authorization responsibility without calling either pattern universally better.

upgradeability

A constructor initializes the implementation's own storage, so proxy state must be configured through a protected initializer executed by delegatecall.

  • An initializer consumes version 1 and normally runs once in the proxy's storage context.
  • Passing encoded initializer calldata to the proxy constructor makes deployment and initialization one atomic transaction.
  • A reinitializer with a higher version configures state introduced by a later upgrade, and each version can be consumed only once.
  • Reinitializers cannot be nested, and inherited initializer calls must be ordered manually so a parent is not initialized twice.

Why interviewers ask this: The interviewer is evaluating whether you understand separate proxy storage, versioned initialization, and inheritance responsibilities.

upgradeability

_disableInitializers permanently locks the implementation's initialization state so nobody can take over that standalone contract.

  • OpenZeppelin sets the initialized version to the maximum uint64 value, causing later initializer and reinitializer calls to revert.
  • It is normally called in the implementation constructor, which affects the implementation's storage rather than the proxy's storage.
  • The proxy remains initializable because delegatecall reads and writes the proxy's separate initialization slot.
  • This lock does not replace initializing the proxy promptly, preferably through constructor calldata in the same deployment transaction.

Why interviewers ask this: A strong answer distinguishes locking the implementation from initializing the proxy and explains why both actions are required.

oopsoliditysolid

An upgrade must preserve every existing storage location because the new implementation interprets state already stored in the proxy.

  • Do not reorder, remove, or change the type of existing state variables; add new variables only after the compatible existing layout.
  • Solidity inheritance linearization contributes to slot order, so reordering base contracts or inserting variables into a base can shift storage used by child contracts.
  • Reserved __gap slots let an inherited contract add variables later by reducing the gap by the same number of consumed slots.
  • OpenZeppelin 5.x upgradeable contracts use ERC-7201 namespaced storage to reduce inheritance collisions, but each namespace still needs a compatible validated layout.

Why interviewers ask this: The interviewer is checking whether you treat proxy storage as a persistent schema and account for inherited as well as directly declared variables.

proxyupgradeability

The transparent pattern resolves proxy and implementation selector clashes by combining the selector with the caller's admin status.

  • A selector is only the first four bytes of a function signature hash, so unrelated signatures can share it.
  • A non-admin call is always delegated, including a call matching upgradeToAndCall, so the implementation function remains reachable to users.
  • An admin call reaches only the proxy's upgrade dispatch, and every other selector reverts instead of falling back to the implementation.
  • OpenZeppelin warns against adding external functions to TransparentUpgradeableProxy because a new function can override the implicit upgrade selector and make upgrades inaccessible.

Why interviewers ask this: A strong answer explains both the four-byte collision and why transparent dispatch depends on sender identity rather than selector uniqueness alone.

authgovernance

The proxy pattern defines where authorization is checked, while a timelock can make an authorized upgrade wait through a public delay.

  • UUPS delegates authorization to _authorizeUpgrade, commonly protected by onlyOwner or a dedicated role.
  • Transparent upgrades are authorized by the owner of ProxyAdmin, so that ownership can belong to a governance contract rather than an individual account.
  • TimelockController separates proposer, executor, and canceller roles and requires a scheduled operation to satisfy its minimum delay before execution.
  • A timelock limits who can upgrade and when, but it does not prove storage compatibility or implementation correctness, which still require technical validation.

Why interviewers ask this: The interviewer is evaluating whether you can separate upgrade authority, delayed governance execution, and implementation safety checks.

Locked questions

  • 21

    How does ERC-4626 model conversions between assets and vault shares?

    secrets
  • 22

    What rounding directions does ERC-4626 require for conversion and preview functions?

  • 23

    Why is an empty ERC-4626 vault vulnerable to an inflation or donation attack?

    vulnerabilitiessecrets
  • 24

    How do virtual shares, virtual assets, and a decimals offset mitigate ERC-4626 donation attacks in current OpenZeppelin contracts?

  • 25

    How do ERC-1155 balances, approvals, and batch operations differ from single-token models?

    batchtokens
  • 26

    What receiver hooks does ERC-1155 require, and why do they create a reentrancy boundary?

    hookstokensreentrancy
  • 27

    How does the ERC-2612 permit flow create an ERC-20 allowance without an approval transaction from the owner?

    tokenstransactions
  • 28

    How do nonce and deadline semantics limit replay of an ERC-2612 permit?

    estimationtransactions
  • 29

    How are an EIP-712 domain separator and final typed-data digest constructed?

  • 30

    How does ERC-1271 standardize signature validation for contract accounts?

    validation
  • 31

    How does Solidity assign storage slots to static state fields?

    storagesolidsolidity
  • 32

    How is the storage slot for a Solidity mapping value derived?

    soliditydata-structuressolid
  • 33

    How are dynamic arrays, bytes, and strings represented in Solidity storage?

    data-structuressoliditysolid
  • 34

    What trade-offs should guide storage packing in an EVM contract?

    evm
  • 35

    How should a developer reason about cold and warm SLOAD access and current SSTORE costs and refunds?

  • 36

    Why does EVM memory expansion have a quadratic cost component?

    componentsmemoryevm
  • 37

    How do ABI encoding and zero versus nonzero bytes affect calldata cost?

    data-location
  • 38

    What lifecycle and use cases does EIP-1153 transient storage provide?

  • 39

    How do event topics, data, and log blooms affect event design and indexing?

    indexesdesign
  • 40

    What are the core parts of a The Graph subgraph and how should its indexing handle chain reorganizations?

    indexes
  • 41

    When a contract reads a Chainlink Data Feed through latestRoundData, what should it check before using the value?

    oracles
  • 42

    Why do Chainlink Data Feed consumers normally call the proxy address rather than its current aggregator directly?

    proxyupgradeabilityoracles
  • 43

    Why would you use the pull-over-push pattern to distribute Ether or tokens to many recipients?

    tokens
  • 44

    How do CREATE and CREATE2 differ when a factory deploys contracts, and what makes a CREATE2 address deterministic?

    deployment
  • 45

    What is an EIP-1167 minimal proxy clone, and how can initialization make clones unsafe?

    proxyupgradeability
  • 46

    How do you define useful input domains for Foundry fuzz tests and investigate a counterexample?

    foundry
  • 47

    How do handlers and ghost state help structure a Foundry invariant test?

    foundry
  • 48

    What is the basic difference between optimistic and ZK rollups in their trust assumptions and finality?

    lockinglayer2
  • 49

    What roles do the sequencer, data availability, and L1 finality play on an L2, and how is a Chainlink Sequencer Uptime Feed used?

    oracles
  • 50

    What is the message lifecycle of a canonical lock-and-mint bridge, including the return path and the main validation checks?

    validationbridges
  • 51

    How would you convert a constructor-based contract into a UUPS implementation?

  • 52

    How would you authorize UUPS upgrades through roles and a timelock?

  • 53

    You need to add state to an upgradeable contract. How do you do it without breaking its storage layout?

    upgradeability
  • 54

    State looks corrupted after a bad proxy layout upgrade. How would you diagnose and recover it?

    proxyupgradeability
  • 55

    How would you initialize state introduced by the second version of a UUPS contract?

  • 56

    How do you deploy a UUPS proxy so neither the implementation nor the proxy can be taken over during initialization?

    proxyupgradeabilitydeployment
  • 57

    A platform security team must own upgrades while several feature teams ship implementations without upgrade code. Would you choose Transparent or UUPS proxies?

  • 58

    How would you validate storage compatibility before upgrading with OpenZeppelin upgrades tooling?

    validation
  • 59

    How would you rehearse a proxy upgrade and prove that old state and behavior still work?

    proxyupgradeability
  • 60

    A Transparent proxy upgrade call fails and an admin-path business call never reaches the implementation. How do you debug it?

    proxyupgradeability
  • 61

    A vault guards withdraw against reentrancy, but a malicious receiver reenters transferCollateral and spends the same collateral twice. How would you repair and test the shared invariant?

    secretsreentrancy
  • 62

    During a vault withdrawal, a callback observes an inflated share price after supply is reduced but before accounted assets are reduced, then uses that price in a lending protocol. How would you fix this read-only reentrancy path?

    secretsreentrancycallbacks
  • 63

    A lending market used the current reserve ratio of one AMM as its collateral oracle, and an attacker used a flash loan to inflate collateral for one transaction. What replacement would you deploy and how would you validate it?

    oraclesdefitransactions
  • 64

    Users of your swap router are being sandwiched because transactions allow amountOutMin of zero and remain valid for an hour. How would you limit the loss without claiming to eliminate MEV?

    transactions
  • 65

    An AccessControl audit shows that one deployer EOA holds DEFAULT_ADMIN_ROLE and can grant minting, upgrading, pausing, and treasury roles. How would you repair the admin graph and enforce least privilege?

    least-privilegedeployment
  • 66

    A relayed EIP-712 withdrawal signature can be submitted repeatedly and also works on a copy of the contract on another chain. How would you make each authorization unique, scoped, and expiring?

    auth
  • 67

    A wallet lets users pass any plugin address to delegatecall, and a malicious plugin overwrites the owner slot and drains assets. How would you redesign the plugin boundary?

    delegationwallets
  • 68

    A settlement contract marks an order paid after target.call(data) without checking the returned success flag, so a failed payout leaves completed accounting. How would you preserve atomicity?

    concurrency
  • 69

    An ERC-1155 marketplace debits listing inventory only after safeTransferFrom, allowing a buyer contract to reenter from onERC1155Received and purchase the same units again. How would you secure the accounting?

    tokens
  • 70

    A collateral module treats an 8-decimal Chainlink price as 18 decimals, undervaluing one asset by 10 billion and breaking liquidation thresholds. How would you fix and test the scaling?

    scalingoracles
  • 71

    A revenue distributor sends Ether to 8,000 recipients in one transaction, and the call now exceeds the block gas limit or reverts on one hostile receiver. How would you redesign it?

    transactionsgaserror-handling
  • 72

    An upgradeable proxy needs new treasury, settlementEpoch, and feeBps fields, and a proposed patch reorders old variables to pack them. How would you reduce new storage cost without corrupting the proxy?

    proxyupgradeability
  • 73

    A hot settleAccount path reads the same account balance and global total several times and writes intermediate values that are overwritten before return. What would you change?

  • 74

    An external quoteBatch function accepts a large array of structs as memory, only validates and reads it, and becomes expensive as batches grow. How would you optimize the data path?

    optimizationmemorybatch
  • 75

    A payment contract stores every historical Payment struct forever, but the product dashboard is the only consumer while refund logic needs current totals and claim status. What should remain on-chain?

  • 76

    A members array is scanned for every authorization check, and removal shifts all later entries, so both operations become too costly. How would you provide lookup and removal in constant time?

    auth
  • 77

    finalizeEpoch now updates tens of thousands of positions and can no longer finish within one block. How would you make the state transition resumable without double-processing accounts?

    concurrency
  • 78

    A frequently called protocol uses a persistent-storage reentrancy guard, and all target chains now support EIP-1153. How would you adopt a transient guard and prove that multicall composability still works?

    decision-makingtypingreentrancy
  • 79

    A non-upgradeable router's execute path costs more than expected, and a review suggests custom errors, immutables, and broad unchecked blocks. How would you decide and apply the optimizations?

    upgradeabilityoptimizationimmutability
  • 80

    An L2 batch submitter spends most of its fee on posting transaction data to L1, and each item currently uses standard ABI words. How would you reduce calldata without creating an unsafe decoder?

    batchtransactionsdata-location
  • 81

    You are wiring an ETH/USD Chainlink feed into a collateral contract. What checks and normalization would you implement before returning an 18-decimal price?

    normalizationoracles
  • 82

    A lending contract using Chainlink prices is being deployed on Arbitrum. How would you add the Sequencer Uptime Feed and a recovery grace period?

    oraclesdeployment
  • 83

    You have a TOKEN/ETH feed with 18 decimals and an ETH/USD feed with 8 decimals. How would you calculate TOKEN/USD at 18 decimals without losing precision or overflowing?

    tokens
  • 84

    The primary oracle becomes stale while users have open lending positions. What fail-closed or fallback behavior would you choose?

    oracles
  • 85

    How would you implement an OpenZeppelin ERC4626 vault backed by an ERC-20 strategy without breaking the standard's accounting?

    secretstokens
  • 86

    How would you configure and test a current OpenZeppelin ERC4626 vault against the empty-vault inflation attack?

    secretsconfig
  • 87

    Product asks the vault to accept fee-on-transfer and rebasing tokens as underlying assets. What would you support?

    tokenssecrets
  • 88

    A donation can change an ERC4626 exchange rate between a user's preview and execution. How would you enforce slippage bounds without fighting the standard's rounding?

  • 89

    Which Foundry fuzz properties would you write for deposit and redeem on an ERC4626 vault?

    secretsfoundry
  • 90

    How would you build a Foundry invariant handler for an ERC4626 vault when underlying can also be donated directly?

    secretsfoundry
  • 91

    A production transaction was mined with status 0, but the wallet shows no reason and the same call succeeds against the latest block. How do you diagnose the exact failure before proposing a fix?

    walletstransactions
  • 92

    A traced proxy call returns 0x8e4a23d6000000000000000000000000... instead of a readable reason, and the team assumes it is Error(string). How do you identify the real failure safely?

    proxyupgradeability
  • 93

    A swap fails only on mainnet at block 22,450,100 while local mocks and a fork of latest pass. How do you build a deterministic reproduction that remains useful after chain state changes?

    mocking
  • 94

    A refactor passes all tests but raises the gas of executeOrder from 118,000 to 151,000, and the author says the change is probably optimizer noise. How do you find and judge the regression with Foundry?

    optimizationgastesting
  • 95

    A release script is ready for an L2, but its RPC label says production, the deployer holds ETH on several chains, and the team plans to wait for twelve confirmations as on L1. What do you verify and configure before deployment?

    deploymentconfig
  • 96

    You deployed a UUPS proxy successfully, but the explorer shows only the proxy bytecode, reads through the proxy fail, and verification of the implementation address was never attempted. How do you repair and validate the deployment?

    proxyupgradeabilitydeployment
  • 97

    A team wants one CREATE2 address on Ethereum and two L2s, but its predicted addresses differ even though all scripts use the same salt. How do you design and verify the multichain deployment?

    designethereumdeployment
  • 98

    A frontend connected through a production wallet sends approvals to a test deployment because an environment variable contained the right ABI but the wrong address. What safeguards do you add to clients and deployment scripts?

    deploymentconfigwallets
  • 99

    A bridge deposit is final on the source chain but absent after two hours on the destination, and an operator proposes sending the same payload again. How do you diagnose it without executing an already delivered message twice?

    bridges
  • 100

    Explorer verification says the deployed bytecode does not match, although the source looks identical and a teammate suggests flattening it until verification passes. How do you resolve the mismatch and when is Sourcify a valid fallback?

    deployment