Web3 Developer Interview Questions: A Complete Preparation Guide
Landing a Web3 Developer role requires more than just technical knowledge—it demands the ability to articulate your understanding of decentralized systems, demonstrate your problem-solving approach, and show genuine passion for blockchain technology. This guide walks you through the most common web3 developer interview questions and answers, behavioral scenarios you’ll likely encounter, and tactical tips to help you stand out.
Whether you’re interviewing for your first Web3 role or advancing your career in blockchain development, you’ll find practical sample answers you can adapt and frameworks for thinking through complex technical challenges.
Common Web3 Developer Interview Questions
What are smart contracts, and why are they important in Web3?
Why they ask: This tests whether you understand the foundational concept of Web3 development. It’s not just about memorizing a definition—interviewers want to see if you grasp why smart contracts matter and can explain their real-world impact.
Sample answer:
Smart contracts are self-executing programs deployed on a blockchain that automatically execute when predefined conditions are met. What makes them powerful is that once deployed, they run exactly as programmed without intermediaries, downtime, or external manipulation.
In my previous role, I built a staking contract for a DeFi protocol where users could lock tokens and earn rewards. The contract autonomously calculated and distributed rewards based on the amount staked and time locked—all without a company managing payments. This eliminated trust issues and significantly reduced operational overhead compared to a traditional system.
The importance comes down to this: smart contracts enable trustless interactions. Two parties don’t need to trust each other; they trust the code and the blockchain itself.
Personalization tip: Replace the staking example with a contract you’ve actually built or studied. If you’re early in your career, talk about a contract from a tutorial you built and why you found it compelling.
Explain the difference between Web2 and Web3 development.
Why they ask: This question gauges your conceptual understanding of the paradigm shift you’re entering. It also shows whether you’ve truly considered how decentralization changes your approach to building.
Sample answer:
The core difference is architectural. In Web2, I was building applications that relied on centralized servers and databases I controlled. If my server went down, the app went down. Users had to trust me with their data. User authentication was traditional username/password.
With Web3, the backend is decentralized—it’s a blockchain shared across thousands of nodes. I’m building frontends that interact with smart contracts instead of APIs I own. Users authenticate with their private keys, not passwords stored on my server. Data isn’t stored in a database I control; it’s either on-chain or on decentralized storage like IPFS.
This fundamentally changes how I think about data security, state management, and transaction finality. There’s no “undo” button—transactions are immutable. I have to be more thoughtful about contract design upfront because deployment is permanent.
I also can’t rely on traditional monitoring and error handling. A failed transaction still costs gas, so users pay for mistakes.
Personalization tip: Share a specific moment where this paradigm shift affected your work. For example: “When I first wrote a smart contract, I realized I couldn’t just push a patch like I would with a Web2 app.”
How do you approach smart contract security?
Why they ask: This is critical. A single vulnerability in a deployed contract can result in millions lost. This question reveals your security mindset and whether you develop defensively.
Sample answer:
Security is baked into my development process from day one. I don’t treat it as an afterthought.
First, I follow established patterns and avoid writing novel logic. I use OpenZeppelin libraries for standard functionality like ERC-20 tokens or access control—these are battle-tested, not my custom implementation.
Second, I write comprehensive tests before deployment. I use Hardhat and test edge cases: what happens if someone sends zero tokens? What if they call the function twice in one block? I aim for at least 90% code coverage.
Third, I practice defensive coding. I use require statements liberally to validate inputs. I avoid complex logic. I use checks-effects-interactions patterns to prevent reentrancy attacks.
Finally, I have external auditors review my code before mainnet deployment. In my last project, a $2M DeFi contract was audited by Trail of Bits. Even though I was confident, they caught three issues I’d missed.
I also stay updated on common vulnerabilities—I follow auditing firms on Twitter and read vulnerability reports regularly.
Personalization tip: Mention a specific vulnerability you’ve encountered or learned about. “I actually discovered a reentrancy vulnerability in a contract I wrote during testing, which taught me the importance of that pattern.”
What consensus mechanism does Ethereum use, and how does it affect your development?
Why they ask: This tests blockchain fundamentals and practical understanding. They want to know if you can connect protocol-level concepts to how they impact your day-to-day work.
Sample answer:
Ethereum currently uses Proof of Stake (after The Merge in September 2022). Validators stake ETH and are chosen to propose blocks; the network penalizes them if they behave maliciously.
From a development perspective, this affects me in a few ways. First, block time and finality matter. Ethereum targets ~12 seconds per block, but finality takes about 15 minutes. When I’m building on Ethereum, I can’t assume a transaction is truly final after one block—I need to wait for finality or accept the small risk of reorganization.
Second, gas pricing fluctuates. Unlike Proof of Work chains where security assumptions are more consistent, Proof of Stake requires me to think about how validator incentives might change gas dynamics.
For user-facing apps, I design for this reality. I don’t show “transaction complete” until the block is finalized. I build retry logic for failed transactions.
If I were building on other chains—say, Solana, which has different consensus—I’d adjust my assumptions about transaction ordering and finality accordingly.
Personalization tip: Mention a chain you’ve actually worked on and how its consensus mechanism shaped your decisions. “When I built on Arbitrum, I focused on reducing transactions because batch posting costs matter differently than on mainnet Ethereum.”
How do you optimize gas costs in a smart contract?
Why they asks: Gas optimization separates experienced Web3 developers from novices. It shows you understand that blockchain constraints are real and affect user experience and economics.
Sample answer:
I approach gas optimization strategically. First, I understand what’s expensive: storage operations are the most expensive, followed by computation, then memory. Calldata is cheapest.
Before optimizing, I measure. I deploy a contract to a testnet and check gas costs with tools like Remix or Hardhat. Only then do I optimize what actually matters.
In a recent contract, the main bottleneck was an external function that looped through a storage array. I refactored it to pass data as calldata instead of reading from storage. That single change reduced gas by 35%.
I also use established patterns: packing variables into single storage slots instead of spreading them across multiple slots, batch operations to reduce loop iterations, and delegatecall for shared logic across contracts.
But I have a rule: I never sacrifice security or code clarity for gas savings. A 5% gas reduction isn’t worth introducing a vulnerability or making code unmaintainable.
Personalization tip: Walk through a specific optimization you’ve made. Include the before/after gas numbers if possible. “The contract originally cost 150k gas to call; after optimization, it was 97k.”
What is IPFS, and when would you use it in a dApp?
Why they ask: This tests your knowledge of off-chain data storage—a practical necessity since storing large data on-chain is prohibitively expensive.
Sample answer:
IPFS—InterPlanetary File System—is a peer-to-peer distributed storage network. Instead of files being stored on centralized servers, they’re stored across a network of nodes. Each file gets a content-based hash; you retrieve files by their hash, not a location.
I use IPFS when a dApp needs to store large data—images, documents, metadata—that doesn’t need to live on-chain. For instance, I built an NFT marketplace where storing full image files on Ethereum would cost thousands of dollars. Instead, I store images on IPFS and store only the IPFS hash (a 46-character string) on-chain.
The smart contract references the hash, and the frontend resolves it to retrieve the image. This gives me the benefits of immutability—if I have the hash, the data can’t change—while avoiding exorbitant on-chain storage costs.
The trade-off is that IPFS relies on pinning services to ensure files persist. If nobody pins a file, it might eventually be garbage collected. So I use services like Pinata or NFT.storage to guarantee persistence.
Personalization tip: Describe a specific data type you’ve stored off-chain and your reasoning. “We initially wanted to store all user profile data on-chain, but the gas costs made it impractical, so we migrated to IPFS.”
Describe your experience with a decentralized application from conception to deployment.
Why they ask: This isn’t just about technical skills—they want to understand your full-stack thinking, project management, and real-world problem-solving.
Sample answer:
I led the development of a governance dApp for a DAO (decentralized autonomous organization). Here’s how I approached it:
Conception and Planning: We identified the need—the DAO had 200+ members and couldn’t coordinate proposals efficiently. I worked with stakeholders to define requirements: propose, discuss, vote, and execute outcomes on-chain.
Design: I outlined the architecture: a governance token for voting, a smart contract for proposals and voting, and a React frontend. I drafted the contract logic and storage layout, thinking through attack vectors like flash loan attacks on voting power.
Development: I built the Solidity contract using OpenZeppelin’s Governor pattern—proven governance infrastructure. The frontend used ethers.js to interact with the contract and The Graph for indexing historical proposals.
Testing and Security: I wrote extensive tests in Hardhat, including edge cases like voting after a proposal ends. We had an external audit done by a specialized firm.
Deployment: We deployed to a testnet first, ran a month-long pilot with real users, gathered feedback, and refined the contract. Only then did we deploy to mainnet.
Post-launch: I monitored transaction patterns, helped users troubleshoot, and iterated based on real-world usage.
Personalization tip: If you haven’t shipped a full dApp, talk through a project you contributed to significantly. Focus on your specific role and impact.
What are the main security vulnerabilities in smart contracts, and how do you prevent them?
Why they ask: This goes deeper than general security awareness. They want concrete knowledge of Web3-specific attack vectors.
Sample answer:
The main categories I think about are:
Reentrancy: A function calls an external contract before updating state, allowing the external contract to call back into the original function. I prevent this using the checks-effects-interactions pattern—verify conditions, update state, then call external contracts. I also use reentrancy guards from OpenZeppelin.
Integer overflow/underflow: Math operations could wrap around and cause unexpected behavior. Solidity 0.8+ has built-in overflow protection, so this is less of a concern, but I still validate numeric operations.
Front-running: Users can see pending transactions in the mempool and race ahead of them. If I’m building an auction, a bid could be front-run. I mitigate this with commit-reveal schemes or batch auctions.
Logic errors: A contract could have unintended behavior because the logic is wrong. Strong testing catches this.
Access control: Functions could be callable by unauthorized users. I use OpenZeppelin’s AccessControl for role-based permissions.
I prevent these through multiple layers: code review, automated testing, static analysis tools like Slither, and external audits.
Personalization tip: Mention a vulnerability you’ve actually encountered. “I wrote a contract that was vulnerable to front-running. A security researcher pointed it out during a code review, and I redesigned the auction mechanism.”
How do you test smart contracts?
Why they ask: Testing discipline is a hallmark of professional Web3 developers. It shows rigor and maturity.
Sample answer:
I use Hardhat for the testing framework and ethers.js for interacting with contracts. My testing approach is multi-layered.
Unit tests: I write tests for individual functions. For a token contract, I test minting, transferring, burning—success cases and failure cases. I verify that events emit correctly.
Integration tests: I test how functions interact. If a user stakes tokens, votes, and then claims rewards, I test that complete flow.
Edge cases: What happens if someone tries to transfer zero tokens? Send to a zero address? Call a function with max uint values? I test these.
I aim for >90% code coverage. My test file is often as long as the contract itself—that’s normal and expected.
For state machine testing, I use Echidna (a fuzzing tool) on complex contracts. It randomly calls functions in random orders to find unexpected behavior.
I run tests on a local fork of mainnet using Hardhat’s forking feature. This lets me test against real protocol state—for example, if I’m integrating with Uniswap, I can test against actual Uniswap state.
Personalization tip: Share your coverage percentage or a specific edge case that caught a real bug. “My tests found a scenario where the contract could be drained if voting happened in the same block as governance token transfers.”
What tools and frameworks do you use in Web3 development?
Why they ask: This gauges whether you’re equipped for their tech stack and how hands-on you are with the practical infrastructure.
Sample answer:
My core toolkit:
Smart contract development: Hardhat for development, testing, and deployment. Solidity for writing contracts. OpenZeppelin contracts for standard implementations. Foundry is growing on me too—it’s fast and uses Solidity for tests instead of JavaScript.
Frontend: React, ethers.js or web3.js for blockchain interaction. I use Wagmi for hooks that simplify common wallet and contract interactions.
Indexing: The Graph for querying on-chain data. Directly querying a blockchain node for historical data is slow; The Graph indexes events and lets me query efficiently.
Deployment: Hardhat for testnet/mainnet deployments. I use environment variables carefully to manage private keys and RPC endpoints.
Monitoring: Tenderly for debugging failed transactions. It shows exactly where transactions failed and why.
Analysis: Etherscan for block exploration. Slither for static analysis to catch vulnerabilities.
I’m not dogmatic—I’ll use whatever tool solves the problem. Foundry is becoming my preference for new projects because it’s faster and integrates better with my workflow.
Personalization tip: Mention frameworks you’ve actually used. Don’t claim expertise with tools you haven’t hands-on experience with. “I recently switched to Foundry from Hardhat and love the speed, but I still use Hardhat for projects that need heavy JavaScript tooling.”
Explain what a Layer 2 solution is and when you’d use one.
Why they ask: This tests understanding of blockchain scalability—a practical concern that affects design decisions daily.
Sample answer:
Layer 2 solutions move transactions off the main Ethereum blockchain while maintaining its security. They bundle thousands of transactions into one or a few transactions on-chain.
The main types are rollups (optimistic and zero-knowledge) and sidechains.
Optimistic Rollups like Arbitrum and Optimism assume transactions are valid by default and only prove incorrectness if challenged. It’s fast and cheap—transactions cost a fraction of mainnet.
Zero-Knowledge Rollups like zkSync use cryptographic proofs to verify batches of transactions without replaying them. They’re more complex but offer stronger security guarantees.
I use Layer 2s when gas costs are a constraint. For a token-swapping dApp, mainnet Ethereum costs $20-50 per transaction during peak times. On Arbitrum, it’s $0.10-0.50. This dramatically improves user experience.
The trade-off is added complexity and slightly delayed finality. If a user deposits on L2, there’s a delay to exit back to mainnet.
I’ve built on Arbitrum for a lending protocol where we wanted to keep users engaged despite high gas costs. The lower fees meant more users could participate without being priced out.
Personalization tip: Mention a Layer 2 you’ve used and why you chose it over alternatives. “We picked Arbitrum over Optimism for our project because we needed lower latency for our liquidation mechanisms.”
How do you handle private keys and secrets in a Web3 application?
Why they ask: This tests security judgment and understanding of critical infrastructure. Poor key management has led to massive breaches.
Sample answer:
Private keys are the crown jewels—they must never be exposed. My approach depends on context.
For users: I never have access to user private keys. Their wallet (MetaMask, etc.) holds keys, and I interact with the blockchain through their signature requests. The user approves transactions; I don’t sign on their behalf.
For backend operations: If my backend needs to sign transactions (e.g., a bot that claims rewards), I use a hardware wallet or managed key service. I use AWS KMS or a hardware security module—something that keeps the private key encrypted and physically separate from my application server.
For development: I use .env files locally for testnet private keys. These are never committed to version control. I have a .gitignore that prevents .env files from being uploaded.
For deployments: On production, I use CI/CD secrets. GitHub Actions, for example, let me store secrets that are injected at runtime and never logged.
I’ve seen projects get hacked because someone committed a private key to GitHub. Attackers scanned public repos and drained wallets within minutes. It’s not paranoia to be careful here.
Personalization tip: Share a lesson learned or close call if you have one. “Early in my career, I almost committed a testnet private key to GitHub. A colleague caught it during review, and I now use a pre-commit hook to prevent it.”
Behavioral Interview Questions for Web3 Developers
Behavioral questions assess how you work, collaborate, and handle challenges. Use the STAR method—Situation, Task, Action, Result—to structure your answers.
Tell me about a time you had to learn a new blockchain technology quickly.
Why they ask: Web3 is rapidly evolving. They want to see if you’re adaptable and can self-direct learning.
STAR framework:
Situation: I was hired to build a DeFi protocol on Polygon, a blockchain I’d never worked with before. I’d built on Ethereum, but Polygon has different token standards and gas dynamics.
Task: I needed to understand Polygon’s architecture and be productive within two weeks.
Action: I read Polygon’s documentation, watched technical deep-dives from their team, built a simple contract on testnet, and debugged it on mainnet to understand the differences. I asked experienced Polygon developers for feedback. I spent a weekend exploring Polygon DeFi protocols to understand common patterns.
Result: Within two weeks, I shipped a bridge contract and core protocol. I’m now one of the team’s Polygon experts.
Personalization tip: Pick a technology you’ve actually learned under pressure. Make the timeline and stakes realistic.
Describe a situation where your code had a critical bug in production. How did you handle it?
Why they ask: In Web3, bugs can be irreversible and costly. They want to see your response to high-pressure situations and your maturity in owning mistakes.
STAR framework:
Situation: I’d deployed a staking contract to mainnet. Twenty-four hours later, a user reported that unstaking was returning 0 tokens instead of their staked amount.
Task: I had to diagnose the issue, minimize user harm, and fix it without causing further damage.
Action: I immediately pulled transaction logs and reproduced the issue locally. I found a logic error in my calculation—I was dividing by a variable that was sometimes zero, causing the result to round down incorrectly.
I paused new transactions to the contract to prevent further damage. I submitted a governance proposal to upgrade the contract (we had a proxy) to a patched version. While that was being voted on, I compiled a list of affected users and calculated what they were owed.
I communicated transparently with users. No excuse-making, just clarity on what happened and how I was fixing it. We redeployed the patched contract and manually distributed the owed tokens.
Result: No tokens were lost. Users were made whole. The incident took six hours from discovery to fix. We added an additional test case to prevent similar rounding errors. I learned the importance of extreme testing rigor.
Personalization tip: If you haven’t had a production incident, talk about a near-miss or a testing failure that caught a critical bug before deployment. The key is showing you handle pressure maturely.
Tell me about a time you disagreed with a team member’s technical approach.
Why they ask: They want to see if you can collaborate, respectfully challenge ideas, and navigate disagreement.
STAR framework:
Situation: A colleague proposed storing all user balances in a single array instead of a mapping for easier iteration. I thought this was a performance mistake.
Task: I needed to raise my concern without dismissing their idea.
Action: I didn’t just say “that’s wrong.” I asked questions to understand their reasoning. They wanted to iterate through all balances for a community airdrop. Fair point.
I then proposed a hybrid approach: keep balances in a mapping (O(1) lookup, common operation) and use The Graph to index balances for iteration when needed. We discussed gas costs, index query latency, and complexity.
We tested both approaches and ran benchmarks. The data showed the mapping + indexing approach was superior for our use case.
Result: We implemented the mapping approach. The colleague learned a valuable lesson about optimization trade-offs. I learned that not every disagreement is about being right—it’s about finding the best solution together.
Personalization tip: Choose a real disagreement where both perspectives had merit. Avoid making yourself sound always right or always wrong.
Tell me about a time you had to communicate complex technical concepts to non-technical stakeholders.
Why they ask: Web3 adoption depends on clear communication. They want developers who can bridge technical and business worlds.
STAR framework:
Situation: I was building an NFT project, and the marketing team didn’t understand why certain tokenomics decisions mattered.
Task: I needed to explain why the contract’s minting schedule affected the token’s long-term value without overwhelming them with Solidity details.
Action: I used an analogy: “Imagine if stocks split every time a company was worth $1B. The stock would be diluted, and existing holders would be frustrated.” That framed why the NFT supply mattered.
I created a simple spreadsheet showing how different minting schedules affected token distribution over time. I showed three scenarios with different outcomes, letting non-technical people see cause and effect.
I avoided jargon. Instead of “tokenomics,” I said “how many tokens exist and how they’re distributed.” Instead of “proof of stake,” I said “how the network validates transactions.”
Result: Marketing understood the constraints and helped advocate for the better tokenomics design. The project launched with alignment across teams.
Personalization tip: Pick a moment where clarity actually changed an outcome, not just a time you presented something.
Tell me about a time you went above and beyond for a project.
Why they ask: This tests initiative, ownership, and passion for the work.
STAR framework:
Situation: We were launching a DeFi protocol and noticed during late-stage testing that a subtle edge case could cause bad debt—the protocol could go insolvent.
Task: The bug needed fixing before mainnet launch, and we were already behind schedule.
Action: Instead of just reporting the bug, I debugged it myself, designed a fix, and implemented it. I stayed up rewriting the liquidation logic to be more robust.
I tested the fix extensively, ran simulations, and proposed it to the team. When the team had concerns about safety, I sat with the auditors to walk through the changes and ensure they were sound.
Result: We launched on-time with a much more robust protocol. The team appreciated that I didn’t just identify a problem—I solved it. The protocol has been running for a year without liquidation-related issues.
Personalization tip: Don’t make it sound like you single-handedly saved the day. Focus on your specific contribution and teamwork.
Describe a time you failed and what you learned.
Why they ask: They want to see self-awareness and growth mindset. Everyone fails; how you handle it matters.
STAR framework:
Situation: I proposed a gas optimization for a token contract that I was very confident about. I tested it on a small testnet scenario.
Task: The optimization went into production, and I expected to see a 30% improvement.
Action: A user reported that their token transfers were now slower—not in gas cost, but in execution time. I investigated and found that my optimization had a logic bug that was causing unnecessary loops in the average case.
I immediately reverted the contract (we had a proxy pattern) and investigated what went wrong. I’d tested the best-case scenario but not the average case. I learned that optimization requires comprehensive testing, not just the happy path.
Result: I fixed the bug by adding average-case tests to my test suite. The contract now runs 15% faster safely. More importantly, I became more rigorous about testing assumptions, not just outcomes.
Personalization tip: Pick a real failure with a concrete lesson. Show you’ve actually integrated the learning into your subsequent work.
Technical Interview Questions for Web3 Developers
Technical questions assess your depth of knowledge and problem-solving approach.
Design a simple token contract. Walk me through your implementation.
Why they ask: This tests your smart contract design thinking, security mindset, and ability to code under interview pressure.
Answer framework:
Rather than jumping to code, think out loud:
-
Define requirements: What kind of token? ERC-20 standard? Does it have minting/burning? Access control?
-
Outline the core data structures: I’d need a mapping from address to balances, an allowed mapping for approve/transferFrom, and totalSupply.
-
Implement core functions: Transfer (update balances), transferFrom (check allowance), approve.
-
Consider security: Validate inputs (non-zero addresses, sufficient balance), emit events, use SafeMath or check for overflow (though Solidity 0.8+ does this automatically).
-
Use established patterns: Rather than write from scratch, I’d inherit from OpenZeppelin’s ERC20. It’s tested, audited, and the standard.
Sample implementation (conceptual):
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract SimpleToken is ERC20 {
constructor() ERC20("SimpleToken", "SMP") {
_mint(msg.sender, 1000000 * 10 ** 18);
}
}
Why this approach: I’m not writing low-level ERC20 from scratch in an interview. I’m demonstrating that I understand standards, use battle-tested code, and can extend existing contracts. In a real interview, they might ask me to implement a specific function from scratch—that’s when I write more detailed code.
Personalization tip: If they push you to implement more manually, walk through it step-by-step. “First, I’d create a mapping to track balances. Then I’d need an event for transfers. Let me write that…”
How would you design a simple lending protocol? Outline the architecture.
Why they ask: This tests your ability to think about systems design, architecture, and the interaction between multiple smart contracts.
Answer framework:
Think about the key components:
-
Asset management: Users deposit assets and earn interest. I need a Vault contract that holds deposits, tracks individual balances, and mints/burns vault tokens (shares) representing claims.
-
Borrowing: Borrowers can borrow assets by collateralizing other assets. I need a Lending contract that manages borrow positions, collateral ratios, and enforces constraints.
-
Interest rates: The protocol needs to determine what rate borrowers pay. I’d use a model based on utilization: if 80% of assets are lent out, rates are high; if only 10% are lent, rates are low.
-
Liquidation: If a borrower’s collateral value drops below the minimum ratio, their position can be liquidated. I need logic to allow liquidators to repay debt and claim discounted collateral.
-
Oracle integration: The protocol needs real-time asset prices. I’d use Chainlink or another oracle to fetch prices.
Architecture overview:
User Deposits -> Vault Contract -> Holds Assets, Mints Shares
↓
Lending Contract
(borrows from Vault)
↓
Liquidation Engine
(monitors collateral)
↓
Oracle (price feeds)
Key considerations I’d mention:
- Flash loan attacks: I’d add checks to prevent borrowers from using flash loans to artificially spike prices and liquidate themselves to profit.
- Precision: Interest calculations need to be precise. I’d use fixed-point arithmetic.
- Governance: Over time, parameters (interest curves, collateral factors) change via governance, not hardcoded.
Personalization tip: If you’ve studied a real protocol (Aave, Compound), mention it. “This is similar to how Aave structures lending pools—they use their aTokens similarly to vault shares.”
Walk me through how you’d debug a failed transaction on Ethereum.
Why they ask: This tests your practical troubleshooting skills and familiarity with tools.
Answer framework:
-
Get the transaction hash: I’d ask the user for the transaction hash or look it up by address/time.
-
Check Etherscan: I’d go to etherscan.io, search the tx hash, and check the status. If it failed, the page shows “failed.”
-
Look at the error: Etherscan often shows a revert reason. If it says “insufficient balance,” that’s your answer.
-
Use Tenderly: For more detail, I’d use Tenderly’s debugger. I’d paste the tx hash and it shows me exactly which line in the contract caused the revert and what variables contained at that point.
-
Reproduce locally: I’d take the transaction’s input data, replay it on a local fork of mainnet (using Hardhat’s forking), and step through the code.
-
Check contract state: Sometimes the issue is state-dependent. I’d verify that the contract wasn’t in an unexpected state (e.g., paused, out of funds).
-
Test the fix: I’d write a test case that reproduces the failure, confirm my fix resolves it, and test edge cases.
Tools I’d use:
- Etherscan (first look)
- Tenderly (detailed debugging)
- Hardhat with
--vvvflag (verbose logging) console.login Solidity (for debugging)
Personalization tip: Mention a specific transaction you’ve debugged. “I once debugged a transaction that failed in an external call. Tenderly showed me that the external contract had reverted—the original contract was fine.”
Explain how you’d implement access control in a smart contract. Code example.
Why they ask: Access control is fundamental to security. This tests whether you understand authorization patterns and can implement them safely.
Answer framework:
There are a few approaches:
-
Simple owner pattern: Only the deployer can call certain functions.
-
Role-based access control: Different addresses have different roles (admin, operator, user), each with specific permissions.
-
Permission-based: More granular; each address has specific permissions.
For most projects, I’d use OpenZeppelin’s AccessControl:
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
contract MyContract is AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER");
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN");
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
// minting logic
}
function pause() public onlyRole(ADMIN_ROLE) {
// pause logic
}
}
Why this pattern:
- Roles are flexible. I can grant/revoke roles without upgrading the contract.
- It’s audited and widely used.
- It supports multiple admins (unlike simple ownership).
Edge cases to mention:
- Admins should be multisigs (not single EOAs).
- I’d use a timelock so role changes aren’t immediate—gives users time to react.
Personalization tip: If you’ve implemented something different, explain it. “For this project, we used a simpler single-owner pattern because it was a small protocol and we wanted to minimize complexity.”
How would you test a smart contract that depends on external data (like an oracle)?
Why they ask: Testing oracle-dependent contracts is tricky because external calls are unpredictable. This tests your testing sophistication.
Answer framework:
The challenge: oracles are external systems you don’t control. In testing, you can’t rely on real oracle responses.
Strategy:
- Mock the oracle: I’d create a mock oracle contract that returns fixed data I control.
contract MockOracle {
function getPrice(address token) public pure returns (uint256) {
return 100e18; // fixed price
}
}
- Inject the mock: My contract accepts the oracle address in its constructor:
contract Lending {
IOracle public oracle;
constructor(address _oracle) {
oracle = IOracle(_oracle);
}
}
- Test with different prices: In my tests, I deploy the mock and set different prices to test different scenarios:
function testLiquidationAtLowPrice() public {
mockOracle.setPrice(50e18); // asset worth less
// test that liquidation can occur
}
- Test oracle failures: What if the oracle is down or returns bad data? I’d test that the contract handles it gracefully (doesn’t liquidate unnecessarily, or has a fallback).
5