Hiring for this role?

Start free with this plan

Free for your first open role.

Why Hirezen?
  • Every interviewer runs the same script and marks the same signals.
  • AI drafts the write-ups, and the debrief puts every read side by side.
  • No ATS to set up first, and no bot in the call.

Blockchain Developer interview questionsSmart Contract Coding Test round

A 60 min interview plan with a time-boxed script, what each question is for, and the signals to score against. Key skills: Solidity smart contract security in code: finding and fixing reentrancy, signature replay, a missing access check and share rounding in a small vault; proving the worst with a failing Foundry test; writing the invariant that catches its class; and deciding what to do when the contract is already live and cannot be upgraded.

Opening

Who is interviewing, how the round will run, and a question to settle the candidate in. The standard opening

Read it the way an attacker would

17 min
What this part is for

Purpose

Runs over a small Foundry repository handed over at the start, and every defect in it is one you planted, so write it yourself the evening before. `src/ShareVault.sol` is about 130 lines of Solidity 0.8 using OpenZeppelin 5.x for `Ownable`, `ECDSA` and `MessageHashUtils`: a pooled-ETH vault that mints transferable shares, takes a withdrawal fee, and prices shares from `totalAssets()`, the contract's ETH balance less `accruedFees`. Plant four things. First, `withdraw(uint256 shares)` copies `sharesOf[msg.sender]` into a local, sends `assets - fee` to `msg.sender` with a low-level `call`, and only after the call writes `sharesOf[msg.sender] = balance - shares`, `totalShares -= shares` and `accruedFees += fee` — so a holder that is a contract can re-enter `withdraw`, or call `transfer` from its `receive` and have the stale balance written back over the shares it just moved. Second, `withdrawWithSig(owner, receiver, shares, deadline, sig)` recovers the owner from `toEthSignedMessageHash(keccak256(abi.encodePacked(owner, shares, deadline)))`: the signature binds no receiver, no nonce, no chain id and no contract address, and the app's relayer asks users for 30-day deadlines. Third, `setFeeBps(uint16)` has no `onlyOwner` and no cap, though `setFeeRecipient` beside it has the modifier; any value above 10,000 makes `assets - fee` underflow, so every withdrawal reverts. Fourth, `deposit()` prices from a balance that `receive()` lets anyone raise, and it mints zero shares without reverting — so in an empty or nearly empty vault a direct transfer can round the next deposit down to nothing. Leave four things correct and note who flags them: `deposit` subtracts `msg.value` before pricing, which is right because the balance already includes it; it rounds shares down, which is the right direction; `withdrawWithSig` burns shares before it sends, the order `withdraw` gets wrong; and `collectFees` zeroes `accruedFees` before paying the fee recipient. `test/ShareVault.t.sol` has nine passing tests, and every one of them uses a single externally owned account depositing round numbers and each signature once. Four plants in 130 lines is about seven minutes of reading for someone who knows what to look for, which leaves ten for the list; the first question is scored on what the candidate reads first and how they rank it, not on reaching every follow-up. Book the room for 70 minutes; the closing exchange is not inside the 60.

•

I'm [YOUR_NAME] and I write and review contracts at [COMPANY_NAME]. This is a coding hour, but the first part is reading: a small vault that holds ETH, with a test suite that passes. I want to see how you read code that holds other people's money before I see how you write it.

What this line is for

Purpose

Sets reading as the first task, so a candidate who prepared to write a contract from a blank file spends the opening minutes on the code in front of them.

•

Take about seven minutes with the vault before you say anything. You can run the tests. Nothing is off limits — the tests, the comments, the parts that look fine.

What this line is for

Purpose

Gives permission to read in silence and says nothing about what kind of caller to imagine, so whether the candidate thinks of a contract on the far side of a `call` is their own act.

•

What would you refuse to deploy here? Give me the list in order of how much money each problem can lose, and tell me how you found each one.

What this question is for, and what to listen for

Purpose

Separates candidates who trace what an attacker controls at each external call and each signature check from candidates who scan for familiar patterns. The ordering is scored as much as the list: the easiest finding to name is not the most expensive one.

Signals to score

  • Names the write-back after the `call` in `withdraw`, and says an attacker needs only one share and a contract with a `receive` function
  • Sees the cross-function path — calling `transfer` inside the callback so the stale balance is written back over shares already moved — not only re-entering `withdraw`
  • Names all three things the withdrawal signature fails to bind — the receiver, a nonce, the chain and contract — and what each gap allows
  • Notices that a signature already submitted stays replayable from public calldata until its 30-day deadline
  • Finds the missing `onlyOwner` on `setFeeBps`, and says a value above 10,000 freezes every withdrawal rather than paying the attacker
  • Explains the empty-vault case: a direct transfer raises the share price until the next deposit rounds to zero shares, and `deposit` does not refuse it
  • Orders the list by money lost and by what the attacker needs, and can defend putting the reentrancy or the signature first
  • Says why the nine tests could never have caught any of it: one depositor, externally owned accounts only, every signature used once
  • Leaves the correct parts alone — the `msg.value` subtraction, the round-down in `deposit`, the burn before the send in `withdrawWithSig` — or withdraws the concern when asked

