A financial institution is developing a smart contract to issue bonds on Ethereum. A key requirement is that the total supply of bonds must be strictly capped and can never be changed after deployment. Which of the following Solidity code patterns most effectively and securely enforces this immutable cap?
Answer and explanation
Correct answer: D
Using the immutable keyword is the optimal solution. An immutable variable can be assigned a value only once, either at the point of declaration or within the constructor. This ensures the value is set at deployment time and cannot be altered thereafter. It is also more gas-efficient than a standard state variable because its value is directly embedded into the contract's bytecode. A constant variable must be known at compile time, which might not be suitable if the supply is determined by a constructor parameter. A standard public or private state variable could be accidentally modified by another function if not properly protected.
Question 2
A developer is building a decentralized autonomous organization (DAO) where voting power is determined by the amount of a specific ERC20 token a user held at the time a proposal was created. To prevent users from buying tokens after a proposal is made to influence the vote, which technique should be implemented?
Answer and explanation
Correct answer: B
The correct approach is to implement a snapshot mechanism. When a proposal is created, the current block number is recorded. The ERC20 token contract should be designed to allow querying a user's balance at a past block number. This prevents users from acquiring tokens after a proposal is live to gain voting power for that specific proposal. OpenZeppelin's ERC20Votes extension is a standard implementation of this pattern. Simply checking the current balance at vote time is vulnerable to this exact exploit.
Question 3
Multiple answers
A developer is writing a smart contract that interacts with multiple other contracts. One of the external contracts is untrusted and known to be potentially malicious. To prevent reentrancy attacks, which of the following design patterns and security measures are most critical to implement? (Select TWO)
Answer and explanation
Correct answers: A, B
The Checks-Effects-Interactions pattern is a fundamental principle for preventing reentrancy. It dictates that you should perform all internal state checks and updates (Checks, Effects) before making any external calls (Interactions). Using transfer() (or send()) for sending Ether is another key mitigation because it forwards a fixed, small amount of gas (2300), which is not enough for the receiving contract to make a reentrant call back. Using call() with a large gas stipend is what enables reentrancy. Increasing gas price and using low-level calls do not prevent reentrancy.
Question 4
True or False: Using msg.sender within a function called via delegatecall will refer to the address of the contract that initiated the delegatecall, not the original transaction signer.
Answer and explanation
Correct answer: B
False. The delegatecall opcode is unique because it executes the code of another contract but preserves the context of the calling contract. This means that msg.sender and msg.value remain those of the original caller of the function that initiated the delegatecall. This behavior is fundamental to implementing proxy patterns for upgradeable smart contracts.
Question 5
A development team is deploying a complex smart contract system using Hardhat. They need to deploy several contracts, link libraries, and initialize state in a specific, repeatable sequence on both the local testnet and the Sepolia testnet. What is the most appropriate Hardhat feature to automate this process?
Answer and explanation
Correct answer: B
Hardhat's scripting capabilities, located in the scripts/ directory, are designed for this exact purpose. Developers can write JavaScript or TypeScript files that use the Ethers.js or Web3.js plugins to define a precise, repeatable sequence of deployments and contract interactions. These scripts can be run against any configured network (like localhost or sepolia) using the hardhat run --network command, ensuring consistency and automation. The other options are either manual, not designed for deployment, or part of a different framework.
Question 6
A developer needs to create a function that accepts an arbitrary amount of Ether and logs the sender and the amount. The function should not perform any other state changes. Which function declaration is the most appropriate and gas-efficient for this purpose?
Answer and explanation
Correct answer: C
The receive() external payable function is specifically designed to handle plain Ether transfers to a contract where no data (msg.data) is sent. It is the most gas-efficient way to receive Ether. A fallback() function can also receive Ether, but it's a more general-purpose function that also executes when a call is made to the contract with no matching function signature. If the goal is only to receive Ether, receive() is the correct and more specialized choice. A regular payable function like deposit() would require the sender to explicitly call that function with its signature, which is not the standard way to simply send Ether to a contract.
Question 7
A decentralized application (dApp) needs to display a user's token balance and the total supply of an ERC20 token without requiring the user to send a transaction and pay gas. How does a dApp's frontend, using a library like Ethers.js, accomplish this?
Answer and explanation
Correct answer: B
Functions in Solidity marked as view (reads state) or pure (does not read or modify state) do not alter the blockchain's state. Therefore, they can be called without sending a transaction and incurring gas costs. A dApp's frontend uses a Provider (like Infura or Alchemy) to connect to an Ethereum node and make these read-only calls. A Signer is only required for transactions that modify state and need to be signed by a user's private key. The EVM executes the function call locally on the connected node and returns the result without broadcasting a transaction to the network.
Question 8
A developer observes that a transaction to a smart contract is consistently failing with an 'out of gas' error, even after significantly increasing the gas limit. The function being called performs a loop that iterates over an array of addresses stored in contract storage. What is the most likely cause of this issue?
Answer and explanation
Correct answer: B
The most probable cause is that the function's gas cost has surpassed the block gas limit. Each Ethereum block has a maximum amount of gas that can be consumed by all transactions within it. If a single transaction requires more gas than this limit, it can never be included in a block, regardless of the gas limit set by the user. Loops that iterate over unbounded arrays in storage are a common anti-pattern that leads to this problem, as the gas cost grows linearly with the size of the array.
Question 9
A team is building a system that requires a source of on-chain randomness to determine winners in a lottery. Which of the following approaches provides the most secure and manipulation-resistant source of randomness for a smart contract on Ethereum?
Answer and explanation
Correct answer: B
Using on-chain data like block.timestamp or blockhash is highly insecure, as miners can manipulate these values to their advantage. A Verifiable Random Function (VRF), typically provided by oracle services like Chainlink, is the industry standard for secure on-chain randomness. A VRF generates a random number and a cryptographic proof that the number was generated verifiably randomly. The smart contract can then verify this proof on-chain, ensuring that neither the oracle nor the miners could have tampered with the outcome. The commit-reveal scheme is better than on-chain data but can still be complex and less secure than a dedicated VRF service.
Question 10
Case Study:
A decentralized finance (DeFi) startup, 'YieldFarmz', is launching a new staking protocol. Users will deposit an ERC20 token (YFZ) into a staking contract and earn rewards in the same token over time. The protocol needs to be secure, gas-efficient, and fair to all participants, regardless of when they stake or unstake their tokens.
The lead architect has proposed an architecture where the contract maintains a list of all stakers and their deposit amounts. When rewards are to be distributed, a function will loop through this entire list, calculating and transferring rewards to each staker individually. This distribution will be triggered by an admin on a weekly basis.
A junior developer on the team raises concerns about this design, particularly regarding its scalability and potential for denial-of-service as the number of stakers grows. They also worry about the fairness of reward distribution if a user unstakes right before the weekly distribution event.
Given the requirements and the concerns raised, which of the following alternative designs provides the most robust and scalable solution for calculating and distributing staking rewards?
Answer and explanation
Correct answer: C
This describes a widely-used and highly efficient pattern for reward distribution, often seen in top DeFi protocols. Instead of looping, the contract tracks a cumulative rewardPerToken value. Rewards are effectively 'accounted for' every time total deposits change. A user's earned rewards can then be calculated with a simple formula (userDeposit * (currentRewardPerToken - userLastRewardPerToken)) when they choose to interact. This avoids unbounded loops, scales to an infinite number of users, and ensures rewards are calculated fairly up to the exact moment of any action. Batching the loop is a temporary fix that doesn't solve the underlying scalability issue. A simple pull system still requires a complex, potentially gas-intensive calculation for each user.