Note: All 8 questions in this RACE are based on the below contract. This is the same contract you will see for all the 8 questions in this RACE. The question is below the shown contract.
// SPDX-License-Identifier: Unlicensepragmasolidity ^0.8.0;import"@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol";import"@openzeppelin/contracts/interfaces/IERC20.sol";contractFlashLoanisIERC3156FlashLender {bytes32publicconstant CALLBACK_SUCCESS =keccak256("ERC3156FlashBorrower.onFlashLoan");uint256public fee;/** * @param fee_ the fee that should be paid on a flashloan */constructor (uint256 fee_ ) { fee = fee_; }/** * @dev The amount of currency available to be lent. * @param token The loan currency. * @return The amount of `token` that can be borrowed. */functionmaxFlashLoan(address token ) publicviewoverridereturns (uint256) {returnIERC20(token).balanceOf(address(this)); }/** * @dev The fee to be charged for a given loan. * @param token The loan currency. Must match the address of this contract. * @param amount The amount of tokens lent. * @return The amount of `token` to be charged for the loan, on top of the returned principal. */functionflashFee(address token,uint256 amount ) externalviewoverridereturns (uint256) {return fee; }/** * @dev Loan `amount` tokens to `receiver`, and takes it back plus a `flashFee` after the ERC3156 callback. * @param receiver The contract receiving the tokens, needs to implement the `onFlashLoan(address user, uint256 amount, uint256 fee, bytes calldata)` interface.
* @param token The loan currency. Must match the address of this contract. * @param amount The amount of tokens lent. * @param data A data parameter to be passed on to the `receiver` for any custom use. */functionflashLoan(IERC3156FlashBorrower receiver,address token,uint256 amount,bytescalldata data ) externaloverridereturns (bool){uint256 oldAllowance =IERC20(token).allowance(address(this),address(receiver));uint256 oldBal =IERC20(token).balanceOf(address(this));require(amount <= oldBal,"Too many funds requested");IERC20(token).approve(address(receiver), oldAllowance + amount);require( receiver.onFlashLoan(msg.sender, token, amount, fee, data) == CALLBACK_SUCCESS,"Callback failed" );uint256 newBal =IERC20(token).balanceOf(address(this));if(newBal < oldBal + fee) {uint retAmt = oldBal + fee - newBal;require(IERC20(token).transferFrom(msg.sender,address(this), retAmt),"All funds not returned"); }if (IERC20(token).allowance(address(this),address(receiver)) > oldAllowance) {IERC20(token).approve(address(receiver), oldAllowance); }returntrue; }}
Question 1 ✅
Which of the following is an explanation of why flashLoan() could revert?
Question 2 ✅
If the FlashLoan contract were safe, which of the following invariants should hold at the end of any given transaction for some ERC20 token t? Note: old(expr) evaluates expr at the beginning of the transaction.
Comment:
For the flashloan to be safe, the contract's token balance must be maintained no matter which function is called. It must be (A) because flashloan will cause the token balance to either increase or stay the same (depending on fee) and all other functions should maintain token balances
Question 3 ✅
Which of the following tokens would be unsafe for the above contract to loan as doing so could result in theft?
Question 4 ✅
Which external call made by flashLoan() could result in theft if the token(s) identified in the previous question were to be used?
Question 5 ✅
What is the purpose of the fee in the FlashLoan contract as is?
Comment:
In the current FlashLoan contract, as it is, the sole purpose of the fee is to increase the available funds to loan.
Question 6 ✅
Which of the following describes the behavior of maxFlashLoan for a standard ERC20 token over time?
Question 7 - This one tricky
For some arbitrary ERC20 token t, which of the following accurately describes the FlashLoan contract’s balance of t after a successful (i.e. non-reverting) call to flashLoan() (where t is the token requested for the flashloan):
Comment:
flashLoan() can hypothetically finish successfully with any token that implements the ERC20 interface, even if it is a bogus implementation. Therefore, there are no guarantees on the output of IERC20(token).balanceOf(user).
Question 8 ✅
Which of the following are guaranteed to hold after a successful (i.e., non-reverting) execution of flashLoan(), assuming the token for which the flashloan is requested uses OpenZeppelin’s Standard ERC20 implementation?