Platform: Code4rena
Start Date: 24/03/2023
Pot Size: $49,200 USDC
Total HM: 20
Participants: 246
Period: 6 days
Judge: Picodes
Total Solo HM: 1
Id: 226
League: ETH
Rank: 191/246
Findings: 1
Award: $13.13
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: brgltd
Also found by: 0x3b, 0xAgro, 0xGusMcCrae, 0xNorman, 0xRajkumar, 0xSmartContract, 0xTraub, 0xWagmi, 0xWaitress, 0xffchain, 0xhacksmithh, 0xkazim, 0xnev, 3dgeville, ArbitraryExecution, Aymen0909, BRONZEDISC, Bason, Bloqarl, BlueAlder, Brenzee, CodeFoxInc, CodingNameKiki, Cryptor, DadeKuma, DevABDee, Diana, Dug, Englave, Gde, Haipls, HollaDieWaldfee, Ignite, Infect3d, Jerry0x, Josiah, Kaysoft, Koko1912, KrisApostolov, Lavishq, LeoGold, Madalad, PNS, Rappie, RaymondFam, RedTiger, Rickard, Rolezn, Sathish9098, SunSec, T1MOH, UdarTeam, Udsen, Viktor_Cortess, Wander, adriro, ak1, alejandrocovrr, alexzoid, arialblack14, ayden, bin2chen, brevis, btk, c3phas, carlitox477, catellatech, ch0bu, chaduke, ck, climber2002, codeslide, descharre, dingo2077, ernestognw, fatherOfBlocks, favelanky, georgits, helios, hl_, inmarelibero, juancito, ks__xxxxx, lopotras, lukris02, m_Rassska, mahdirostami, maxper, nadin, navinavu, nemveer, p_crypt0, peanuts, pipoca, pixpi, qpzm, rbserver, reassor, roelio, rotcivegaf, scokaf, siddhpurakaran, slvDev, smaul, tnevler, tsvetanovv, turvy_fuzz, vagrant, wen, yac, zzzitron
13.1298 USDC - $13.13
Letter | Name | Description |
---|---|---|
L | Low risk | Potential risk |
NC | Non-critical | Non risky findings |
R | Refactor | Changing the code |
O | Ordinary | Often found issues |
Total Found Issues | 10 |
---|
Count | Explanation | Instances |
---|---|---|
[N-01] | Floating Pragma | 5 |
[N-02] | Same user can stake more than 200 ETH | 1 |
[N-03] | derivatives[i].withdraw(derivativeAmount); sending Ether to safETH contract if anyone call the unstake function then it might burn the tokens in derivative contracts | 1 |
Count | Explanation | Instances |
---|---|---|
[R-01] | Use uint instead of boolean in pauseUnstaking and pauseStaking | 1 |
[R-02] | If user doen't have sufficient safETH revert if unstake() function is called | 1 |
Count | Explanation | Instances |
---|---|---|
[L-01] | No nonreentrant modifier used in the functions stake and unstake | 1 |
It's better to use uint instead of bool , a really good example is openzeppelin re-entrancy guard , Also boolean is way more expensive than using uint ;
- bool public pauseStaking; // true if staking is paused - bool public pauseUnstaking; // true if unstaking is pause
eg:
uint256 private constant notpaused = 1 ; uint256 private constant paused = 2; uint256 private stakelock ; uint256 private unstakelock ;
// inside inizialer we innitialize it to paused stakelock = notpaused ; unstakelock = notpaused;
eg:
function setPauseUnstaking() external onlyOwner { if (unstakelock == paused){ unstakelock = notpaused; } else { unstakelock = paused; }
unstake funtion doesn't have any checks any user can call unstake function not only users who stake safETH. mapping (address => uint256) SafEthHoldersBalances;
we can create a mapping to check whether user have sufficient balance to unstake()
if(SafEthHoldersBalances[msg.sender] < _safEthAmount ){ revert ("User isn't a safETH hodler"); }
REVERT Error(reason: "User isn't a safETH hodler") REVERT Error(reason: "User isn't a safETH hodler")
(In protocol It hasn't mentioned that users not allowed to transfer their safETH Tokens so a user who already staked can transfer his safETH tokens and reciever can call unstake without staking )
use openzeppelin re-entrancy Guard and nonreentrant modifier
Description: Contracts should be deployed with the same compiler version and flags that they have been tested with thoroughly. Locking the pragma helps to ensure that contracts do not accidentally get deployed using, for example, an outdated compiler version that might introduce bugs that affect the contract system negatively.
Recommendation Consider locking the pragma version.
Please consider using at least Solidity 0.8.15 instead of 0.8.13
Same user can stake more that 200 ETH
it("USER1 STAKE 200 ether " , async function () { console.log("BALANCE BEFORE : %s" , await safEthProxy.balanceETHERS() ) const a = ethers.utils.parseEther("200") const s = await safEthProxy.connect(user).stake( {value: a } ) await s.wait(); } ) it("USER1 again STAKE 200 ether " , async function () { console.log("BALANCE BEFORE : %s" , await safEthProxy.balanceETHERS() ) const a = ethers.utils.parseEther("200") const s = await safEthProxy.connect(user).stake( {value: a } ) await s.wait(); } )
MINT : 200032451100303076621 ✓ USER1 STAKE 200 ether MINT : 200032681248109301110 ✓ USER1 again STAKE 200 ether
suggestion*
for (uint i = 0; i < derivativeCount; i++)
So when we call stake function , it will call deposit functions in each derivatives contracts , each derivatives contract have an onlyOwner
which is the safETH
contract address , so when calll stake it will deposit rETH to RocktPool ... then I think it calls uniswap router and exhange ETH to WETH to rETH msg.sender is Reth pool Token contract ..... similar way to other tokens
rETH(0xae78736cd615f374d3085123a210448e74fc6393).transfer(to: [RocketPool], amount: 0.311470004114065856)
so I tried calling the unstake() function with passing 1 ether as param without staking any token ( 0 ) because it has no checks , so any one can call unstake with any amount ,
function executes till
derivatives[i].withdraw(derivativeAmount);
function withdraw(uint256 amount) external onlyOwner { RocketTokenRETHInterface(rethAddress()).burn(amount); (bool sent, ) = address(msg.sender).call{value: address(this).balance}(""); require(sent, "Failed to send Ether");
which call withdraw in respective derivative contracts
function unstake execute until `_burn(msg.sender, _safEthAmount); (here msg.sender is Reth contract)
as there's no amount it reverts ,
I am not sure whether Reth tokens could be burned or not , I think it's reverting , I used hardhat console.log() inside receive function of
saFETH contract which gives this it tranfers ETHER into safETH contract then reverts
ETHER RECeived : 331157191561750609 ETHER RECeived : 332871276653896873 ETHER RECeived : 332897725842115751
`` , This is reverting but there could be a vulnerability
CALL rETH(0xae78736cd615f374d3085123a210448e74fc6393).burn(_rethAmount: 311439063843440753)
#0 - c4-sponsor
2023-04-10T18:21:45Z
toshiSat marked the issue as sponsor acknowledged
#1 - c4-judge
2023-04-24T17:42:45Z
Picodes marked the issue as grade-b