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: 29/65
Findings: 3
Award: $84.06
π Selected for report: 0
π 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
Sending ETH to LIDO can fail makes a return without checking the sent
variable
(bool sent, bytes memory data) = address(_to).call{value: receivedETHAmount}(''); return receivedETHAmount; require(sent, Errors.VT_COLLATERAL_WITHDRAW_INVALID);
Imagine that sent is false, then it will return receivedETHAmount but no ETH was received.
Manual review
Change
(bool sent, bytes memory data) = address(_to).call{value: receivedETHAmount}(''); return receivedETHAmount; require(sent, Errors.VT_COLLATERAL_WITHDRAW_INVALID);
For
(bool sent, bytes memory data) = address(_to).call{value: receivedETHAmount}(''); require(sent, Errors.VT_COLLATERAL_WITHDRAW_INVALID); return receivedETHAmount;
#0 - sforman2000
2022-05-18T01:30:50Z
Duplicate of https://github.com/code-423n4/2022-05-sturdy-findings/issues/157 (high risk)
π 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
44.8581 USDC - $44.86
Missing event emission, critical function addCollateralAsset
doesnt emmit any event
L43
Function addCollateralAsset
doesnt validate vars _acceptVault
and _internalAsset
Recommentation add validation;
function addCollateralAsset( address _externalAsset, address _internalAsset, address _acceptVault ) external onlyAdmin { require(_externalAsset != address(0), "External asset address is not valid"); require(_internalAsset != address(0), "Internal asset address is not valid"); _assetToVaults[_externalAsset] = _acceptVault; _collateralAssets[_externalAsset] = _internalAsset; }
Variable _acceptVault
its not used in function addCollateralAsset
L43
Critical function withdrawOnLiquidation(address _asset, uint256 _amount)
doesnt emit any event
L178
Consider remove the next line thats never used; L47
Consider change >=
to >
in L196
--- a/contracts/protocol/vault/GeneralVault.sol +++ b/contracts/protocol/vault/GeneralVault.sol @@ -193,7 +193,7 @@ contract GeneralVault is VersionedInitializable { // when deposit for collateral, stAssetBalance = aTokenBalance // But stAssetBalance should increase overtime, so vault can grab yield from lendingPool. // yield = stAssetBalance - aTokenBalance - if (stAssetBalance >= aTokenBalance) return stAssetBalance.sub(aTokenBalance); + if (stAssetBalance > aTokenBalance) return stAssetBalance.sub(aTokenBalance); return 0; }
Consider add a validario on _asset
variable
function registerAsset(address _asset) external onlyAdmin { require(_asset != address(0), "Asset address cannot be 0"); _assetsList[_assetsCount] = _asset; _assetsCount = _assetsCount + 1; }
Critical Functions setExchangeToken
, registerAsset
, setCurvePool
and distributeYield
dont emmit events,
setExchangeToken L64
registerAsset L73
setCurvePool L92
distributeYield L118
#0 - HickupHH3
2022-06-06T03:00:02Z
Low issues: NC issues: Empty address checks, event emission, Unused variable _acceptVault Invalid: Unused constant variable ETH (reason: can be used by other vaults in the future)
#1 - HickupHH3
2022-06-06T03:01:36Z
P.S I will prefer if report is formatted to grouping by issues found, not contract. Easier to grade =)
#2 - HickupHH3
2022-06-06T08:54:36Z
"Simplify if" is a gas opt
π 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
24.3601 USDC - $24.36
Variable convexBooster
should be a constant
--- a/contracts/protocol/vault/ethereum/ConvexVault/ConvexCurveLPVault.sol +++ b/contracts/protocol/vault/ethereum/ConvexVault/ConvexCurveLPVault.sol @@ -24,7 +24,7 @@ interface IRewards { contract ConvexCurveLPVault is GeneralVault { using SafeERC20 for IERC20; - address public convexBooster; + address public constant convexBooster = 0xF403C135812408BFbE8713b5A23a04b3D48AAE31; address internal curveLPToken; address internal internalAssetToken; uint256 internal convexPoolId; @@ -37,7 +37,6 @@ contract ConvexCurveLPVault is GeneralVault { function setConfiguration(address _lpToken, uint256 _poolId) external onlyAdmin { require(internalAssetToken == address(0), Errors.VT_INVALID_CONFIGURATION); - convexBooster = 0xF403C135812408BFbE8713b5A23a04b3D48AAE31; curveLPToken = _lpToken; convexPoolId = _poolId; SturdyInternalAsset _interalToken = new SturdyInternalAsset(
i++ is generally more expensive because it must increment a value and "return" the old value, so it may require holding two numbers in memory. ++i only ever uses one number in memory.
--- a/contracts/protocol/vault/GeneralVault.sol +++ b/contracts/protocol/vault/GeneralVault.sol @@ -215,7 +215,7 @@ contract GeneralVault is VersionedInitializable { AssetYield[] memory assetYields = new AssetYield[](length); uint256 extraWETHAmount = _WETHAmount; - for (uint256 i = 0; i < length; i++) { + for (uint256 i = 0; i < length; ++i) { assetYields[i].asset = assets[i]; if (i != length - 1) { // Distribute wethAmount based on percent of asset volume
i++ is generally more expensive because it must increment a value and "return" the old value, so it may require holding two numbers in memory. ++i only ever uses one number in memory.
--- a/contracts/protocol/vault/YieldManager.sol +++ b/contracts/protocol/vault/YieldManager.sol @@ -117,7 +117,7 @@ contract YieldManager is VersionedInitializable, Ownable { **/ function distributeYield(uint256 _offset, uint256 _count) external onlyAdmin { // 1. convert from asset to exchange token via uniswap - for (uint256 i = 0; i < _count; i++) { + for (uint256 i = 0; i < _count; ++i) { address asset = _assetsList[_offset + i]; require(asset != address(0), Errors.UL_INVALID_INDEX); uint256 _amount = IERC20Detailed(asset).balanceOf(address(this)); @@ -127,7 +127,7 @@ contract YieldManager is VersionedInitializable, Ownable { // 2. convert from exchange token to other stable assets via curve swap AssetYield[] memory assetYields = _getAssetYields(exchangedAmount); - for (uint256 i = 0; i < assetYields.length; i++) { + for (uint256 i = 0; i < assetYields.length; ++i) { if (assetYields[i].amount > 0) { uint256 _amount = _convertToStableCoin(assetYields[i].asset, assetYields[i].amount); // 3. deposit Yield to pool for suppliers @@ -153,7 +153,7 @@ contract YieldManager is VersionedInitializable, Ownable { AssetYield[] memory assetYields = new AssetYield[](length); uint256 extraYieldAmount = _totalYieldAmount; - for (uint256 i = 0; i < length; i++) { + for (uint256 i = 0; i < length; ++i) { assetYields[i].asset = assets[i]; if (i != length - 1) { // Distribute yieldAmount based on percent of asset volume