Platform: Code4rena
Start Date: 07/04/2023
Pot Size: $47,000 USDC
Total HM: 20
Participants: 120
Period: 6 days
Judge: GalloDaSballo
Total Solo HM: 4
Id: 230
League: ETH
Rank: 31/120
Findings: 1
Award: $184.53
🌟 Selected for report: 0
🚀 Solo Findings: 0
184.5341 USDC - $184.53
Having no Flash fee will break support for ERC20 tokens that don't support zero transfers
PrivatePool.sol#flashLoan() attempts to transfer funds even if there isn't a fee. If changeFee
is not set yet or set to zero, then the flash loan fee will be zero. The function then attempts to transfer 0 value to the contract.
function flashFee(address, uint256) public view returns (uint256) { return changeFee; }
uint256 fee = flashFee(token, tokenId); // if base token is ETH then check that caller sent enough for the fee if (baseToken == address(0) && msg.value < fee) revert InvalidEthAmount(); // transfer the NFT to the borrower ERC721(token).safeTransferFrom(address(this), address(receiver), tokenId); // call the borrower bool success = receiver.onFlashLoan(msg.sender, token, tokenId, fee, data) == keccak256("ERC3156FlashBorrower.onFlashLoan"); // check that flashloan was successful if (!success) revert FlashLoanFailed(); // transfer the NFT from the borrower ERC721(token).safeTransferFrom(address(receiver), address(this), tokenId); //@audit-- If fee is zero, function still attempts to transfer // transfer the fee from the borrower if (baseToken != address(0)) ERC20(baseToken).transferFrom(msg.sender, address(this), fee); return success; }
If the underlying ERC20 doesn't support transfers with zero value then the flashloan will always revert when the deposit fee is zero. (see https://github.com/d-xo/weird-erc20).
Manual Review
Only transfer if the amount to transfer is not zero:
uint256 fee = flashFee(token, tokenId); // if base token is ETH then check that caller sent enough for the fee if (baseToken == address(0) && msg.value < fee) revert InvalidEthAmount(); // transfer the NFT to the borrower ERC721(token).safeTransferFrom(address(this), address(receiver), tokenId); // call the borrower bool success = receiver.onFlashLoan(msg.sender, token, tokenId, fee, data) == keccak256("ERC3156FlashBorrower.onFlashLoan"); // check that flashloan was successful if (!success) revert FlashLoanFailed(); // transfer the NFT from the borrower ERC721(token).safeTransferFrom(address(receiver), address(this), tokenId); //@audit-- Transfer the fee only if fee is more than zero if(fee > 0){ // transfer the fee from the borrower if (baseToken != address(0)) {ERC20(baseToken).transferFrom(msg.sender, address(this), fee)}; } return success; }
#0 - c4-pre-sort
2023-04-20T17:53:45Z
0xSorryNotSorry marked the issue as duplicate of #278
#1 - c4-judge
2023-05-01T07:16:53Z
GalloDaSballo marked the issue as satisfactory