Platform: Code4rena
Start Date: 05/05/2022
Pot Size: $125,000 DAI
Total HM: 17
Participants: 62
Period: 14 days
Judge: leastwood
Total Solo HM: 15
Id: 120
League: ETH
Rank: 24/62
Findings: 2
Award: $305.22
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x4non, 0xDjango, 0xNazgul, 0xkatana, 0xsomeone, AuditsAreUS, BouSalman, BowTiedWardens, Cityscape, Funen, GimelSec, Hawkeye, JC, MaratCerby, MiloTruck, Picodes, Ruhum, TerrierLover, WatchPug, Waze, bobirichman, catchup, cccz, cryptphi, csanuragjain, delfin454000, ellahi, fatherOfBlocks, hake, horsefacts, hyh, jayjonah8, joestakey, kebabsec, kenta, mics, oyc_109, robee, samruna, shenwilly, sikorico, simon135, throttle, tintin
178.6573 DAI - $178.66
Multiples files have a floating pragma, consider move to a fixed version. Source; https://swcregistry.io/docs/SWC-103
Critical function setWhitelist
and setMaxFlashLoan
doesnt emit event
Critical function setMaxFlashLoan
doesnt emit event
L164
_maxFlashLoanAmount
Vars name
and symbol
are defined in the ERC20Upgradeable, consider renaming it to _name
and _symbol
--- a/contracts-hardhat/CrossChainCanonicalAlchemicTokenV2.sol +++ b/contracts-hardhat/CrossChainCanonicalAlchemicTokenV2.sol @@ -6,13 +6,13 @@ import {AlchemicTokenV2Base} from "./AlchemicTokenV2Base.sol"; contract CrossChainCanonicalAlchemicTokenV2 is CrossChainCanonicalBase, AlchemicTokenV2Base { function initialize( - string memory name, - string memory symbol, + string memory _name, + string memory _symbol, address[] memory _bridgeTokens ) public initializer { __CrossChainCanonicalBase_init( - name, - symbol, + _name, + _symbol, msg.sender, _bridgeTokens );
Critical function transferOwnership
doesnt emit event
L39
success
variableConsider use the variable to check success approve
--- a/contracts-hardhat/gALCX.sol +++ b/contracts-hardhat/gALCX.sol @@ -61,6 +61,7 @@ contract gALCX is ERC20 { /// @notice Approve the staking pool to move funds in this address, can be called by anyone function reApprove() public { bool success = alcx.approve(address(pools), type(uint).max); + require(success, "Failed to approve"); } // PUBLIC FUNCTIONS
Vars name
and symbol
are defined in the ERC20Upgradeable, consider renaming it to _name
and _symbol
--- a/contracts-hardhat/CrossChainCanonicalGALCX.sol +++ b/contracts-hardhat/CrossChainCanonicalGALCX.sol @@ -5,13 +5,13 @@ import {CrossChainCanonicalBase} from "./CrossChainCanonicalBase.sol"; contract CrossChainCanonicalGALCX is CrossChainCanonicalBase { function initialize( - string memory name, - string memory symbol, + string memory _name, + string memory _symbol, address[] memory _bridgeTokens ) public initializer { __CrossChainCanonicalBase_init( - name, - symbol, + _name, + _symbol, msg.sender, _bridgeTokens );
Function setWeights
shoud assert that tokens and weights arrays has the same length.
Recommendation, add;
function setWeights( address weightToken, address[] memory tokens, uint256[] memory weights ) external override onlyAdmin { assert( tokens.length == weights.length, "Length of tokens and weights must be equal" );
Critical function setWeights
should emit event
L178
Critical function setCollateralSource
should emit event
#0 - 0xleastwood
2022-06-09T20:57:00Z
All issues are non-critical.
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0x4non, 0xDjango, 0xNazgul, 0xf15ers, 0xkatana, 0xsomeone, AlleyCat, BowTiedWardens, Cityscape, Fitraldys, Funen, GimelSec, Hawkeye, JC, MaratCerby, MiloTruck, Randyyy, TerrierLover, Tomio, UnusualTurtle, WatchPug, Waze, _Adam, augustg, bobirichman, catchup, csanuragjain, ellahi, fatherOfBlocks, hake, hansfriese, horsefacts, ignacio, joestakey, kenta, mics, oyc_109, robee, samruna, sashik_eth, sikorico, simon135, throttle
126.5591 DAI - $126.56
Ownable.sol is imported but not used, consider remove the import<br /> L6
alcx
--- a/contracts-hardhat/gALCX.sol +++ b/contracts-hardhat/gALCX.sol @@ -9,7 +9,7 @@ import {IALCXSource} from "./interfaces/IALCXSource.sol"; /// @title A wrapper for single-sided ALCX staking contract gALCX is ERC20 { - IERC20 public alcx = IERC20(0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF); + IERC20 public constant alcx = IERC20(0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF); IALCXSource public pools = IALCXSource(0xAB8e74017a8Cc7c15FFcCd726603790d26d7DeCa); uint public poolId = 1; uint public constant exchangeRatePrecision = 1e18;
++i;
to increment variablesYou could also use the pattern, this pattern is more gas efficient;
for(uint i = 0; i < N; ) { // CODE unchecked { i++; } }
Recommendation
--- a/contracts-full/AlchemistV2.sol +++ b/contracts-full/AlchemistV2.sol @@ -987,7 +987,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { /// @param owner The address which owns the account. function _preemptivelyHarvestDeposited(address owner) internal { Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; - for (uint256 i = 0; i < depositedTokens.values.length; i++) { + for (uint256 i = 0; i < depositedTokens.values.length; ++i) { _preemptivelyHarvest(depositedTokens.values[i]); } } @@ -1279,7 +1279,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { /// @param owner The address of the account owner. function _distributeUnlockedCreditDeposited(address owner) internal { Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; - for (uint256 i = 0; i < depositedTokens.values.length; i++) { + for (uint256 i = 0; i < depositedTokens.values.length; ++i) { _distributeUnlockedCredit(depositedTokens.values[i]); } } @@ -1352,7 +1352,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { /// @param owner The address of the account owner. function _poke(address owner) internal { Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; - for (uint256 i = 0; i < depositedTokens.values.length; i++) { + for (uint256 i = 0; i < depositedTokens.values.length; ++i) { _poke(owner, depositedTokens.values[i]); } } @@ -1458,7 +1458,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { uint256 totalValue = 0; Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; - for (uint256 i = 0; i < depositedTokens.values.length; i++) { + for (uint256 i = 0; i < depositedTokens.values.length; ++i) { address yieldToken = depositedTokens.values[i]; address underlyingToken = _yieldTokens[yieldToken].underlyingToken; uint256 shares = _accounts[owner].balances[yieldToken]; @@ -1521,7 +1521,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { int256 debt = _accounts[owner].debt; Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; - for (uint256 i = 0; i < depositedTokens.values.length; i++) { + for (uint256 i = 0; i < depositedTokens.values.length; ++i) { address yieldToken = depositedTokens.values[i]; uint256 currentAccruedWeight = _yieldTokens[yieldToken].accruedWeight;
--- a/contracts-full/AlchemistV2.sol +++ b/contracts-full/AlchemistV2.sol @@ -1241,7 +1241,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { /// /// @return The loss in basis points. function _loss(address yieldToken) internal view returns (uint256) { - YieldTokenParams memory yieldTokenParams = _yieldTokens[yieldToken]; + YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; uint256 amountUnderlyingTokens = _convertYieldTokensToUnderlying(yieldToken, yieldTokenParams.activeBalance); uint256 expectedUnderlyingValue = yieldTokenParams.expectedValue; @@ -1278,7 +1278,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { /// /// @param owner The address of the account owner. function _distributeUnlockedCreditDeposited(address owner) internal { - Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; + Sets.AddressSet memory depositedTokens = _accounts[owner].depositedTokens; for (uint256 i = 0; i < depositedTokens.values.length; i++) { _distributeUnlockedCredit(depositedTokens.values[i]); } @@ -1311,7 +1311,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { uint256 amount, uint256 minimumAmountOut ) internal returns (uint256) { - YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; + YieldTokenParams memory yieldTokenParams = _yieldTokens[yieldToken]; ITokenAdapter adapter = ITokenAdapter(yieldTokenParams.adapter); address underlyingToken = yieldTokenParams.underlyingToken; @@ -1351,7 +1351,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { /// /// @param owner The address of the account owner. function _poke(address owner) internal { - Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; + Sets.AddressSet memory depositedTokens = _accounts[owner].depositedTokens; for (uint256 i = 0; i < depositedTokens.values.length; i++) { _poke(owner, depositedTokens.values[i]); } @@ -1457,7 +1457,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { function _totalValue(address owner) internal view returns (uint256) { uint256 totalValue = 0; - Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; + Sets.AddressSet memory depositedTokens = _accounts[owner].depositedTokens; for (uint256 i = 0; i < depositedTokens.values.length; i++) { address yieldToken = depositedTokens.values[i]; address underlyingToken = _yieldTokens[yieldToken].underlyingToken; @@ -1553,7 +1553,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { /// /// @return The virtual active balance. function _calculateUnrealizedActiveBalance(address yieldToken) internal view returns (uint256) { - YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; + YieldTokenParams memory yieldTokenParams = _yieldTokens[yieldToken]; uint256 activeBalance = yieldTokenParams.activeBalance; if (activeBalance == 0) { @@ -1580,7 +1580,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { /// /// @return The amount of unlocked credit available. function _calculateUnlockedCredit(address yieldToken) internal view returns (uint256) { - YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; + YieldTokenParams memory yieldTokenParams = _yieldTokens[yieldToken]; uint256 pendingCredit = yieldTokenParams.pendingCredit; if (pendingCredit == 0) { @@ -1643,7 +1643,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { /// /// @return The amount of underlying tokens. function _convertYieldTokensToUnderlying(address yieldToken, uint256 amount) internal view returns (uint256) { - YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; + YieldTokenParams memory yieldTokenParams = _yieldTokens[yieldToken]; ITokenAdapter adapter = ITokenAdapter(yieldTokenParams.adapter); return amount * adapter.price() / 10**yieldTokenParams.decimals; } @@ -1655,7 +1655,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { /// /// @return The amount of yield tokens. function _convertUnderlyingTokensToYield(address yieldToken, uint256 amount) internal view returns (uint256) { - YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; + YieldTokenParams memory yieldTokenParams = _yieldTokens[yieldToken]; ITokenAdapter adapter = ITokenAdapter(yieldTokenParams.adapter); return amount * 10**yieldTokenParams.decimals / adapter.price(); }
--- a/contracts-full/AlchemistV2.sol +++ b/contracts-full/AlchemistV2.sol @@ -1224,14 +1224,14 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { uint256 amount, function(uint256, uint256) internal pure returns (uint256) operation ) internal { - YieldTokenParams memory yieldTokenParams = _yieldTokens[yieldToken]; + YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; uint256 amountUnderlyingTokens = _convertYieldTokensToUnderlying(yieldToken, amount); uint256 updatedActiveBalance = operation(yieldTokenParams.activeBalance, amount); uint256 updatedExpectedValue = operation(yieldTokenParams.expectedValue, amountUnderlyingTokens); - _yieldTokens[yieldToken].activeBalance = updatedActiveBalance; - _yieldTokens[yieldToken].expectedValue = updatedExpectedValue; + yieldTokenParams.activeBalance = updatedActiveBalance; + yieldTokenParams.expectedValue = updatedExpectedValue; } /// @dev Gets the amount of loss that `yieldToken` has incurred measured in basis points. When the expected @@ -1456,12 +1456,13 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { /// @return The total value. function _totalValue(address owner) internal view returns (uint256) { uint256 totalValue = 0; + Account storage account = _accounts[owner]; - Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; + Sets.AddressSet storage depositedTokens = account.depositedTokens; for (uint256 i = 0; i < depositedTokens.values.length; i++) { address yieldToken = depositedTokens.values[i]; address underlyingToken = _yieldTokens[yieldToken].underlyingToken; - uint256 shares = _accounts[owner].balances[yieldToken]; + uint256 shares = account.balances[yieldToken]; uint256 amountUnderlyingTokens = _convertSharesToUnderlyingTokens(yieldToken, shares); totalValue += _normalizeUnderlyingTokensToDebt(underlyingToken, amountUnderlyingTokens); @@ -1486,11 +1487,13 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { ) internal returns (uint256) { uint256 shares = _convertYieldTokensToShares(yieldToken, amount); - if (_accounts[recipient].balances[yieldToken] == 0) { - _accounts[r@@ -379,12 +379,12 @@ contract TransmuterBuffer is ITransmuterBuffer, AccessControl, Initializable { } // clear current strats - for (uint256 j = 0; j < registeredUnderlyings.length; j++) { + for (uint256 j = 0; j < registeredUnderlyings.length; ++j) { delete _yieldTokens[registeredUnderlyings[j]]; } uint256 numYTokens = supportedYieldTokens.length; - for (uint256 i = 0; i < numYTokens; i++) { + for (uint256 i = 0; i < numYTokens; ++i) { address yieldToken = supportedYieldTokens[i]; IAlchemistV2.YieldTokenParams memory params = IAlchemistV2(alchemist) @@ -476,7 +476,7 @@ contract TransmuterBuffer is ITransmuterBuffer, AccessControl, Initializable { IAlchemistV2(alchemist).poke(address(this)); Weighting storage weighting = weightings[weightToken]; - for (uint256 j = 0; j < weighting.tokens.length; j++) { + for (uint256 j = 0; j < weighting.tokens.length; ++j) { address token = weighting.tokens[j]; uint256 actionAmt = (amount * weighting.weights[token]) / weighting.totalWeight; action(token, actionAmt); depositedTokens.add(yieldToken); } - _accounts[recipient].balances[yieldToken] += shares; + account.balances[yieldToken] += shares; _yieldTokens[yieldToken].totalShares += shares; return shares; @@ -1518,14 +1521,15 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { /// /// @return The amount of debt that the account owned by `owner` will have after an update. function _calculateUnrealizedDebt(address owner) internal view returns (int256) { - int256 debt = _accounts[owner].debt; + Account storage account = _accounts[owner]; + int256 debt = account.debt; - Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; + Sets.AddressSet storage depositedTokens = account.depositedTokens; for (uint256 i = 0; i < depositedTokens.values.length; i++) { address yieldToken = depositedTokens.values[i]; uint256 currentAccruedWeight = _yieldTokens[yieldToken].accruedWeight; - uint256 lastAccruedWeight = _accounts[owner].lastAccruedWeights[yieldToken]; + uint256 lastAccruedWeight = account.lastAccruedWeights[yieldToken]; uint256 unlockedCredit = _calculateUnlockedCredit(yieldToken); currentAccruedWeight += unlockedCredit > 0 @@ -1536,7 +1540,7 @@ contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { continue; } - uint256 balance = _accounts[owner].balances[yieldToken]; + uint256 balance = account.balances[yieldToken]; uint256 unrealizedCredit = ((currentAccruedWeight - lastAccruedWeight) * balance) / FIXED_POINT_SCALAR; debt -= SafeCast.toInt256(unrealizedCredit);
Since solidity 0.8 got arithmetics checks you dont need to use safemath
--- a/contracts-hardhat/TransmuterBuffer.sol +++ b/contracts-hardhat/TransmuterBuffer.sol @@ -3,8 +3,6 @@ pragma solidity ^0.8.11; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import "@openzeppelin/contracts/utils/math/Math.sol"; -import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "./base/Errors.sol"; @@ -24,7 +22,6 @@ import "./interfaces/IERC20TokenReceiver.sol"; /// /// @notice An interface contract to buffer funds between the Alchemist and the Transmuter contract TransmuterBuffer is ITransmuterBuffer, AccessControl, Initializable { - using SafeMath for uint256; using FixedPointMath for FixedPointMath.Number; /// @notice The identifier of the role which maintains other roles.
++i;
to increment variablesYou could also use the pattern, this pattern is more gas efficient;
for(uint i = 0; i < N; ) { // CODE unchecked { i++; } }
Recommendation
--- a/contracts-hardhat/TransmuterBuffer.sol +++ b/contracts-hardhat/TransmuterBuffer.sol @@ -169,7 +169,7 @@ contract TransmuterBuffer is ITransmuterBuffer, AccessControl, Initializable { returns (uint256 totalBuffered) { totalBuffered = TokenUtils.safeBalanceOf(underlyingToken, address(this)); - for (uint256 i = 0; i < _yieldTokens[underlyingToken].length; i++) { + for (uint256 i = 0; i < _yieldTokens[underlyingToken].length; ++i) { totalBuffered += _getTotalBuffered(_yieldTokens[underlyingToken][i]); } } @@ -183,7 +183,7 @@ contract TransmuterBuffer is ITransmuterBuffer, AccessControl, Initializable { Weighting storage weighting = weightings[weightToken]; delete weighting.tokens; weighting.totalWeight = 0; - for (uint256 i = 0; i < tokens.length; i++) { + for (uint256 i = 0; i < tokens.length; ++i) { address yieldToken = tokens[i]; // For any weightToken that is not the debtToken, we want to verify that the yield-tokens being @@ -232,14 +232,14 @@ contract TransmuterBuffer is ITransmuterBuffer, AccessControl, Initializable { sources[_alchemist] = true; if (alchemist != address(0)) { - for (uint256 i = 0; i < registeredUnderlyings.length; i++) { + for (uint256 i = 0; i < registeredUnderlyings.length; ++i) { TokenUtils.safeApprove(registeredUnderlyings[i], alchemist, 0); } TokenUtils.safeApprove(debtToken, alchemist, 0); } alchemist = _alchemist; - for (uint256 i = 0; i < registeredUnderlyings.length; i++) { + for (uint256 i = 0; i < registeredUnderlyings.length; ++i) { TokenUtils.safeApprove(registeredUnderlyings[i], alchemist, type(uint256).max); } TokenUtils.safeApprove(debtToken, alchemist, type(uint256).max); @@ -269,7 +269,7 @@ contract TransmuterBuffer is ITransmuterBuffer, AccessControl, Initializable { } // only add to the array if not already contained in it - for (uint256 i = 0; i < registeredUnderlyings.length; i++) { + for (uint256 i = 0; i < registeredUnderlyings.length; ++i) { if (registeredUnderlyings[i] == underlyingToken) { revert IllegalState(); } @@ -379,12 +379,12 @@ contract TransmuterBuffer is ITransmuterBuffer, AccessControl, Initializable { } // clear current strats - for (uint256 j = 0; j < registeredUnderlyings.length; j++) { + for (uint256 j = 0; j < registeredUnderlyings.length; ++j) { delete _yieldTokens[registeredUnderlyings[j]]; } uint256 numYTokens = supportedYieldTokens.length; - for (uint256 i = 0; i < numYTokens; i++) { + for (uint256 i = 0; i < numYTokens; ++i) { address yieldToken = supportedYieldTokens[i]; IAlchemistV2.YieldTokenParams memory params = IAlchemistV2(alchemist) @@ -476,7 +476,7 @@ contract TransmuterBuffer is ITransmuterBuffer, AccessControl, Initializable { IAlchemistV2(alchemist).poke(address(this)); Weighting storage weighting = weightings[weightToken]; - for (uint256 j = 0; j < weighting.tokens.length; j++) { + for (uint256 j = 0; j < weighting.tokens.length; ++j) { address token = weighting.tokens[j]; uint256 actionAmt = (amount * weighting.weights[token]) / weighting.totalWeight; action(token, actionAmt);
++i;
to increment variablesYou could also use the pattern, this pattern is more gas efficient;
for(uint i = 0; i < N; ) { // CODE unchecked { i++; } }
Recommendation
--- a/contracts-full/EthAssetManager.sol +++ b/contracts-full/EthAssetManager.sol @@ -211,7 +211,7 @@ contract EthAssetManager is Multicall, Mutex, IERC20TokenReceiver { convexRewards = params.convexRewards; convexPoolId = params.convexPoolId; - for (uint256 i = 0; i < NUM_META_COINS; i++) { + for (uint256 i = 0; i < NUM_META_COINS; ++i) { _metaPoolAssetCache[i] = params.metaPool.coins(i); if (_metaPoolAssetCache[i] == IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)) { _metaPoolAssetCache[i] = weth; @@ -564,7 +564,7 @@ contract EthAssetManager is Multicall, Mutex, IERC20TokenReceiver { IERC20[NUM_META_COINS] memory tokens = _metaPoolAssetCache; uint256 total = 0; - for (uint256 i = 0; i < NUM_META_COINS; i++) { + for (uint256 i = 0; i < NUM_META_COINS; ++i) { // Skip over approving WETH since we are directly swapping ETH. if (i == uint256(MetaPoolAsset.ETH)) continue;
++i;
to increment variablesYou could also use the pattern, this pattern is more gas efficient;
for(uint i = 0; i < N; ) { // CODE unchecked { i++; } }
Recommendation
--- a/contracts-full/ThreePoolAssetManager.sol +++ b/contracts-full/ThreePoolAssetManager.sol @@ -247,11 +247,11 @@ contract ThreePoolAssetManager is Multicall, Mutex, IERC20TokenReceiver { convexRewards = params.convexRewards; convexPoolId = params.convexPoolId; - for (uint256 i = 0; i < NUM_STABLE_COINS; i++) { + for (uint256 i = 0; i < NUM_STABLE_COINS; ++i) { _threePoolAssetCache[i] = params.threePool.coins(i); } - for (uint256 i = 0; i < NUM_META_COINS; i++) { + for (uint256 i = 0; i < NUM_META_COINS; ++i) { _metaPoolAssetCache[i] = params.metaPool.coins(i); } @@ -350,7 +350,7 @@ contract ThreePoolAssetManager is Multicall, Mutex, IERC20TokenReceiver { uint256 previousBalance; - for (uint256 i = 0; i < 256; i++) { + for (uint256 i = 0; i < 256; ++i) { uint256 examineBalance; if ((examineBalance = (v.maximum + v.minimum) / 2) == previousBalance) break; @@ -770,7 +770,7 @@ contract ThreePoolAssetManager is Multicall, Mutex, IERC20TokenReceiver { uint256 threePoolDecimals = SafeERC20.expectDecimals(address(threePoolToken)); uint256 normalizedTotal = 0; - for (uint256 i = 0; i < NUM_STABLE_COINS; i++) { + for (uint256 i = 0; i < NUM_STABLE_COINS; ++i) { if (amounts[i] == 0) continue; uint256 tokenDecimals = SafeERC20.expectDecimals(address(tokens[i])); @@ -899,7 +899,7 @@ contract ThreePoolAssetManager is Multicall, Mutex, IERC20TokenReceiver { IERC20[NUM_META_COINS] memory tokens = _metaPoolAssetCache; uint256 total = 0; - for (uint256 i = 0; i < NUM_META_COINS; i++) { + for (uint256 i = 0; i < NUM_META_COINS; ++i) { if (amounts[i] == 0) continue; total += amounts[i];
Recommendation
--- a/contracts-hardhat/StakingPools.sol +++ b/contracts-hardhat/StakingPools.sol @@ -185,7 +185,8 @@ contract StakingPools is IStakingPools, ReentrancyGuard { _updatePools(); uint256 _totalRewardWeight = _ctx.totalRewardWeight; - for (uint256 _poolId = 0; _poolId < _pools.length(); _poolId++) { + uint256 _length = _pools.length(); + for (uint256 _poolId = 0; _poolId < _length; _poolId++) { Pool.Data storage _pool = _pools.get(_poolId); uint256 _currentRewardWeight = _pool.rewardWeight; @@ -360,7 +361,8 @@ contract StakingPools is IStakingPools, ReentrancyGuard { /// @dev Updates all of the pools. function _updatePools() internal { - for (uint256 _poolId = 0; _poolId < _pools.length(); _poolId++) { + uint256 _length = _pools.length(); + for (uint256 _poolId = 0; _poolId < _length; _poolId++) { Pool.Data storage _pool = _pools.get(_poolId); _pool.update(_ctx); }
++i;
to increment variablesYou could also use the pattern, this pattern is more gas efficient;
for(uint i = 0; i < N; ) { // CODE unchecked { i++; } }
Recommendation
--- a/contracts-hardhat/StakingPools.sol +++ b/contracts-hardhat/StakingPools.sol @@ -185,7 +185,7 @@ contract StakingPools is IStakingPools, ReentrancyGuard { _updatePools(); uint256 _totalRewardWeight = _ctx.totalRewardWeight; - for (uint256 _poolId = 0; _poolId < _pools.length(); _poolId++) { + for (uint256 _poolId = 0; _poolId < _pools.length(); ++_poolId) { Pool.Data storage _pool = _pools.get(_poolId); uint256 _currentRewardWeight = _pool.rewardWeight; @@ -360,7 +360,7 @@ contract StakingPools is IStakingPools, ReentrancyGuard { /// @dev Updates all of the pools. function _updatePools() internal { - for (uint256 _poolId = 0; _poolId < _pools.length(); _poolId++) { + for (uint256 _poolId = 0; _poolId < _pools.length(); ++_poolId) { Pool.Data storage _pool = _pools.get(_poolId); _pool.update(_ctx); }