Follow-up questions

  • Who is on the other end of that `call`, and what can it do before the next line runs?
  • A user signed a withdrawal last week and the relayer submitted it. What is that signature worth today?
  • Nobody makes money by calling `setFeeBps`. Why is it still on your list?
  • `deposit` subtracts `msg.value` from the balance before pricing. Is that a bug?
  • Suppose the vault already holds 1,200 ETH. Does the empty-vault finding still matter?

Prove it, then fix it

23 min
What this part is for

Purpose

Moves from reading to writing. The candidate proves the finding they ranked first with a test that fails on the repository as written, fixes the contract, and then writes the property that would have found the bug without being told where to look. The code is scored on whether it would compile and run under `forge test`, not on style.

•

Take the one you ranked first. Write the Foundry test that fails today because of it, fix the contract, and run the test again. After that I want the invariant — the sentence about this vault that must always be true — written as a test that could have found this on its own.

What this line is for

Purpose

Makes a failing test the first deliverable, so the fix is measured by something written before it, and asks for the property separately so the candidate cannot stop at the one scenario they already know.

•

Write the test that shows the bug, fix `ShareVault.sol`, and then write an invariant test with a handler that would have found the same bug if nobody had pointed you at it.

What this question is for, and what to listen for

Purpose

The load-bearing exercise. A scenario test proves what the candidate already knows; an invariant test is how a team finds the next bug, and it only works if the handler can do what an attacker can — including be a contract. Many candidates who have used fuzzing have only written handlers whose actors are all externally owned accounts.

Signals to score

  • Writes an attacker contract whose `receive` calls back into the vault, and asserts on value extracted or shares created rather than on a revert message
  • Makes the test fail on the unmodified contract and reads the failure back before changing `ShareVault.sol`
  • Fixes `withdraw` by writing the balance, `totalShares` and `accruedFees` before any ETH leaves, rather than by adding a guard and keeping the order
  • Says whether a reentrancy guard is still worth having after the reordering, on which functions, and roughly what it costs per call
  • States the invariants in plain words before coding them: shares held add up to `totalShares`; the ETH balance covers `accruedFees`; redeeming every holder's shares one at a time never pays out more than `totalAssets()`
  • Builds a handler that bounds inputs, draws callers from a fixed list of actors, and includes at least one contract actor that calls back into the vault
  • Explains why an invariant run whose actors are all externally owned accounts passes against the original contract
  • Rejects `payable(msg.sender).transfer` and its 2,300-gas stipend as the fix, and says what it breaks for recipients that are smart-contract wallets
  • Checks what else the change touches — `withdrawWithSig`, `transfer`, and what a view returns if another contract reads it mid-call
  • Says where the suite runs, with a number of runs and a call depth, and what a failing invariant blocks

Follow-up questions

  • Your invariant run passed a thousand times against the original contract. Who were the callers?
  • You added `nonReentrant` to `withdraw`. Walk the `transfer` path again — does it still work?
  • Why not send with `payable(msg.sender).transfer` instead?
  • Which invariant fails first against the original code, and roughly how many calls does it take?
  • What does your guard cost per withdrawal, and is there a cheaper one on a chain that has transient storage?

What a signature is allowed to mean

11 min
What this part is for

Purpose

One question on the signature path. The candidate has named its gaps; now they design what the owner should sign and say what becomes of the signatures already in circulation.

•

Rewrite what `withdrawWithSig` checks. Tell me exactly what the owner signs, how the contract refuses a second use, and what changes for an owner whose account is a smart-contract wallet.

What this question is for, and what to listen for

Purpose

Tests whether the candidate knows the mechanism behind typed signatures rather than the name of the standard. The separator is whether every signed field is justified by an attack it closes, and whether they remember owners who cannot produce an ECDSA signature at all.

Signals to score

  • Moves to EIP-712 typed data with a domain of name, version, chain id and verifying contract, and says what the domain closes
  • Puts receiver, shares, nonce and deadline in the signed struct, and says the submitter can then choose nothing except whether to submit
  • Uses a per-owner nonce that increments on use, and says why it beats a map of used digests: the owner can cancel
  • Gives the owner a way to invalidate outstanding signatures before their deadlines
  • Verifies smart-contract wallet owners through ERC-1271, and says a check built only on signature recovery shuts them out
  • Keeps a library's recovery, which rejects malleable signatures, rather than raw `ecrecover`, which returns the zero address on garbage
  • Says that copying a correctly bound withdrawal out of the mempool gains nothing, because only the submitter's gas changes
  • Says a fixed contract does not invalidate signatures still valid on the old one, and ties the danger's length to the deadline
  • Shortens the deadline and says what the relayer's users lose by it

