Platform: Code4rena
Start Date: 03/05/2022
Pot Size: $30,000 USDC
Total HM: 6
Participants: 93
Period: 3 days
Judge: gzeon
Id: 118
League: ETH
Rank: 14/93
Findings: 2
Award: $891.81
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: gzeon
Also found by: AuditsAreUS, BowTiedWardens, Ruhum, pedroais, shenwilly, teddav
861.5328 USDC - $861.53
https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsMinter.sol#L616 https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsMinter.sol#L627 https://github.com/code-423n4/2022-05-runes/blob/main/contracts/ForgottenRunesWarriorsMinter.sol#L527
The team (owner
of the contract) can rugpull at any time by withdrawing all the funds in the contract.
There is no limit to how much can be withdrawn. And the users won't be able to get their refunds.
The function setVaultAddress
allows the owner of the contract to set the vault to any address.
The function withdrawAll
allows the owner/the team to withdraw all ETH on the contract.
The function forwardERC20s
allows the owner/the team to withdraw any ERC20 on the contract.
Manual analysis
Only allow the withdraw functions / forwardERC20s to be called after the refund process is done
#0 - cryppadotta
2022-05-06T15:22:54Z
acknowledged, but this is in the readme
#1 - gzeoneth
2022-06-18T17:20:39Z
🌟 Selected for report: defsec
Also found by: 0v3rf10w, 0x1f8b, 0x4non, 0x52, 0xDjango, 0xf15ers, 0xkatana, 0xliumin, AuditsAreUS, BowTiedWardens, CertoraInc, Cr4ckM3, Funen, GimelSec, Hawkeye, IllIllI, Kulk0, M0ndoHEHE, MaratCerby, Picodes, Ruhum, TerrierLover, TrungOre, VAD37, WatchPug, berndartmueller, broccolirob, catchup, cccz, cryptphi, csanuragjain, delfin454000, dirk_y, eccentricexit, ellahi, fatherOfBlocks, gzeon, hake, hansfriese, hickuphh3, horsefacts, hubble, hyh, ilan, joestakey, kebabsec, kenta, kenzo, leastwood, m9800, marximimus, minhquanym, oyc_109, p4st13r4, pauliax, pedroais, peritoflores, plotchy, rajatbeladiya, reassor, rfa, robee, rotcivegaf, samruna, shenwilly, shung, simon135, sorrynotsorry, sseefried, teddav, throttle, tintin, unforgiven, z3s
30.2807 USDC - $30.28
Refunds could end up being stuck for all users.
First issue:
The function _safeTransferETHWithFallback
has a first fallback if to send WETH if the ETH transfer fails. But the WETH transfer can fail (silently) too. You need to check the return value of WETH.transfer
and revert if it is false.
You can see the code of WETH here: https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code
Second issue: If the user you are trying to refund uses a smart contract, the WETH might end up being lost. If the smart contract doesnt accept ETH, then the call will fallback to WETH. But if the contract doesnt have a method to transfer out the WETH, then the WETH will be lost. Which is terrible for the user.
Manual analysis
Rewrite the _safeTransferETHWithFallback
function.
function _safeTransferETHWithFallback(address to, uint256 amount) internal { if (!_safeTransferETH(to, amount)) { IWETH(weth).deposit{value: amount}(); require(IERC20(weth).transfer(to, amount), "failed WETH transfer"); } }
For the second issue. I would recommend maybe having 2 different refund functions. One as ETH, and the other one as WETH
#0 - KenzoAgada
2022-06-06T05:57:08Z
First issue is duplicate of #250 and invalid as WETH can not fail silently. Second issue - I might comment later. I think there's a dup of it somewhere.
#1 - gzeoneth
2022-06-18T20:01:11Z
As warden's QA report.