Web3 Interview Questions - Hard
Hard-level Web3 interview questions covering MEV, zero-knowledge proofs, advanced DeFi, and protocol design.
Q1: Explain MEV (Maximal Extractable Value) and mitigation strategies.
Answer:
Sandwich Attack
MEV Mitigation
1// ✅ Use private mempools (Flashbots)
2// ✅ Implement slippage protection
3contract MEVProtected {
4 function swapWithSlippage(
5 address tokenIn,
6 address tokenOut,
7 uint256 amountIn,
8 uint256 minAmountOut, // Minimum acceptable output
9 uint256 deadline
10 ) external {
11 require(block.timestamp <= deadline, "Expired");
12
13 uint256 amountOut = swap(tokenIn, tokenOut, amountIn);
14 require(amountOut >= minAmountOut, "Slippage too high");
15 }
16}
17
18// ✅ Use commit-reveal schemes
19contract CommitReveal {
20 mapping(address => bytes32) public commitments;
21
22 function commit(bytes32 hash) external {
23 commitments[msg.sender] = hash;
24 }
25
26 function reveal(
27 uint256 amount,
28 bytes32 secret
29 ) external {
30 require(
31 keccak256(abi.encodePacked(amount, secret)) == commitments[msg.sender],
32 "Invalid"
33 );
34 // Execute trade
35 }
36}
Flashbots Integration
1const { FlashbotsBundleProvider } = require('@flashbots/ethers-provider-bundle');
2
3async function sendPrivateTransaction() {
4 const flashbotsProvider = await FlashbotsBundleProvider.create(
5 provider,
6 authSigner,
7 'https://relay.flashbots.net'
8 );
9
10 const signedTransactions = await flashbotsProvider.signBundle([
11 {
12 signer: wallet,
13 transaction: {
14 to: contractAddress,
15 data: txData,
16 gasLimit: 200000
17 }
18 }
19 ]);
20
21 const simulation = await flashbotsProvider.simulate(
22 signedTransactions,
23 targetBlockNumber
24 );
25
26 if (simulation.firstRevert) {
27 console.log('Simulation failed');
28 return;
29 }
30
31 const bundleSubmission = await flashbotsProvider.sendRawBundle(
32 signedTransactions,
33 targetBlockNumber
34 );
35}
Q2: Explain zero-knowledge proofs and zk-SNARKs.
Answer:
ZK Proof Properties
zk-SNARK Example
1// Verifier contract (generated by circom/snarkjs)
2contract Verifier {
3 struct VerifyingKey {
4 Pairing.G1Point alpha;
5 Pairing.G2Point beta;
6 Pairing.G2Point gamma;
7 Pairing.G2Point delta;
8 Pairing.G1Point[] gamma_abc;
9 }
10
11 struct Proof {
12 Pairing.G1Point a;
13 Pairing.G2Point b;
14 Pairing.G1Point c;
15 }
16
17 function verify(
18 uint[2] memory a,
19 uint[2][2] memory b,
20 uint[2] memory c,
21 uint[1] memory input
22 ) public view returns (bool) {
23 Proof memory proof;
24 proof.a = Pairing.G1Point(a[0], a[1]);
25 proof.b = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
26 proof.c = Pairing.G1Point(c[0], c[1]);
27
28 VerifyingKey memory vk = verifyingKey();
29
30 // Verify the proof
31 return verifyProof(proof, input, vk);
32 }
33}
34
35// Usage: Private transactions
36contract PrivateTransfer {
37 Verifier public verifier;
38
39 mapping(bytes32 => bool) public nullifiers;
40
41 function transfer(
42 uint[2] memory a,
43 uint[2][2] memory b,
44 uint[2] memory c,
45 bytes32 nullifier,
46 bytes32 commitment
47 ) external {
48 require(!nullifiers[nullifier], "Already spent");
49 require(verifier.verify(a, b, c, [uint(nullifier)]), "Invalid proof");
50
51 nullifiers[nullifier] = true;
52 // Transfer without revealing amount or recipient
53 }
54}
ZK Applications
Q3: Design a complex DeFi protocol (lending with liquidations).
Answer:
Protocol Architecture
1contract LendingProtocol {
2 struct Market {
3 uint256 totalSupply;
4 uint256 totalBorrows;
5 uint256 supplyRate;
6 uint256 borrowRate;
7 uint256 collateralFactor; // 75% = 0.75e18
8 uint256 liquidationThreshold; // 80% = 0.80e18
9 uint256 liquidationBonus; // 5% = 0.05e18
10 }
11
12 mapping(address => Market) public markets;
13 mapping(address => mapping(address => uint256)) public supplied;
14 mapping(address => mapping(address => uint256)) public borrowed;
15
16 // Supply assets
17 function supply(address token, uint256 amount) external {
18 IERC20(token).transferFrom(msg.sender, address(this), amount);
19 supplied[msg.sender][token] += amount;
20 markets[token].totalSupply += amount;
21
22 updateInterestRates(token);
23 }
24
25 // Borrow assets
26 function borrow(address token, uint256 amount) external {
27 require(
28 getAccountLiquidity(msg.sender) >= amount,
29 "Insufficient collateral"
30 );
31
32 borrowed[msg.sender][token] += amount;
33 markets[token].totalBorrows += amount;
34
35 IERC20(token).transfer(msg.sender, amount);
36 updateInterestRates(token);
37 }
38
39 // Calculate account liquidity
40 function getAccountLiquidity(address account)
41 public
42 view
43 returns (uint256)
44 {
45 uint256 totalCollateral = 0;
46 uint256 totalBorrowed = 0;
47
48 address[] memory tokens = getMarketTokens();
49
50 for (uint256 i = 0; i < tokens.length; i++) {
51 address token = tokens[i];
52
53 uint256 supplyValue = supplied[account][token] *
54 getPrice(token) *
55 markets[token].collateralFactor / 1e18;
56
57 uint256 borrowValue = borrowed[account][token] *
58 getPrice(token);
59
60 totalCollateral += supplyValue;
61 totalBorrowed += borrowValue;
62 }
63
64 return totalCollateral > totalBorrowed ?
65 totalCollateral - totalBorrowed : 0;
66 }
67
68 // Liquidate under-collateralized position
69 function liquidate(
70 address borrower,
71 address borrowToken,
72 address collateralToken,
73 uint256 repayAmount
74 ) external {
75 require(
76 isLiquidatable(borrower),
77 "Account not liquidatable"
78 );
79
80 // Repay borrowed amount
81 IERC20(borrowToken).transferFrom(msg.sender, address(this), repayAmount);
82 borrowed[borrower][borrowToken] -= repayAmount;
83 markets[borrowToken].totalBorrows -= repayAmount;
84
85 // Calculate collateral to seize
86 uint256 collateralValue = repayAmount *
87 getPrice(borrowToken) *
88 (1e18 + markets[collateralToken].liquidationBonus) /
89 (getPrice(collateralToken) * 1e18);
90
91 // Transfer collateral to liquidator
92 supplied[borrower][collateralToken] -= collateralValue;
93 IERC20(collateralToken).transfer(msg.sender, collateralValue);
94 }
95
96 function isLiquidatable(address account) public view returns (bool) {
97 uint256 totalCollateral = 0;
98 uint256 totalBorrowed = 0;
99
100 address[] memory tokens = getMarketTokens();
101
102 for (uint256 i = 0; i < tokens.length; i++) {
103 address token = tokens[i];
104
105 uint256 supplyValue = supplied[account][token] *
106 getPrice(token) *
107 markets[token].liquidationThreshold / 1e18;
108
109 uint256 borrowValue = borrowed[account][token] *
110 getPrice(token);
111
112 totalCollateral += supplyValue;
113 totalBorrowed += borrowValue;
114 }
115
116 return totalBorrowed > totalCollateral;
117 }
118
119 // Interest rate model (simplified)
120 function updateInterestRates(address token) internal {
121 Market storage market = markets[token];
122
123 uint256 utilizationRate = market.totalSupply > 0 ?
124 market.totalBorrows * 1e18 / market.totalSupply : 0;
125
126 // Simple linear model
127 market.borrowRate = 2e16 + (utilizationRate * 18e16 / 1e18); // 2% + utilization * 18%
128 market.supplyRate = market.borrowRate * utilizationRate / 1e18;
129 }
130
131 function getPrice(address token) internal view returns (uint256) {
132 // Get price from oracle (Chainlink, etc.)
133 return IOracle(oracle).getPrice(token);
134 }
135
136 function getMarketTokens() internal view returns (address[] memory) {
137 // Return list of supported tokens
138 }
139}
Liquidation Flow
Q4: Implement cross-chain bridge protocol.
Answer:
Bridge Architecture
Source Chain Contract
1contract SourceBridge {
2 address public validator;
3 mapping(bytes32 => bool) public processedDeposits;
4
5 event Deposit(
6 address indexed user,
7 address indexed token,
8 uint256 amount,
9 uint256 indexed destinationChainId,
10 uint256 nonce
11 );
12
13 event Withdrawal(
14 address indexed user,
15 address indexed token,
16 uint256 amount,
17 bytes32 indexed depositHash
18 );
19
20 function deposit(
21 address token,
22 uint256 amount,
23 uint256 destinationChainId
24 ) external {
25 IERC20(token).transferFrom(msg.sender, address(this), amount);
26
27 uint256 nonce = getNonce(msg.sender);
28
29 emit Deposit(
30 msg.sender,
31 token,
32 amount,
33 destinationChainId,
34 nonce
35 );
36 }
37
38 function withdraw(
39 address user,
40 address token,
41 uint256 amount,
42 uint256 sourceChainId,
43 uint256 nonce,
44 bytes[] memory signatures
45 ) external {
46 bytes32 depositHash = keccak256(
47 abi.encodePacked(user, token, amount, sourceChainId, nonce)
48 );
49
50 require(!processedDeposits[depositHash], "Already processed");
51 require(verifySignatures(depositHash, signatures), "Invalid signatures");
52
53 processedDeposits[depositHash] = true;
54 IERC20(token).transfer(user, amount);
55
56 emit Withdrawal(user, token, amount, depositHash);
57 }
58
59 function verifySignatures(
60 bytes32 hash,
61 bytes[] memory signatures
62 ) internal view returns (bool) {
63 require(signatures.length >= 2, "Need 2+ signatures");
64
65 bytes32 ethSignedHash = keccak256(
66 abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)
67 );
68
69 address[] memory signers = new address[](signatures.length);
70
71 for (uint256 i = 0; i < signatures.length; i++) {
72 address signer = recoverSigner(ethSignedHash, signatures[i]);
73 require(isValidator(signer), "Invalid validator");
74
75 // Check for duplicate signers
76 for (uint256 j = 0; j < i; j++) {
77 require(signers[j] != signer, "Duplicate signer");
78 }
79
80 signers[i] = signer;
81 }
82
83 return true;
84 }
85
86 function recoverSigner(bytes32 hash, bytes memory signature)
87 internal
88 pure
89 returns (address)
90 {
91 (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature);
92 return ecrecover(hash, v, r, s);
93 }
94
95 function splitSignature(bytes memory sig)
96 internal
97 pure
98 returns (bytes32 r, bytes32 s, uint8 v)
99 {
100 require(sig.length == 65, "Invalid signature length");
101
102 assembly {
103 r := mload(add(sig, 32))
104 s := mload(add(sig, 64))
105 v := byte(0, mload(add(sig, 96)))
106 }
107 }
108
109 function getNonce(address user) internal view returns (uint256) {
110 // Implementation
111 }
112
113 function isValidator(address addr) internal view returns (bool) {
114 // Check if address is authorized validator
115 }
116}
Destination Chain Contract
1contract DestinationBridge {
2 mapping(bytes32 => bool) public processedWithdrawals;
3
4 event Mint(
5 address indexed user,
6 address indexed token,
7 uint256 amount,
8 bytes32 indexed sourceDepositHash
9 );
10
11 function mint(
12 address user,
13 address token,
14 uint256 amount,
15 uint256 sourceChainId,
16 uint256 nonce,
17 bytes[] memory signatures
18 ) external {
19 bytes32 depositHash = keccak256(
20 abi.encodePacked(user, token, amount, sourceChainId, nonce)
21 );
22
23 require(!processedWithdrawals[depositHash], "Already processed");
24 require(verifySignatures(depositHash, signatures), "Invalid signatures");
25
26 processedWithdrawals[depositHash] = true;
27
28 // Mint wrapped token
29 IWrappedToken(token).mint(user, amount);
30
31 emit Mint(user, token, amount, depositHash);
32 }
33
34 function burn(address token, uint256 amount, uint256 destinationChainId) external {
35 IWrappedToken(token).burn(msg.sender, amount);
36
37 emit Burn(msg.sender, token, amount, destinationChainId);
38 }
39}
Q5: Explain and implement governance mechanisms.
Answer:
Governance Token
1import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
2
3contract GovernanceToken is ERC20Votes {
4 constructor() ERC20("Governance Token", "GOV") ERC20Permit("Governance Token") {
5 _mint(msg.sender, 1000000 * 10**18);
6 }
7
8 function _afterTokenTransfer(
9 address from,
10 address to,
11 uint256 amount
12 ) internal override(ERC20Votes) {
13 super._afterTokenTransfer(from, to, amount);
14 }
15
16 function _mint(address to, uint256 amount) internal override(ERC20Votes) {
17 super._mint(to, amount);
18 }
19
20 function _burn(address account, uint256 amount) internal override(ERC20Votes) {
21 super._burn(account, amount);
22 }
23}
Governor Contract
1contract Governor {
2 struct Proposal {
3 uint256 id;
4 address proposer;
5 address[] targets;
6 uint256[] values;
7 bytes[] calldatas;
8 uint256 startBlock;
9 uint256 endBlock;
10 uint256 forVotes;
11 uint256 againstVotes;
12 uint256 abstainVotes;
13 bool executed;
14 bool canceled;
15 mapping(address => Receipt) receipts;
16 }
17
18 struct Receipt {
19 bool hasVoted;
20 uint8 support; // 0=against, 1=for, 2=abstain
21 uint256 votes;
22 }
23
24 GovernanceToken public token;
25 uint256 public proposalCount;
26 uint256 public votingDelay = 1; // 1 block
27 uint256 public votingPeriod = 50400; // ~1 week
28 uint256 public proposalThreshold = 100000 * 10**18; // 100k tokens
29 uint256 public quorumVotes = 400000 * 10**18; // 400k tokens
30
31 mapping(uint256 => Proposal) public proposals;
32
33 event ProposalCreated(
34 uint256 id,
35 address proposer,
36 address[] targets,
37 string description
38 );
39
40 event VoteCast(
41 address indexed voter,
42 uint256 proposalId,
43 uint8 support,
44 uint256 votes
45 );
46
47 function propose(
48 address[] memory targets,
49 uint256[] memory values,
50 bytes[] memory calldatas,
51 string memory description
52 ) external returns (uint256) {
53 require(
54 token.getPriorVotes(msg.sender, block.number - 1) > proposalThreshold,
55 "Proposer votes below threshold"
56 );
57
58 uint256 proposalId = ++proposalCount;
59 Proposal storage proposal = proposals[proposalId];
60
61 proposal.id = proposalId;
62 proposal.proposer = msg.sender;
63 proposal.targets = targets;
64 proposal.values = values;
65 proposal.calldatas = calldatas;
66 proposal.startBlock = block.number + votingDelay;
67 proposal.endBlock = block.number + votingDelay + votingPeriod;
68
69 emit ProposalCreated(proposalId, msg.sender, targets, description);
70
71 return proposalId;
72 }
73
74 function castVote(uint256 proposalId, uint8 support) external {
75 Proposal storage proposal = proposals[proposalId];
76
77 require(block.number >= proposal.startBlock, "Voting not started");
78 require(block.number <= proposal.endBlock, "Voting ended");
79
80 Receipt storage receipt = proposal.receipts[msg.sender];
81 require(!receipt.hasVoted, "Already voted");
82
83 uint256 votes = token.getPriorVotes(msg.sender, proposal.startBlock);
84
85 if (support == 0) {
86 proposal.againstVotes += votes;
87 } else if (support == 1) {
88 proposal.forVotes += votes;
89 } else if (support == 2) {
90 proposal.abstainVotes += votes;
91 } else {
92 revert("Invalid support value");
93 }
94
95 receipt.hasVoted = true;
96 receipt.support = support;
97 receipt.votes = votes;
98
99 emit VoteCast(msg.sender, proposalId, support, votes);
100 }
101
102 function execute(uint256 proposalId) external payable {
103 Proposal storage proposal = proposals[proposalId];
104
105 require(!proposal.executed, "Already executed");
106 require(block.number > proposal.endBlock, "Voting not ended");
107 require(proposal.forVotes > proposal.againstVotes, "Proposal defeated");
108 require(proposal.forVotes >= quorumVotes, "Quorum not reached");
109
110 proposal.executed = true;
111
112 for (uint256 i = 0; i < proposal.targets.length; i++) {
113 (bool success, ) = proposal.targets[i].call{value: proposal.values[i]}(
114 proposal.calldatas[i]
115 );
116 require(success, "Transaction execution reverted");
117 }
118 }
119}
Timelock
1contract Timelock {
2 uint256 public constant MINIMUM_DELAY = 2 days;
3 uint256 public constant MAXIMUM_DELAY = 30 days;
4
5 mapping(bytes32 => bool) public queuedTransactions;
6
7 event QueueTransaction(
8 bytes32 indexed txHash,
9 address indexed target,
10 uint256 value,
11 bytes data,
12 uint256 eta
13 );
14
15 event ExecuteTransaction(
16 bytes32 indexed txHash,
17 address indexed target,
18 uint256 value,
19 bytes data,
20 uint256 eta
21 );
22
23 function queueTransaction(
24 address target,
25 uint256 value,
26 bytes memory data,
27 uint256 eta
28 ) public returns (bytes32) {
29 require(eta >= block.timestamp + MINIMUM_DELAY, "Delay too short");
30
31 bytes32 txHash = keccak256(abi.encode(target, value, data, eta));
32 queuedTransactions[txHash] = true;
33
34 emit QueueTransaction(txHash, target, value, data, eta);
35 return txHash;
36 }
37
38 function executeTransaction(
39 address target,
40 uint256 value,
41 bytes memory data,
42 uint256 eta
43 ) public payable returns (bytes memory) {
44 bytes32 txHash = keccak256(abi.encode(target, value, data, eta));
45
46 require(queuedTransactions[txHash], "Transaction not queued");
47 require(block.timestamp >= eta, "Transaction not ready");
48 require(block.timestamp <= eta + MAXIMUM_DELAY, "Transaction stale");
49
50 queuedTransactions[txHash] = false;
51
52 (bool success, bytes memory returnData) = target.call{value: value}(data);
53 require(success, "Transaction execution reverted");
54
55 emit ExecuteTransaction(txHash, target, value, data, eta);
56 return returnData;
57 }
58}
Q6: Implement EIP-2535 (Diamond Pattern) for modular contracts.
Answer:
Diamond Storage Pattern
1library LibDiamond {
2 bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");
3
4 struct FacetAddressAndPosition {
5 address facetAddress;
6 uint96 functionSelectorPosition;
7 }
8
9 struct DiamondStorage {
10 mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
11 bytes4[] selectors;
12 mapping(bytes4 => bool) supportedInterfaces;
13 address contractOwner;
14 }
15
16 function diamondStorage() internal pure returns (DiamondStorage storage ds) {
17 bytes32 position = DIAMOND_STORAGE_POSITION;
18 assembly {
19 ds.slot := position
20 }
21 }
22}
23
24contract Diamond {
25 constructor(address _owner, address _diamondCutFacet) {
26 LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
27 ds.contractOwner = _owner;
28
29 // Add diamondCut function
30 bytes4 selector = IDiamondCut.diamondCut.selector;
31 ds.selectorToFacetAndPosition[selector].facetAddress = _diamondCutFacet;
32 ds.selectors.push(selector);
33 }
34
35 fallback() external payable {
36 LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
37 address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
38 require(facet != address(0), "Function does not exist");
39
40 assembly {
41 calldatacopy(0, 0, calldatasize())
42 let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
43 returndatacopy(0, 0, returndatasize())
44 switch result
45 case 0 { revert(0, returndatasize()) }
46 default { return(0, returndatasize()) }
47 }
48 }
49}
50
51// Facet example
52contract TokenFacet {
53 bytes32 constant TOKEN_STORAGE_POSITION = keccak256("token.storage");
54
55 struct TokenStorage {
56 mapping(address => uint256) balances;
57 uint256 totalSupply;
58 }
59
60 function tokenStorage() internal pure returns (TokenStorage storage ts) {
61 bytes32 position = TOKEN_STORAGE_POSITION;
62 assembly {
63 ts.slot := position
64 }
65 }
66
67 function balanceOf(address account) external view returns (uint256) {
68 return tokenStorage().balances[account];
69 }
70
71 function transfer(address to, uint256 amount) external returns (bool) {
72 TokenStorage storage ts = tokenStorage();
73 require(ts.balances[msg.sender] >= amount, "Insufficient balance");
74
75 ts.balances[msg.sender] -= amount;
76 ts.balances[to] += amount;
77
78 return true;
79 }
80}
Q7: Explain account abstraction (EIP-4337).
Answer:
UserOperation
1struct UserOperation {
2 address sender;
3 uint256 nonce;
4 bytes initCode;
5 bytes callData;
6 uint256 callGasLimit;
7 uint256 verificationGasLimit;
8 uint256 preVerificationGas;
9 uint256 maxFeePerGas;
10 uint256 maxPriorityFeePerGas;
11 bytes paymasterAndData;
12 bytes signature;
13}
14
15interface IAccount {
16 function validateUserOp(
17 UserOperation calldata userOp,
18 bytes32 userOpHash,
19 uint256 missingAccountFunds
20 ) external returns (uint256 validationData);
21}
22
23contract SimpleAccount is IAccount {
24 address public owner;
25 uint256 public nonce;
26
27 function validateUserOp(
28 UserOperation calldata userOp,
29 bytes32 userOpHash,
30 uint256 missingAccountFunds
31 ) external override returns (uint256) {
32 require(msg.sender == entryPoint, "Only EntryPoint");
33
34 bytes32 hash = keccak256(abi.encode(userOpHash, block.chainid, address(this)));
35 address signer = ECDSA.recover(hash, userOp.signature);
36
37 require(signer == owner, "Invalid signature");
38
39 if (missingAccountFunds > 0) {
40 (bool success, ) = payable(msg.sender).call{value: missingAccountFunds}("");
41 require(success, "Payment failed");
42 }
43
44 return 0; // Success
45 }
46
47 function execute(address dest, uint256 value, bytes calldata func) external {
48 require(msg.sender == entryPoint, "Only EntryPoint");
49 (bool success, ) = dest.call{value: value}(func);
50 require(success, "Execution failed");
51 }
52}
Paymaster (Gas Sponsorship)
1contract Paymaster {
2 function validatePaymasterUserOp(
3 UserOperation calldata userOp,
4 bytes32 userOpHash,
5 uint256 maxCost
6 ) external returns (bytes memory context, uint256 validationData) {
7 // Verify user is eligible for sponsorship
8 require(isEligible(userOp.sender), "Not eligible");
9
10 // Check paymaster has enough funds
11 require(address(this).balance >= maxCost, "Insufficient funds");
12
13 return ("", 0);
14 }
15
16 function postOp(
17 PostOpMode mode,
18 bytes calldata context,
19 uint256 actualGasCost
20 ) external {
21 // Post-operation logic (e.g., charge user in tokens)
22 }
23}
Q8: Design a decentralized exchange with concentrated liquidity (Uniswap V3).
Answer:
Position Management
1contract ConcentratedLiquidityPool {
2 struct Position {
3 uint128 liquidity;
4 int24 tickLower;
5 int24 tickUpper;
6 uint256 feeGrowthInside0LastX128;
7 uint256 feeGrowthInside1LastX128;
8 uint128 tokensOwed0;
9 uint128 tokensOwed1;
10 }
11
12 mapping(bytes32 => Position) public positions;
13
14 int24 public tick;
15 uint160 public sqrtPriceX96;
16
17 function mint(
18 address recipient,
19 int24 tickLower,
20 int24 tickUpper,
21 uint128 amount
22 ) external returns (uint256 amount0, uint256 amount1) {
23 require(tickLower < tickUpper, "Invalid ticks");
24 require(amount > 0, "Amount must be > 0");
25
26 bytes32 positionKey = keccak256(
27 abi.encodePacked(recipient, tickLower, tickUpper)
28 );
29
30 Position storage position = positions[positionKey];
31
32 // Calculate token amounts needed
33 (amount0, amount1) = calculateAmounts(
34 tickLower,
35 tickUpper,
36 amount,
37 tick,
38 sqrtPriceX96
39 );
40
41 // Update position
42 position.liquidity += amount;
43 position.tickLower = tickLower;
44 position.tickUpper = tickUpper;
45
46 // Transfer tokens
47 if (amount0 > 0) token0.transferFrom(msg.sender, address(this), amount0);
48 if (amount1 > 0) token1.transferFrom(msg.sender, address(this), amount1);
49
50 return (amount0, amount1);
51 }
52
53 function swap(
54 bool zeroForOne,
55 int256 amountSpecified,
56 uint160 sqrtPriceLimitX96
57 ) external returns (int256 amount0, int256 amount1) {
58 SwapState memory state = SwapState({
59 amountSpecifiedRemaining: amountSpecified,
60 amountCalculated: 0,
61 sqrtPriceX96: sqrtPriceX96,
62 tick: tick,
63 liquidity: liquidity
64 });
65
66 while (state.amountSpecifiedRemaining != 0) {
67 StepComputations memory step;
68
69 step.sqrtPriceStartX96 = state.sqrtPriceX96;
70
71 // Get next tick
72 (step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord(
73 state.tick,
74 tickSpacing,
75 zeroForOne
76 );
77
78 // Compute swap step
79 (state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) =
80 SwapMath.computeSwapStep(
81 state.sqrtPriceX96,
82 getSqrtRatioAtTick(step.tickNext),
83 state.liquidity,
84 state.amountSpecifiedRemaining,
85 fee
86 );
87
88 state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount);
89 state.amountCalculated += step.amountOut;
90
91 // Cross tick if needed
92 if (state.sqrtPriceX96 == getSqrtRatioAtTick(step.tickNext)) {
93 int128 liquidityNet = ticks[step.tickNext].liquidityNet;
94 state.liquidity = zeroForOne ?
95 state.liquidity - liquidityNet :
96 state.liquidity + liquidityNet;
97 state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext;
98 }
99 }
100
101 // Update global state
102 tick = state.tick;
103 sqrtPriceX96 = state.sqrtPriceX96;
104
105 return (amount0, amount1);
106 }
107}
Q9: Implement a decentralized identity system.
Answer:
DID Registry
1contract DIDRegistry {
2 struct DIDDocument {
3 address controller;
4 string publicKey;
5 string serviceEndpoint;
6 uint256 created;
7 uint256 updated;
8 bool revoked;
9 }
10
11 mapping(bytes32 => DIDDocument) public dids;
12 mapping(bytes32 => mapping(bytes32 => bool)) public credentials;
13
14 event DIDCreated(bytes32 indexed did, address controller);
15 event DIDUpdated(bytes32 indexed did);
16 event CredentialIssued(bytes32 indexed did, bytes32 indexed credentialHash);
17
18 function createDID(
19 bytes32 did,
20 string memory publicKey,
21 string memory serviceEndpoint
22 ) external {
23 require(dids[did].controller == address(0), "DID already exists");
24
25 dids[did] = DIDDocument({
26 controller: msg.sender,
27 publicKey: publicKey,
28 serviceEndpoint: serviceEndpoint,
29 created: block.timestamp,
30 updated: block.timestamp,
31 revoked: false
32 });
33
34 emit DIDCreated(did, msg.sender);
35 }
36
37 function issueCredential(
38 bytes32 did,
39 bytes32 credentialHash,
40 bytes memory signature
41 ) external {
42 require(!dids[did].revoked, "DID revoked");
43 require(verifyIssuer(msg.sender), "Not authorized issuer");
44
45 credentials[did][credentialHash] = true;
46
47 emit CredentialIssued(did, credentialHash);
48 }
49
50 function verifyCredential(
51 bytes32 did,
52 bytes32 credentialHash
53 ) external view returns (bool) {
54 return credentials[did][credentialHash] && !dids[did].revoked;
55 }
56
57 function revokeDID(bytes32 did) external {
58 require(dids[did].controller == msg.sender, "Not controller");
59 dids[did].revoked = true;
60 }
61}
Q10: Explain and implement a DAO treasury management system.
Answer:
Treasury Contract
1contract DAOTreasury {
2 struct Budget {
3 uint256 amount;
4 uint256 spent;
5 uint256 startTime;
6 uint256 endTime;
7 address recipient;
8 bool active;
9 }
10
11 struct VestingSchedule {
12 uint256 totalAmount;
13 uint256 released;
14 uint256 startTime;
15 uint256 duration;
16 address beneficiary;
17 }
18
19 mapping(uint256 => Budget) public budgets;
20 mapping(uint256 => VestingSchedule) public vestingSchedules;
21
22 uint256 public budgetCount;
23 uint256 public vestingCount;
24
25 address public governance;
26
27 modifier onlyGovernance() {
28 require(msg.sender == governance, "Not governance");
29 _;
30 }
31
32 function createBudget(
33 uint256 amount,
34 uint256 duration,
35 address recipient
36 ) external onlyGovernance returns (uint256) {
37 uint256 budgetId = budgetCount++;
38
39 budgets[budgetId] = Budget({
40 amount: amount,
41 spent: 0,
42 startTime: block.timestamp,
43 endTime: block.timestamp + duration,
44 recipient: recipient,
45 active: true
46 });
47
48 return budgetId;
49 }
50
51 function spend(
52 uint256 budgetId,
53 uint256 amount,
54 address to
55 ) external {
56 Budget storage budget = budgets[budgetId];
57
58 require(budget.active, "Budget not active");
59 require(msg.sender == budget.recipient, "Not recipient");
60 require(block.timestamp <= budget.endTime, "Budget expired");
61 require(budget.spent + amount <= budget.amount, "Exceeds budget");
62
63 budget.spent += amount;
64 payable(to).transfer(amount);
65 }
66
67 function createVesting(
68 uint256 amount,
69 uint256 duration,
70 address beneficiary
71 ) external onlyGovernance returns (uint256) {
72 uint256 vestingId = vestingCount++;
73
74 vestingSchedules[vestingId] = VestingSchedule({
75 totalAmount: amount,
76 released: 0,
77 startTime: block.timestamp,
78 duration: duration,
79 beneficiary: beneficiary
80 });
81
82 return vestingId;
83 }
84
85 function releaseVested(uint256 vestingId) external {
86 VestingSchedule storage schedule = vestingSchedules[vestingId];
87
88 require(msg.sender == schedule.beneficiary, "Not beneficiary");
89
90 uint256 vested = calculateVested(vestingId);
91 uint256 releasable = vested - schedule.released;
92
93 require(releasable > 0, "Nothing to release");
94
95 schedule.released += releasable;
96 payable(schedule.beneficiary).transfer(releasable);
97 }
98
99 function calculateVested(uint256 vestingId) public view returns (uint256) {
100 VestingSchedule storage schedule = vestingSchedules[vestingId];
101
102 if (block.timestamp < schedule.startTime) {
103 return 0;
104 }
105
106 if (block.timestamp >= schedule.startTime + schedule.duration) {
107 return schedule.totalAmount;
108 }
109
110 uint256 elapsed = block.timestamp - schedule.startTime;
111 return (schedule.totalAmount * elapsed) / schedule.duration;
112 }
113
114 receive() external payable {}
115}
Summary
Hard Web3 topics:
- MEV: Sandwich attacks, Flashbots, mitigation
- Zero-Knowledge: zk-SNARKs, privacy, scalability
- Complex DeFi: Lending protocols, liquidations
- Cross-Chain: Bridge protocols, validators
- Governance: DAO, proposals, voting, timelock
- Diamond Pattern: Modular contracts, upgradeable
- Account Abstraction: Smart wallets, paymasters
- Concentrated Liquidity: Uniswap V3, capital efficiency
- Decentralized Identity: DIDs, verifiable credentials
- Treasury Management: Budgets, vesting, multi-sig
These advanced concepts enable building production-grade Web3 protocols.
Related Snippets
- Bitcoin (Nakamoto) Consensus Interview Questions
Bitcoin consensus algorithm interview questions covering Proof-of-Work (PoW) and … - Byzantine Fault Tolerance (BFT) Consensus Interview Questions
Byzantine Fault Tolerance (BFT) consensus algorithm interview questions covering … - Cardano Interview Questions - Easy
Easy-level Cardano interview questions covering blockchain basics, Plutus, and … - Cardano Interview Questions - Hard
Hard-level Cardano interview questions covering advanced optimization and formal … - Cardano Interview Questions - Medium
Medium-level Cardano interview questions covering advanced Plutus development … - Consensus Algorithms Comparison Interview Questions
Consensus algorithm comparison and general implementation interview questions. … - Cosmos Chain Operations Interview Questions - Easy
Easy-level Cosmos chain operation interview questions covering chain operations, … - Cosmos Chain Operations Interview Questions - Hard
Hard-level Cosmos chain operation questions covering advanced algorithms, … - Cosmos Chain Operations Interview Questions - Medium
Medium-level Cosmos chain operation questions covering advanced chain … - Cosmos SDK Interview Questions - Easy
Easy-level Cosmos SDK interview questions covering chain code, SDK basics, and … - Cosmos SDK Interview Questions - Hard
Hard-level Cosmos SDK interview questions covering advanced SDK internals, … - Cosmos SDK Interview Questions - Medium
Medium-level Cosmos SDK interview questions covering advanced module … - Ethereum Proof-of-Stake Consensus Interview Questions
Ethereum Proof-of-Stake consensus algorithm interview questions covering Casper … - Ouroboros (Cardano) Consensus Interview Questions
Ouroboros consensus algorithm interview questions covering Cardano's … - Paxos Consensus Interview Questions
Paxos consensus algorithm interview questions covering the classic distributed … - Polkadot (NPoS) Consensus Interview Questions
Polkadot consensus algorithm interview questions covering Nominated … - Polkadot Interview Questions - Easy
Easy-level Polkadot interview questions covering blockchain basics, Substrate, … - Polkadot Interview Questions - Hard
Hard-level Polkadot interview questions covering advanced optimization and … - Polkadot Interview Questions - Medium
Medium-level Polkadot interview questions covering advanced Substrate … - Solana Interview Questions - Easy
Easy-level Solana interview questions covering blockchain basics, programs, and … - Solana Interview Questions - Hard
Hard-level Solana interview questions covering advanced optimization, security, … - Solana Interview Questions - Medium
Medium-level Solana interview questions covering advanced program development, … - Solana Proof of History Consensus Interview Questions
Solana consensus algorithm interview questions covering Proof of History (PoH) … - Tendermint Consensus Interview Questions
Tendermint consensus algorithm interview questions covering the Byzantine Fault … - Web3 Interview Questions - Easy
Easy-level Web3 interview questions covering blockchain fundamentals, Ethereum, … - Web3 Interview Questions - Medium
Medium-level Web3 interview questions covering DeFi, advanced Solidity, …