Follow-up questions

  • Once the receiver is signed, what can someone who copies the transaction still do?
  • Why a nonce rather than marking each digest as used?
  • An owner holds their shares in a multisig wallet. Can they use this function after your change?
  • The chain id is in the domain. What should happen to a cached domain separator if the chain forks?
  • The new vault is live. What are last week's signatures worth against the old one?

It is already live

9 min
What this part is for

Purpose

The turn. The vault stops being a repository: it is deployed, it cannot be upgraded, and it has no pause. This question reads incident response in its quiet form — a flaw nobody has used yet, where the team controls the timing and can still lose everything by what its own actions reveal.

•

One more fact about this vault. It has been live for five weeks and holds 1,200 ETH from 310 depositors. It is not behind a proxy, it has no pause, and the relayer has been submitting signed withdrawals for three weeks.

What this line is for

Purpose

Turns the findings into a live problem without saying anyone has used them, so the candidate has to decide what urgent means before acting.

•

You found these problems this morning and nobody outside the room knows. What happens between now and the moment the funds are safe — and what do you make sure does not happen?

What this question is for, and what to listen for

Purpose

Reads order of operations, discretion and judgment under a clock nobody can see yet. Candidates who have been through this worry first about what their own fix will tell an attacker.

Signals to score

  • Stops the relayer submitting signed withdrawals at once, and says why that step reveals nothing to anyone watching the chain
  • Checks the chain before planning: re-entrant calls into `withdraw`, a signature used twice, any change to `feeBps`
  • Keeps the finding to named people, and pushes no fix to a public repository and publishes no verified source for a replacement before users are safe
  • Accepts that with no pause and no upgrade path the only protection is ETH leaving the vault, and names the ways: depositors withdraw, or the team moves funds in a planned rescue
  • Treats a rescue that relies on the flaw itself as a decision for leadership and legal counsel, not an engineering call
  • Plans the move to a reviewed replacement, and knows the old contract's 30-day signatures set how long it stays dangerous
  • Writes the message to depositors so it says what to do and by when, without describing the flaw
  • Gets an outside reviewer on the replacement despite the clock, rather than trading review for speed
  • Says when the full disclosure is published: after funds are safe, with a date

Follow-up questions

  • What is the first thing you do that someone watching the chain could not notice?
  • Your fix is ready. Where does the code live, and when does it become public?
  • Could the team move the funds out itself, and who decides whether it should?
  • What does the message to depositors say, and what does it leave out?

Closing

We have a few minutes left, and I would rather spend them on your questions than on mine. Ask me anything about how contracts get written and shipped here — who signs off before a contract holds funds, what our tests cannot see, whether we have ever had to move funds out of a contract in a hurry.

What this line is for

Purpose

A candidate who has shipped contracts asks about review gates and life after deployment; one who has not asks which chain and which framework. Listing the topics keeps the signal in what they pick.

One honest thing before you go: [name a true, unflattering fact about your own contracts — a function no test calls from a contract, a signature scheme nobody has re-read since launch, a contract with no way to pause]. That would be yours from the first week.

What this line is for

Purpose

Ends on a specific weakness the team has. It is the pitch that works on the candidate this round is looking for, and a warning to one who expected everything audited and settled. Check it is still true on the day.

Their questions for you, and what happens next. The standard closing

Blockchain Developer interviews — common questions

Who is this Blockchain Developer interview plan for?
It is written for the interviewer, not the candidate: the hiring manager, engineer or panel member running the Smart Contract Coding Test round for a Blockchain Developer role. It gives you a 60 min script to follow in the conversation — 4 questions with what each one is for and the signals to score against — so you are not writing the round from scratch the night before.
What does the Smart Contract Coding Test round assess?
This round is focused on: Solidity smart contract security in code: finding and fixing reentrancy, signature replay, a missing access check and share rounding in a small vault; proving the worst with a failing Foundry test; writing the invariant that catches its class; and deciding what to do when the contract is already live and cannot be upgraded. It works through Read it the way an attacker would, Prove it, then fix it, What a signature is allowed to mean and It is already live, scoring against 37 observable signals, with follow-up prompts on all 4 questions for going deeper where an answer is thin.
How is the 60 min split up?
60 min on 4 questions. The questions take in Read it the way an attacker would (17 min), Prove it, then fix it (23 min), What a signature is allowed to mean (11 min) and It is already live (9 min). The timings are there so the round stays on schedule and every candidate gets the same shape of interview — which is what makes two candidates comparable afterwards.
What other rounds should I run for a Blockchain Developer?

A single round does not cover a whole role. The other rounds in this library for a Blockchain Developer:

Hiring for this role?

Open this plan in Hirezen and make it a position in one click.

  • Every interviewer runs the same script and marks the same signals.
  • AI drafts the write-ups, and the debrief puts every read side by side.
  • No ATS to set up first, and no bot in the call.
Start free with this plan

Free for your first open role.