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: 100/246
Findings: 1
Award: $42.06
đ 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
42.0604 USDC - $42.06
Number | Issues Details |
---|---|
[L-01] | No storage gap for upgradable contracts might lead to storage slot collision |
[L-02] | Missing checks for address(0x0) |
[L-03] | Missing event for critical parameter change |
[L-04] | Critical address changes should use two-step procedure |
[L-05] | Unbounded loop while iterating with derivativeCount |
[L-06] | No limit for maxSlippage() functions |
Number | Issues Details |
---|---|
[NC-01] | Use latest Solidity version |
[NC-02] | Use stable pragma statement |
[NC-03] | Use named imports instead of plain import FILE.SOL |
[NC-04] | NatSpec is incomplete |
[NC-05] | Update external dependency to latest version |
[NC-06] | Solidity compiler optimizations can be problematic |
[NC-07] | Initial value check is missing in Set Functions |
[NC-08] | You can use named parameters in mapping types |
For upgradeable contracts, there must be storage gap to âallow developers to freely add new state variables in the future without compromising the storage compatibility with existing deploymentsâ. Otherwise, it may be very difficult to write new implementation code. The storage gap is essential for upgradeable contract because âIt allows us to freely add new state variables in the future without compromising the storage compatibility with existing deploymentsâ.
Without storage gap, the variable in the contract contract might be overwritten by the upgraded contract if new variables are added. This could have unintended and very serious consequences to the child contracts.
Consider defining an appropriate storage gap in each upgradeable parent contract at the end of all the storage variable definitions as follows:
uint256[50] __gap; // gap to reserve storage in the contract for future variable additions
Lack of zero-address validation on address parameters may lead to transaction reverts, waste gas, require resubmission of transactions and may even force contract redeployments in certain cases within the protocol.
contracts/SafEth/derivatives/WstEth.sol 33: function initialize(address _owner) external initializer { contracts/SafEth/derivatives/SfrxEth.sol 36: function initialize(address _owner) external initializer { 37: _transferOwnership(_owner); 182: function addDerivative( 183: address _contractAddress, contracts/SafEth/derivatives/Reth.sol 42: function initialize(address _owner) external initializer { 43: _transferOwnership(_owner);
Consider adding explicit zero-address validation on input parameters of address type.
contracts/SafEth/derivatives/WstEth.sol 48: function setMaxSlippage(uint256 _slippage) external onlyOwner { 49: Â Â maxSlippage = _slippage; 50: } contracts/SafEth/derivatives/SfrxEth.sol 51: function setMaxSlippage(uint256 _slippage) external onlyOwner { 52: Â Â maxSlippage = _slippage; 53: } contracts/SafEth/derivatives/Reth.sol 58: function setMaxSlippage(uint256 _slippage) external onlyOwner { 59: Â Â maxSlippage = _slippage; 60: }
The critical procedures should be a two-step process.
contracts/SafEth/derivatives/WstEth.sol 33: function initialize(address _owner) external initializer { 34: _transferOwnership(_owner); contracts/SafEth/derivatives/SfrxEth.sol 36: function initialize(address _owner) external initializer { 37: _transferOwnership(_owner); contracts/SafEth/derivatives/Reth.sol 42: function initialize(address _owner) external initializer { 43: _transferOwnership(_owner);
Recommended Mitigation Steps Lack of two-step procedure for critical operations leaves them error-prone. Consider adding a two- step procedure on the critical functions.
derivativeCount
In SafEth.sol we have few function which iterating the state variable derivativeCount
and it is possible unbounded loop.
New derivativeCount
 are added in addDerivative()
function, without a maximum size limit.
You can add a maximum number of derivativeCount
 that can be added, to prevent unbounded loop.
maxSlippage()
functionsIn WstEth.sol
, SfrxEth.sol
, Reth.sol
and SafEth.sol
functions.
These functions allows the owner of the smart contract to set the maximum slippage for the contract without any checks for a maximum value. This means that it is possible for the owner to set an unreasonably high value for maxSlippage
, which could potentially result in users losing a significant amount of funds when they execute transactions on the smart contract.
Recommended Mitigation Steps
It would be a good security practice to add a check for a maximum value to ensure that the owner cannot set an unreasonably high value for maxSlippage
. This would help to protect users and prevent potential losses due to large slippage values.
Solidity pragma versioning should be upgraded to latest available version.
Using a floating pragma statement ^0.8.13
is discouraged as code can compile to different bytecodes with different compiler versions. Use a stable pragma statement to get a deterministic bytecode.
IMPORT FILE.SOL
Recommendation: `import {contract1 , contract2} from "filename.sol";
It is recommended that Solidity contracts are fully annotated using NatSpec for all public interfaces (everything in the ABI). It is clearly stated in the Solidity official documentation. In complex projects such as Defi, the interpretation of all functions and their arguments and returns is important for code readability and auditability. You also need to add return parameters in NatSpec comments.
The latest version is 4.8.2
package.json:
82: "@openzeppelin/contracts": "^4.8.0", 83: "@openzeppelin/contracts-upgradeable": "^4.8.1",
hardhat.config.ts 27: Â Â optimizer: { 28:Â Â Â Â enabled: true, 29:Â Â Â Â runs: 100000, 30:Â Â Â },
Protocol has enabled optional compiler optimizations in Solidity. There have been several optimization bugs with security implications. Moreover, optimizations are actively being developed. Solidity compiler optimizations are disabled by default, and it is unclear how many contracts in the wild actually use them.
contracts/SafEth/derivatives/WstEth.sol 48: function setMaxSlippage(uint256 _slippage) external onlyOwner { contracts/SafEth/SafEth.sol 202: function setMaxSlippage( 203: uint _derivativeIndex, 204: uint _slippage 214: function setMinAmount(uint256 _minAmount) external onlyOwner { 215: minAmount = _minAmount;
Checking whether the current value and the new value are the same should be added.
From Solidity 0.8.18 you can use named parameters in mapping types. This will make the code much more readable.
#0 - c4-sponsor
2023-04-07T22:20:09Z
toshiSat marked the issue as sponsor acknowledged
#1 - c4-judge
2023-04-24T17:24:41Z
Picodes marked the issue as grade-a