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: 148/246
Findings: 2
Award: $17.67
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: adriro
Also found by: 0xMirce, 0xRajkumar, 0xepley, BPZ, Bahurum, Bauer, Co0nan, Emmanuel, Franfran, HollaDieWaldfee, IgorZuk, MiloTruck, NoamYakov, RedTiger, Ruhum, T1MOH, Tricko, ad3sh_, auditor0517, bin2chen, carrotsmuggler, eyexploit, handsomegiraffe, igingu, jasonxiale, koxuan, lukris02, monrel, nadin, peanuts, rbserver, rvierdiiev, shaka, sinarette, tnevler, y1cunhui
4.5426 USDC - $4.54
Incorrectly calculates the price of ETH in terms of WstETH The function returns the price of WstETH in terms of stETH. Getting underlying value in terms of ETH for each derivative. Since stETH does not have the same value as ETH the output price incorrect. Price of stETH at the time of writing this report The impact is severe because the underlying value is used to calculate the price of safeETH in regards to ETH . The incorrect price may be higher or lower than the amount that can be staked, which is the core calculation in the protocol.
The function IWstETH(WST_ETH).getStETHByWstETH() only converts WstETH to stETH. Thus, ethPerDerivative() returns the value of WstETH in terms of stETH.
84: @notice - Get price of derivative in terms of ETH 85: */ 86: function ethPerDerivative(uint256 _amount) public view returns (uint256) { 87: return IWStETH(WST_ETH).getStETHByWstETH(10 ** 18); 88: }
Manual review
Add extra steps to price() to approximate the rate for converting stETH to ETH. The same problem was confirmed at medium
#0 - c4-pre-sort
2023-04-04T17:16:12Z
0xSorryNotSorry marked the issue as duplicate of #588
#1 - c4-judge
2023-04-23T11:07:04Z
Picodes changed the severity to 3 (High Risk)
#2 - c4-judge
2023-04-24T20:46:22Z
Picodes marked the issue as satisfactory
🌟 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
WstEth.sol#L5 SfrxEth.sol#L6 SafEth.sol#L9 Reth.sol#L13 transferOwnership function is used to change Ownership from OwnableUpgradeable.sol.
There is another Openzeppelin Ownable contract (Ownable2StepUpgradeable.sol) has transferOwnership function, use is more secure due to 2-stage ownership transfer. Ownable2StepUpgradeable.sol
contracts/SafEth/derivatives/Reth.sol 24: address public constant UNISWAP_ROUTER = 25: 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45;
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L24-L25 ROUTER etc. In case the addresses change due to reasons such as updating their versions in the future, addresses coded as constants cannot be updated, so it is recommended to add the update option with the onlyOwner modifier.
initialize() function can be called by anybody when the contract is not initialized.
More importantly, if someone else runs this function, they will have full authority because of the _transferOwnership() function.
Here is a definition of initialize() function.
File: contracts/SafEth/derivatives/WstEth.sol function initialize(address _owner) external initializer { _transferOwnership(_owner); maxSlippage = (1 * 10 ** 16); // 1% } File: contracts/SafEth/derivatives/SfrxEth.sol function initialize(address _owner) external initializer { _transferOwnership(_owner); maxSlippage = (1 * 10 ** 16); // 1% } File: contracts/SafEth/SafEth.sol function initialize( string memory _tokenName, string memory _tokenSymbol ) external initializer { ERC20Upgradeable.__ERC20_init(_tokenName, _tokenSymbol); _transferOwnership(msg.sender); minAmount = 5 * 10 ** 17; // initializing with .5 ETH as minimum maxAmount = 200 * 10 ** 18; // initializing with 200 ETH as maximum } File: contracts/SafEth/derivatives/Reth.sol function initialize(address _owner) external initializer { _transferOwnership(_owner); maxSlippage = (1 * 10 ** 16); // 1% }
Add a control that makes initialize() only call the Deployer Contract or EOA;
if (msg.sender != DEPLOYER_ADDRESS) { revert NotDeployer(); }
Some event-emit description hasn’t parameter. Add to parameter for front-end website or client app , they can has that something has happened on the blockchain. Events with no old value;
File: contracts/SafEth/SafEth.sol 154: emit Rebalanced();
File: hardhat.config.ts 26: settings: { 27: optimizer: { 28: enabled: true, 29: runs: 100000,
Therefore, it is unclear how well they are being tested and exercised. High-severity security issues due to optimization bugs have occurred in the past. A high-severity bug in the emscripten-generated solc-js compiler used by Truffle and Remix persisted until late 2018. The fix for this bug was not reported in the Solidity CHANGELOG.
Another high-severity optimization bug resulting in incorrect bit shift results was patched in Solidity 0.5.6. More recently, another bug due to the incorrect caching of keccak256 was reported. A compiler audit of Solidity from November 2018 concluded that the optional optimizations may not be safe. It is likely that there are latent bugs related to optimization and that new bugs will be introduced due to future optimizations.
Exploit Scenario A latent or future bug in Solidity compiler optimizations—or in the Emscripten transpilation to solc-js—causes a security vulnerability in the contracts.
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. https://docs.soliditylang.org/en/v0.8.15/natspec-format.html
NatSpec comments should be increased in contracts
Solidity code is also cleaner in another way that might not be noticeable: the struct Point. We were importing it previously with global import but not using it. The Point struct polluted the source code with an unnecessary object we were not using because we did not need it. This was breaking the rule of modularity and modular programming: only import what you need Specific imports with curly braces allow us to apply this rule better.
import {contract1 , contract2} from "filename.sol";
Rather than using abi.encodePacked for appending bytes, since version 0.8.4, bytes.concat() is enabled Since version 0.8.4 for appending bytes, bytes.concat() can be used instead of abi.encodePacked(,).
File: contracts/SafEth/derivatives/Reth.sol 70: abi.encodePacked("contract.address", "rocketTokenRETH") 125: abi.encodePacked("contract.address", "rocketDepositPool") 136: abi.encodePacked( 162: abi.encodePacked("contract.address", "rocketDepositPool") 191: abi.encodePacked("contract.address", "rocketTokenRETH") 233: abi.encodePacked("contract.address", "rocketTokenRETH")
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L70 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L125 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L136 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L162 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L191 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L233
Functions should be ordered following the Soldiity conventions ( https://docs.soliditylang.org/en/v0.8.15/style-guide.html#order-of-functions): receive() function should be placed after the constructor and before every other function.
File: contracts/SafEth/derivatives/WstEth.sol 97: receive() external payable {}
File: contracts/SafEth/derivatives/SfrxEth.sol 126: receive() external payable {}
File: contracts/SafEth/SafEth.sol 246: receive() external payable {}
File: contracts/SafEth/derivatives/Reth.sol 244: receive() external payable {}
There are many addresses and constants used in the system. It is recommended to put the most used ones in one file (for example constants.sol, use inheritance to access these values):
This will help with readability and easier maintenance for future changes. constants.sol Use and import this file in contracts that require access to these values. This is just a suggestion, in some use cases this may result in higher gas usage in the distribution
Events help non-contract tools to track changes, and events prevent users from being surprised by changes
Add Event-Emit
File: contracts/SafEth/derivatives/WstEth.sol 48: function setMaxSlippage(uint256 _slippage) external onlyOwner { 56: function withdraw(uint256 _amount) external onlyOwner { 73: function deposit() external payable onlyOwner returns (uint256) {
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L48 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L56 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L73
File: contracts/SafEth/derivatives/SfrxEth.sol 51: function setMaxSlippage(uint256 _slippage) external onlyOwner { 60: function withdraw(uint256 _amount) external onlyOwner { 94: function deposit() external payable onlyOwner returns (uint256) {
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L51 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L60 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L94
File: contracts/SafEth/derivatives/Reth.sol 58: function setMaxSlippage(uint256 _slippage) external onlyOwner { 107: function withdraw(uint256 amount) external onlyOwner { 156: function deposit() external payable onlyOwner returns (uint256) {
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L58 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L107 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L156
I recommend using header for Solidity code layout and readability https://github.com/transmissions11/headers
/*////////////////////////////////////////////////////////////// TESTING 123 //////////////////////////////////////////////////////////////*/
#0 - c4-sponsor
2023-04-07T22:20:42Z
toshiSat marked the issue as sponsor acknowledged
#1 - c4-judge
2023-04-24T17:26:19Z
Picodes marked the issue as grade-b