Platform: Code4rena
Start Date: 13/05/2022
Pot Size: $30,000 USDC
Total HM: 8
Participants: 65
Period: 3 days
Judge: hickuphh3
Total Solo HM: 1
Id: 125
League: ETH
Rank: 15/65
Findings: 4
Award: $381.35
π Selected for report: 1
π Solo Findings: 0
π Selected for report: pedroais
Also found by: 0x4non, 0x52, 0xf15ers, 0xliumin, CertoraInc, Dravee, GimelSec, IllIllI, MaratCerby, StErMi, TerrierLover, WatchPug, berndartmueller, cccz, dipp, fatherOfBlocks, hake, hickuphh3, hyh, isamjay, mtz, oyc_109, p4st13r4, peritoflores, rotcivegaf, saian, simon135, sorrynotsorry, sseefried, tabish, z3s
14.8433 USDC - $14.84
If the call fails, the refunds did not succeed and the caller will lose all funds of receivedETHAmount
.
In the LidoVault
what heredit from GeneralVault
, the function withdrawCollateral
recibe an address _asset
and an address _to
If _asset
its address(0)
what reference ETH and the _to
its a contract performs a low-level .call
inside the _withdrawFromYieldPool
: (bool sent, bytes memory data) = address(_to).call{value: receivedETHAmount}('');
but does not check the return value if the call succeeded.
hardhat
#0 - sforman2000
2022-05-18T01:31:12Z
Duplicate of https://github.com/code-423n4/2022-05-sturdy-findings/issues/157 (high risk)
π Selected for report: rotcivegaf
Also found by: AuditsAreUS, MaratCerby, StErMi, berndartmueller, cccz, dipp
283.5578 USDC - $283.56
https://github.com/code-423n4/2022-05-sturdy/blob/78f51a7a74ebe8adfd055bdbaedfddc05632566f/smart-contracts/GeneralVault.sol#L75-L89 https://github.com/code-423n4/2022-05-sturdy/blob/78f51a7a74ebe8adfd055bdbaedfddc05632566f/smart-contracts/LidoVault.sol#L79-L104 https://github.com/code-423n4/2022-05-sturdy/blob/78f51a7a74ebe8adfd055bdbaedfddc05632566f/smart-contracts/ConvexCurveLPVault.sol#L131-L149
Possible lost value in depositCollateral
function call
In call depositCollateral
can will send value and the asset can be an ERC20(!= address(0)), if LidoVault
and ConvexCurveLPVault
contract receive this call the fouds will lost
Also in LidoVault, L88, if send as asset ETH(== address(0)) and send more value than _amount
(msg.value > _amount), the exedent will lost
In GeneralVault, depositCollateral
function:
msg.value
is zero when the _asset
is ERC20(!= address(0))msg.value
is equeal to _amount
when the _asset
ETH(== address(0))function depositCollateral(address _asset, uint256 _amount) external payable virtual { if (_asset != address(0)) { // asset = ERC20 require(msg.value == 0, <AN ERROR FROM Errors LIBRARY>); } else { // asset = ETH require(msg.value == _amount, <AN ERROR FROM Errors LIBRARY>); } // Deposit asset to vault and receive stAsset // Ex: if user deposit 100ETH, this will deposit 100ETH to Lido and receive 100stETH TODO No Lido (address _stAsset, uint256 _stAssetAmount) = _depositToYieldPool(_asset, _amount); // Deposit stAsset to lendingPool, then user will get aToken of stAsset ILendingPool(_addressesProvider.getLendingPool()).deposit( _stAsset, _stAssetAmount, msg.sender, 0 ); emit DepositCollateral(_asset, msg.sender, _amount); }
Also can remove the require(msg.value > 0, Errors.VT_COLLATERAL_DEPOSIT_REQUIRE_ETH);
in LidoVault, L88
π Selected for report: IllIllI
Also found by: 0x1f8b, 0x4non, 0xNazgul, 0xf15ers, 0xkatana, 0xliumin, AlleyCat, BouSalman, Dravee, Funen, GimelSec, Hawkeye, MaratCerby, Picodes, StErMi, TerrierLover, WatchPug, Waze, berndartmueller, bobirichman, cryptphi, csanuragjain, defsec, delfin454000, dipp, fatherOfBlocks, hake, hickuphh3, hyh, joestakey, kebabsec, mics, mtz, oyc_109, p4st13r4, p_crypt0, robee, rotcivegaf, sikorico, simon135, sorrynotsorry, tintin
47.9905 USDC - $47.99
Contest: Sturdy
Autor: Rotcivegaf
Scope:
ConvexCurveLPVault.sol, L154
function _getWithdrawalAmount(address _asset, uint256 _amount) ^------------^
GeneralVault.sol, L136
function withdrawOnLiquidation(address _asset, uint256 _amount) ^------------^
LidoVault.sol, L91
LidoVault.sol:91:19: Warning: Unused local variable. (bool sent, bytes memory data) = LIDO.call{value: msg.value}(''); ^---------------^
LidoVault.sol, L109
function _getWithdrawalAmount(address _asset, uint256 _amount) ^------------^
LidoVault.sol, L140
(bool sent, bytes memory data) = address(_to).call{value: receivedETHAmount}(''); ^---------------^
Change the GeneralVault contract to abstract contract to avoid the deploy by mistake and define the functions without implementation:
processYield
replace {}
to ;
pricePerShare
replace {}
to ;
_depositToYieldPool
replace {}
to ;
_withdrawFromYieldPool
replace {}
to ;
_getWithdrawalAmount
replace {}
to ;
The withdrawOnLiquidation
(L136) function only returns the _amount
parameter, avoid the implementation to avoid mistakes and that the inherited contract implements it
In some functions/variables missing documentation:
In GeneralVault, the depositCollateral
(L75) function:
The safeApprove
of _stAsset
should call inside this function and just before deposit
and no in the _depositToYieldPool
function depositCollateral(address _asset, uint256 _amount) external payable virtual { // Deposit asset to vault and receive stAsset // Ex: if user deposit 100ETH, this will deposit 100ETH to Lido and receive 100stETH TODO No Lido (address _stAsset, uint256 _stAssetAmount) = _depositToYieldPool(_asset, _amount); IERC20(_stAsset).safeApprove(address(_addressesProvider.getLendingPool()), _stAssetAmount); // Deposit stAsset to lendingPool, then user will get aToken of stAsset ILendingPool(_addressesProvider.getLendingPool()).deposit( _stAsset, _stAssetAmount, msg.sender, 0 ); emit DepositCollateral(_asset, msg.sender, _amount); }
Remember import SafeERC20
library and using SafeERC20 for IERC20;
In CollateralAdapter, the import {ILendingPool} from '../../interfaces/ILendingPool.sol';
is unused
In GeneralVault, the import {ILendingPool} from '../../interfaces/ILendingPool.sol';
is unused
In YieldManager, the import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol';
, import {ISwapRouter} from '../../interfaces/ISwapRouter.sol';
, import {TransferHelper} from '../libraries/helpers/TransferHelper.sol';
are unused
withdrawOnLiquidation
only returns _amount
The contract LidoVault heredit from GeneralVault the function withdrawOnLiquidation
but dont reimplement it, an user can use this function by mistake esperando withdraw a collateral
Implement it with a revert to avoid mistake calls
function withdrawOnLiquidation(address, uint256) external virtual returns (uint256) { revert(<AN ERROR FROM Errors LIBRARY>); }
GeneralVault.sol, L77: There is an open TODO
and don't understand what it mean
GeneralVault.sol, L148: The function convertOnLiquidation
its commented
#0 - HickupHH3
2022-06-06T08:44:21Z
NC: NC-01 + NC-05, NC-02, NC-03, NC-04, L-02, L-03 Invalid: L-01. Reverting actually might be a bug because it affects the LendingPool (OOS) flow and break liquidation flows.
π Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0x4non, 0xNazgul, 0xf15ers, 0xkatana, 0xliumin, Cityscape, Dravee, Fitraldys, Funen, GimelSec, Hawkeye, JC, MaratCerby, SooYa, StErMi, Tomio, WatchPug, Waze, bobirichman, defsec, delfin454000, fatherOfBlocks, hake, hansfriese, hickuphh3, ignacio, joestakey, kebabsec, mics, mtz, oyc_109, robee, rotcivegaf, samruna, sikorico, simon135, z3s
34.9587 USDC - $34.96
Contest: Sturdy
Autor: Rotcivegaf
Scope:
ConvexCurveLPVault.sol, L138 L141: Replace curveLPToken
to _asset
, this save the gas to SLOAD
ConvexCurveLPVault.sol, L44 L45: cache IERC20Detailed(_lpToken).symbol())
in a local variable to save gas of storage reads
convexBooster
contract variableConvexCurveLPVault.sol, L27 L40: address public convexBooster;
to address public constant convexBooster;
and remove the set in setConfiguration
function
When the value of the post-loop increment/decrement is not stored or used in any calculations, the prefix increment/decrement operators (++<var>
/--<var>
) cost less gas PER LOOP than the postfix increment/decrement operators (<var>++
/<var>--
)
Use ++<var>
rather than <var>++
to save gas
In ConvexCurveLPVault.sol, L106 In GeneralVault.sol, L218 In YieldManager.sol, L120, L130, L156
In YieldManager.sol, L121: Move _offset + i
outside the for
to consume less gas
_count = _offset + _offset; for (uint256 i = _offset; i < _count; i++) { address asset = _assetsList[i]; require(asset != address(0), Errors.UL_INVALID_INDEX); uint256 _amount = IERC20Detailed(asset).balanceOf(address(this)); _convertAssetToExchangeToken(asset, _amount); }
In YieldManager.sol, L130: Caching the array length is more gas efficient
uint256 assetYieldsLength = assetYields.length; for (uint256 i = 0; i < assetYieldsLength; i++) { ...
In YieldManager.sol, L158: Move length - 1
outside the for
to consume less gas
uint256 lengthLessOne = length - 1; for (uint256 i = 0; i < length; i++) { assetYields[i].asset = assets[i]; if (i != lengthLessOne) { // Distribute yieldAmount based on percent of asset volume assetYields[i].amount = _totalYieldAmount.percentMul( volumes[i].mul(PercentageMath.PERCENTAGE_FACTOR).div(totalVolume) ); extraYieldAmount = extraYieldAmount.sub(assetYields[i].amount); } else { // without calculation, set remained extra amount assetYields[i].amount = extraYieldAmount; } }