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: 50/246
Findings: 4
Award: $115.63
π Selected for report: 0
π Solo Findings: 0
π Selected for report: monrel
Also found by: 0xRajkumar, 0xfusion, AkshaySrivastav, Bahurum, Brenzee, Cryptor, Dug, Haipls, Koolex, Krace, MiloTruck, RaymondFam, RedTiger, ToonVH, Tricko, Vagner, aga7hokakological, anodaram, bart1e, bin2chen, bytes032, carrotsmuggler, ck, d3e4, giovannidisiena, igingu, juancito, mahdirostami, mert_eren, n33k, nemveer, parsely, pavankv, sashik_eth, shaka, sinarette, ulqiorra, yac
3.4908 USDC - $3.49
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEthStorage.sol#L23 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L141 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L73
rebalanceToWeights
and increase underlyingValue
for usersderivative
, which will cause a withdraw
call on the derivative from revert (due to a small amount of funds) during call rebalanceToWeights
from the ownersafEthTotalSupply
will 1 safEth (we ignore fee and other roundings
)contracts/SafEth/SafEth.sol uint256 mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice;
derivatives[WST_ETH ].balance()
will return full balance of contract sent via stake + sent directly
contracts/SafEth/WstEth.sol function balance() public view returns (uint256) { return IERC20(WST_ETH).balanceOf(address(this)); }
as we can see, get the full balance of the contract, and not the really staked amount. Which increases the price for the user
contracts/SafEth/SafEth.sol 72: underlyingValue += 73: (derivatives[i].ethPerDerivative(derivatives[i].balance()) * 74: derivatives[i].balance()) / 75: 10 ** 18;
underlyingValue = 1e18 * 1001e18/ 1e18 = 1001e18
preDepositPrice = 1e18 * 1001e18 / 1e18 = 1001e18
mintAmount = 1e18 * 1e18/ 1001e18 = 1e14
-> Also the first user can stake 100 wei and transfer 1e16 WST_ETH funds to derivate than price will 1e34 and block any stake (No tested)
underlyingValue = 1e18 * 1e18 / 1e18 = 1e18
preDepositPrice = 1e18 * 1e18 / 100= 1e34
weight
0 for derivative ArebalanceToWeights
contracts/SafEth/SafEth.sol 141: if (derivatives[i].balance() > 0)
derivatives[i].withdraw
which will revert because can't swap or unwrap 1 weiSafEth.sol // total supply = +-0.1e18, derivatives[Wst].balance() = +-1000.1 Wst, 73: underlyingValue -> +-1000.1 e18 81: preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply = 1e18 * 1000.1e18 / 0.1e18 = +-1.0001e22 98: mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice = 100e18 * 1e18 / 1.001e22 = +-10e15
unstake
for 0.1 safEth and receive 0.1/0.101 all funds which equal = 0.1/0.101 * (100 + 1000 + 0.1) = 1089.20792 Eth = 89 ETH +- profit for user A. (Yes, of course, this is provided that User A will stake first and the next transaction will be large, But it also works on a different scale, which requires larger balances to do it)Take into account only the real staked amount in the price of assets and also remove derivatives with weight 0
#0 - c4-pre-sort
2023-04-04T13:44:12Z
0xSorryNotSorry marked the issue as duplicate of #454
#1 - c4-judge
2023-04-21T16:21:16Z
Picodes marked the issue as duplicate of #1098
#2 - c4-judge
2023-04-24T20:56:53Z
Picodes marked the issue as satisfactory
π Selected for report: __141345__
Also found by: 0xWaitress, 0xbepresent, AkshaySrivastav, Bauer, Haipls, HollaDieWaldfee, Lirios, MiloTruck, SaeedAlipoor01988, UdarTeam, adriro, bytes032, ck, d3e4, hihen, hl_, kaden, ladboy233, lopotras, m_Rassska, peanuts, reassor, volodya
8.2654 USDC - $8.27
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L118 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L142
Issues with calling withdraw
for one of the added Derivative
results in an inability to withdraw all users' funds across all Derivatives
until changes/corrections/updates/removal of the problematic derivative are made. Setting the weight
for the problematic derivative
also doesn't work, because rebalancing is impossible when the withdrawal method reverts for at least one of the derivatives.
This can happen quite often due to reasons such as price changes in the pool for one of the derivatives, incorrect settings, internal changes in those derivatives, or various situations that may occur in projects used in these derivatives.
When the user accesses the unstake output, the withdraw method is called for each derivation. We see that there is no error handling, and successful execution of the withdraw
method is expected. Accordingly, all derivations must work successfully for the user to withdraw at least some funds
Now let's assume the situation that in one of the projects for which the derivative was created, an update was carried out / an error was detected / all funds were stolen or the price collapsed
That causes it to be not possible to withdraw funds from the corresponding derivative
, which entails the revert withdraw
function.
In most cases, rebalancing will not help either, because the withdraw function is also called there. That will cause the need to upgrade the derivative contract.
Situations that can cause funds to be blocked:
Manual review
Develop the possibility of withdrawing funds in the event of poor performance of one of the derivatives
#0 - c4-pre-sort
2023-04-04T17:29:43Z
0xSorryNotSorry marked the issue as duplicate of #703
#1 - c4-judge
2023-04-21T15:05:47Z
Picodes marked the issue as satisfactory
#2 - c4-judge
2023-04-24T19:33:31Z
Picodes marked the issue as not a duplicate
#3 - c4-judge
2023-04-24T19:33:43Z
Picodes marked the issue as duplicate of #770
#4 - c4-judge
2023-04-24T21:37:46Z
Picodes changed the severity to 2 (Med Risk)
π 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
The situation is aggravated by the fact that it is not possible to remove the derivative, only to exclude it from the calculations https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L182
Instances:
contracts\SafEth\SafEth.sol 53: _transferOwnership(msg.sender);
Recommendation:
__Ownable_init()
The user can only check in manual mode whether it is profitable for him to unstake
or not and how much he will receive eth in end
Recommendation: Add various auxiliary methods that will allow the user to calculate his final rewards, etc.
There is no validation for setMinAmount
and setMaxAmount
, addDerivative
methods on valid input parameters.
The owner can mistakenly set 'minAmount > maxAmount', maxAmount == 0
or set zero address to derivative, which will implicitly block the stake
or unstake
method
Instances
contracts\SafEth\SafEth.sol 214: function setMinAmount(uint256 _minAmount) external onlyOwner { 215: minAmount = _minAmount; 216: emit ChangeMinAmount(minAmount); 217: } 223: function setMaxAmount(uint256 _maxAmount) external onlyOwner { 224: maxAmount = _maxAmount; 225: emit ChangeMaxAmount(maxAmount); 226: }
Recommendation:
Add at least that _minAmount <= _maxAmount
, _maxAmount > 0
requirement and check on zero address for addDerivative
Instances
contracts\SafEth\SafEth.sol 4: import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5: import "../interfaces/IWETH.sol"; 6: import "../interfaces/uniswap/ISwapRouter.sol"; 7: import "../interfaces/lido/IWStETH.sol"; 8: import "../interfaces/lido/IstETH.sol"; contracts\SafEth\derivatives\Reth.sol import "../../interfaces/frax/IsFrxEth.sol";
Instances
contracts\SafEth\derivatives\Reth.sol 238: factory.getPool(rocketTokenRETHAddress, W_ETH_ADDRESS, 500) 177: uint256 amountSwapped = swapExactInputSingleHop( 178: W_ETH_ADDRESS, 179: rethAddress(), 180: 500, 181: msg.value, 182: minOut 183: );
Empty or unused function parameters should be commented out as a better and declarative way to silence runtime warning messages.
Instances:
contracts\SafEth\derivatives\SfrxEth.sol 111: function ethPerDerivative(uint256 _amount) public view returns (uint256) contracts\SafEth\derivatives\WstEth.sol 86: function ethPerDerivative(uint256 _amount) public view returns (uint256)
contracts\SafEth\derivatives\SfrxEth.sol 111: function ethPerDerivative(uint256 /*_amount*/) public view returns (uint256)
uint256
and uint
are used.Which is misleading that uint can have a different value than uint256
https://github.com/ethereum/solidity/issues/14026
Instances:
contracts\SafEth\SafEth.sol 26: event Staked(address indexed recipient, uint ethIn, uint safEthOut); 27: event Unstaked(address indexed recipient, uint ethOut, uint safEthIn); 28: event WeightChange(uint indexed index, uint weight); 29: event DerivativeAdded( 30: address indexed contractAddress, 31: uint weight, 32: uint index 33: ); 71: for (uint i = 0; i < derivativeCount; i++) 84: for (uint i = 0; i < derivativeCount; i++) { 92: uint derivativeReceivedEthValue = (derivative.ethPerDerivative( 140: for (uint i = 0; i < derivativeCount; i++) { 147: for (uint i = 0; i < derivativeCount; i++) { 203: uint _derivativeIndex, 204: uint _slippage contracts\SafEth\derivatives\Reth.sol 171: uint rethPerEth = (10 ** 36) / poolPrice();
The order of functions does not correspond to any code style. private/internal
in the middle of the contract, etc
https://docs.soliditylang.org/en/v0.8.19/style-guide.html
contracts\SafEth\SafEth.sol 246: receive() external payable {} contracts\SafEth\derivatives\Reth.sol 50: function name() public pure returns (string memory) { 66: function rethAddress() private view returns (address) { 83: function swapExactInputSingleHop( 120: function poolCanDeposit(uint256 _amount) private view returns (bool) { 228: function poolPrice() private view returns (uint256) { 244: receive() external payable {} contracts\SafEth\derivatives\WstEth.sol 41: function name() public pure returns (string memory) { 97: receive() external payable {} contracts\SafEth\derivatives\SfrxEth.sol 44: function name() public pure returns (string memory) { 126: receive() external payable {}
#0 - c4-sponsor
2023-04-07T21:57:04Z
toshiSat marked the issue as sponsor acknowledged
#1 - c4-judge
2023-04-24T17:18:33Z
Picodes marked the issue as grade-b
π Selected for report: Rolezn
Also found by: 0x3b, 0xGordita, 0xSmartContract, 0xhacksmithh, 0xnev, 0xpanicError, 4lulz, Angry_Mustache_Man, ArbitraryExecution, Aymen0909, Bason, BlueAlder, EvanW, Franfran, HHK, Haipls, IgorZuk, JCN, KrisApostolov, Madalad, MiksuJak, MiniGlome, RaymondFam, ReyAdmirado, Rickard, Sathish9098, Udsen, adriro, alexzoid, anodaram, arialblack14, c3phas, carlitox477, ch0bu, chaduke, codeslide, d3e4, dicethedev, ernestognw, fatherOfBlocks, georgits, hunter_w3b, inmarelibero, lukris02, mahdirostami, maxper, pavankv, pixpi, rotcivegaf, smaul, tank, tnevler, wen, yac
90.7432 USDC - $90.74
Even when the weight for the derivative
was set to 0, it still affects the price of transactions
Instances:
contracts\SafEth\derivatives\SafEth.sol 71: for (uint i = 0; i < derivativeCount; i++) 72: underlyingValue += 73: (derivatives[i].ethPerDerivative(derivatives[i].balance()) * 74: derivatives[i].balance()) / 75: 10 ** 18; 84: for (uint i = 0; i < derivativeCount; i++) { 85: uint256 weight = weights[i]; 86: IDerivative derivative = derivatives[i]; 87: if (weight == 0) continue; 113: for (uint256 i = 0; i < derivativeCount; i++) { 114: // withdraw a percentage of each asset based on the amount of safETH 115: uint256 derivativeAmount = (derivatives[i].balance() * 116: _safEthAmount) / safEthTotalSupply; 117: if (derivativeAmount == 0) continue; // if derivative empty ignore 118: derivatives[i].withdraw(derivativeAmount);
In the contracts, there are duplicates of identical pieces of code that should be extracted into internal functions for optimizing contract size, gas usage, and readability.
Instances:
contracts\SafEth\derivatives\Reth.sol 121: address rocketDepositPoolAddress = RocketStorageInterface( 122: ROCKET_STORAGE_ADDRESS 123: ).getAddress( 124: keccak256( 125: abi.encodePacked("contract.address", "rocketDepositPool") 126: ) 127: ); 128: RocketDepositPoolInterface rocketDepositPool = RocketDepositPoolInterface( 129: rocketDepositPoolAddress 130: ); 158: address rocketDepositPoolAddress = RocketStorageInterface( 159: ROCKET_STORAGE_ADDRESS 160: ).getAddress( 161: keccak256( 162: abi.encodePacked("contract.address", "rocketDepositPool") 163: ) 164: ); 165: 166: RocketDepositPoolInterface rocketDepositPool = RocketDepositPoolInterface( 167: rocketDepositPoolAddress 168: );
keccak256
for abi.encodePacked("contract.address", ***)
should be pre calculateThe value of keccak256
for constants string is immutable and can be written as a constant or directly in code, without having to be called on every call
Instances:
contracts\SafEth\derivatives\Reth.sol 69: keccak256( 70: abi.encodePacked("contract.address", "rocketTokenRETH") 71: ) 124: keccak256( 125: abi.encodePacked("contract.address", "rocketDepositPool") 126: ) 135: keccak256( 136: abi.encodePacked( 137: "contract.address", 138: "rocketDAOProtocolSettingsDeposit" 139: ) 140: ) 161: keccak256( 162: abi.encodePacked("contract.address", "rocketDepositPool") 163: ) 190: keccak256( 191: abi.encodePacked("contract.address", "rocketTokenRETH") 192: ) 232: keccak256( 233: abi.encodePacked("contract.address", "rocketTokenRETH") 234: )
contract.address, rocketTokenRETH
should be use private rethAddress
methodIn the Reth.sol
contract, there are two instances of identical code that corresponds to the code in the rethAddress()
method. It is recommended to use the rethAddress()
call in those places instead.
contracts\SafEth\derivatives\Reth.sol 66: function rethAddress() private view returns (address) { 67: return 68: RocketStorageInterface(ROCKET_STORAGE_ADDRESS).getAddress( 69: keccak256( 70: abi.encodePacked("contract.address", "rocketTokenRETH") 71: ) 72: ); 73: } 187: address rocketTokenRETHAddress = RocketStorageInterface( 188: ROCKET_STORAGE_ADDRESS 189: ).getAddress( 190: keccak256( 191: abi.encodePacked("contract.address", "rocketTokenRETH") 192: ) 193: ); 229: address rocketTokenRETHAddress = RocketStorageInterface( 230: ROCKET_STORAGE_ADDRESS 231: ).getAddress( 232: keccak256( 233: abi.encodePacked("contract.address", "rocketTokenRETH") 234: ) 235: );
In the following cases, the call to storage or call the same method more than once to the same variable. Which is redundant and needs caching optimizations
stake()
:derivativeCount
2 time in methodderivatives[i].balance()
2 time in loopderivatives[i]
3 time in loopcontracts\SafEth\derivatives\SafEth.sol 71: for (uint i = 0; i < derivativeCount; i++) 84: for (uint i = 0; i < derivativeCount; i++) 72: underlyingValue += 73: (derivatives[i].ethPerDerivative(derivatives[i].balance()) * 74: derivatives[i].balance()) / 75: 10 ** 18;
unstake(uint256 _safEthAmount)
:derivatives[i]
2 time in loopcontracts\SafEth\derivatives\SafEth.sol 113: for (uint256 i = 0; i < derivativeCount; i++) { 114: // withdraw a percentage of each asset based on the amount of safETH 115: uint256 derivativeAmount = (derivatives[i].balance() * 116: _safEthAmount) / safEthTotalSupply; 117: if (derivativeAmount == 0) continue; // if derivative empty ignore 118: derivatives[i].withdraw(derivativeAmount); 119: }
rebalanceToWeights()
:derivativeCount
2 time in methodderivatives[i]
3 time in loopderivatives[i].balance
2 time in looptotalWeight
every iterationweights[i]
2 time in loopcontracts\SafEth\derivatives\SafEth.sol 140: for (uint i = 0; i < derivativeCount; i++) { 147: for (uint i = 0; i < derivativeCount; i++) { 140: for (uint i = 0; i < derivativeCount; i++) { 141: if (derivatives[i].balance() > 0) 142: derivatives[i].withdraw(derivatives[i].balance()); 143: } 150: totalWeight; 148: if (weights[i] == 0 || ethAmountToRebalance == 0) continue; 149: uint256 ethAmount = (ethAmountToRebalance * weights[i]) / 150: totalWeight;
rebalanceToWeights()
:derivativeCount
2 time in methodderivatives[i]
3 time in loopderivatives[i].balance
2 time in looptotalWeight
every iterationweights[i]
2 time in loopcontracts\SafEth\derivatives\SafEth.sol 140: for (uint i = 0; i < derivativeCount; i++) { 147: for (uint i = 0; i < derivativeCount; i++) { 140: for (uint i = 0; i < derivativeCount; i++) { 141: if (derivatives[i].balance() > 0) 142: derivatives[i].withdraw(derivatives[i].balance()); 143: } 150: totalWeight; 148: if (weights[i] == 0 || ethAmountToRebalance == 0) continue; 149: uint256 ethAmount = (ethAmountToRebalance * weights[i]) / 150: totalWeight;
addDerivative(address _contractAddress, uint256 _weight)
:derivativeCount
5 time in methodcontracts\SafEth\derivatives\SafEth.sol 186: derivatives[derivativeCount] = IDerivative(_contractAddress); 187: weights[derivativeCount] = _weight; 188: derivativeCount++; 189: 190: uint256 localTotalWeight = 0; 191: for (uint256 i = 0; i < derivativeCount; i++) 192: localTotalWeight += weights[i]; 193: totalWeight = localTotalWeight; 194: emit DerivativeAdded(_contractAddress, _weight, derivativeCount); 195: }
adjustWeight
, addDerivative
In the adjustWeight
and addDerivative
function, the loop for calculating the localTotalWeight
can be optimized by just updating the totalWeight
based on the change in weight rather than looping through all the derivatives.
contracts\SafEth\derivatives\SafEth.sol 169: weights[_derivativeIndex] = _weight; 170: uint256 localTotalWeight = 0; 171: for (uint256 i = 0; i < derivativeCount; i++) 172: localTotalWeight += weights[i]; 173: totalWeight = localTotalWeight; 174: emit WeightChange(_derivativeIndex, _weight); 190: uint256 localTotalWeight = 0; 191: for (uint256 i = 0; i < derivativeCount; i++) 192: localTotalWeight += weights[i]; 193: totalWeight = localTotalWeight;
Example solution:
// adjustWeight totalWeight = totalWeight - weights[_derivativeIndex] + _weight; weights[_derivativeIndex] = _weight; emit WeightChange(_derivativeIndex, _weight); // addDerivate totalWeight += _weight;
ethAmountToRebalance
equal ZEROWhen calling the rebalanceToWeights()
method, if ethAmountToRebalance
is equal to ZERO, going through the loop is redundant as every iteration in the loop will be skipped
contracts\SafEth\derivatives\SafEth.sol 145: uint256 ethAmountToRebalance = ethAmountAfter - ethAmountBefore; 146: 147: for (uint i = 0; i < derivativeCount; i++) { 148: if (weights[i] == 0 || ethAmountToRebalance == 0) continue; 149: uint256 ethAmount = (ethAmountToRebalance * weights[i]) / 150: totalWeight; 151: // Price will change due to slippage 152: derivatives[i].deposit{value: ethAmount}(); 153: }
It's recommended to move the condition ethAmountToRebalance == 0
outside the loop, for example:
uint256 ethAmountToRebalance = ethAmountAfter - ethAmountBefore; if (ethAmountToRebalance != 0){ for (uint i = 0; i < derivativeCount; i++) { if (weights[i] == 0) continue; uint256 ethAmount = (ethAmountToRebalance * weights[i]) / totalWeight; // Price will change due to slippage derivatives[i].deposit{value: ethAmount}(); } }
Dynamic calculation of values ββwith a known result should be avoided for better readability (reduction count of magic numbers) and lower gas prices
contracts\SafEth\derivatives\Reth.sol 44: maxSlippage = (1 * 10 ** 16); // 1% 171: uint rethPerEth = (10 ** 36) / poolPrice(); 173: uint256 minOut = ((((rethPerEth * msg.value) / 10 ** 18) * 174: ((10 ** 18 - maxSlippage))) / 10 ** 18); 214: RocketTokenRETHInterface(rethAddress()).getEthValue(10 ** 18); 215: else return (poolPrice() * 10 ** 18) / (10 ** 18); 241: return (sqrtPriceX96 * (uint(sqrtPriceX96)) * (1e18)) >> (96 * 2);
contracts\SafEth\derivatives\SfrxEth.sol 38: maxSlippage = (1 * 10 ** 16); // 1% 74: uint256 minOut = (((ethPerDerivative(_amount) * _amount) / 10 ** 18) * 75: (10 ** 18 - maxSlippage)) / 10 ** 18; 112: uint256 frxAmount = IsFrxEth(SFRX_ETH_ADDRESS).convertToAssets( 113: 10 ** 18 114: ); 115: return ((10 ** 18 * frxAmount) / 116: IFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).price_oracle());
contracts\SafEth\derivatives\WstEth.sol 35: maxSlippage = (1 * 10 ** 16); // 1% 60: uint256 minOut = (stEthBal * (10 ** 18 - maxSlippage)) / 10 ** 18; 87: return IWStETH(WST_ETH).getStETHByWstETH(10 ** 18);
contracts\SafEth\SafEth.sol 54: minAmount = 5 * 10 ** 17; // initializing with .5 ETH as minimum 55: maxAmount = 200 * 10 ** 18; // initializing with 200 ETH as maximum 72: underlyingValue += 73: (derivatives[i].ethPerDerivative(derivatives[i].balance()) * 74: derivatives[i].balance()) / 75: 10 ** 18; 79: if (totalSupply == 0) 80: preDepositPrice = 10 ** 18; // initializes with a price of 1 81: else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply; 92: uint derivativeReceivedEthValue = (derivative.ethPerDerivative( 93: depositAmount 94: ) * depositAmount) / 10 ** 18; 98: uint256 mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice;
10 ** 18
-> 1 ether
;1 * 10 ** 16
-> 1e16
| 0.01 ether
| move to constants ONE_PERCENTAGE
;5 * 10 ** 17
-> 0.5 ether
200 * 10 ** 18
-> 200 ether
96 * 2
-> 192
In 0.8.15 the conditions necessary for inlining are relaxed. Benchmarks show that the change significantly decreases the bytecode size (which impacts the deployment cost) while the effect on the runtime gas usage is smaller.
In 0.8.17 prevent the incorrect removal of storage writes before calls to Yul functions that conditionally terminate the external EVM call; Simplify the starting offset of zero-length operations to zero. More efficient overflow checks for multiplication.
In 0.8.18 Added new optimization
In 0.8.19 better finding and organizing duplicate assembly code
With the import statement, it saves gas to specifically import only the parts of the contracts, not the complete ones. Example:
import {contract1 , contract2} from "filename.sol";
Instances:
contracts\SafEth\derivatives\WstEth.sol 5: import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
contracts\SafEth\derivatives\Reth.sol 170: if (!poolCanDeposit(msg.value))
#0 - c4-sponsor
2023-04-07T21:55:58Z
toshiSat marked the issue as sponsor confirmed
#1 - c4-judge
2023-04-23T15:09:41Z
Picodes marked the issue as grade-a