Platform: Code4rena
Start Date: 25/11/2021
Pot Size: $80,000 USDC
Total HM: 35
Participants: 32
Period: 7 days
Judge: GalloDaSballo
Total Solo HM: 27
Id: 59
League: ETH
Rank: 21/32
Findings: 2
Award: $562.83
🌟 Selected for report: 5
🚀 Solo Findings: 0
🌟 Selected for report: WatchPug
Also found by: GiveMeTestEther, Meta0xNull, robee, ye0lde
robee
The following require messages are of length more than 32 and we think are short enough to short them into exactly 32 characters such that it will be placed in one slot of memory and the require function will cost less gas. The list:
Solidity file: Auction.sol, In line 223, Require message length to shorten: 40 Solidity file: Auction.sol, In line 230, Require message length to shorten: 39 Solidity file: Auction.sol, In line 659, Require message length to shorten: 37 Solidity file: AuctionEscapeHatch.sol, In line 177, Require message length to shorten: 38 Solidity file: AuctionParticipant.sol, In line 136, Require message length to shorten: 37 Solidity file: UniswapV2Library.sol, In line 12, Require message length to shorten: 37 Solidity file: UniswapV2Library.sol, In line 37, Require message length to shorten: 37 Solidity file: UniswapV2Library.sol, In line 38, Require message length to shorten: 40 Solidity file: UniswapV2Library.sol, In line 45, Require message length to shorten: 40 Solidity file: UniswapV2Library.sol, In line 55, Require message length to shorten: 40 Solidity file: Malt.sol, In line 65, Require message length to shorten: 40 Solidity file: MovingAverage.sol, In line 412, Require message length to shorten: 34 Solidity file: RewardDistributor.sol, In line 154, Require message length to shorten: 33 Solidity file: RewardDistributor.sol, In line 295, Require message length to shorten: 33 Solidity file: RewardDistributor.sol, In line 304, Require message length to shorten: 34 Solidity file: RewardDistributor.sol, In line 313, Require message length to shorten: 33 Solidity file: RewardDistributor.sol, In line 321, Require message length to shorten: 36 Solidity file: RewardOverflowPool.sol, In line 77, Require message length to shorten: 37
#0 - 0xScotch
2021-12-09T22:21:57Z
#317
#1 - GalloDaSballo
2021-12-27T22:15:14Z
Duplicate of #317
robee
Some tokens (like USDT) do not work when changing the allowance from an existing non-zero allowance value.They must first be approved by zero and then the actual allowance must be approved. You don't first approve 0 in the following places in the codebase:
approve without approving 0 first AuctionParticipant.sol, 58, auctionRewardToken.approve(address(auction), balance); approve without approving 0 first UniswapHandler.sol, 141, rewardToken.approve(address(router), rewardBalance); approve without approving 0 first UniswapHandler.sol, 166, malt.approve(address(router), maltBalance); approve without approving 0 first UniswapHandler.sol, 196, rewardToken.approve(address(router), rewardBalance); approve without approving 0 first UniswapHandler.sol, 197, malt.approve(address(router), maltBalance); approve without approving 0 first UniswapHandler.sol, 227, lpToken.approve(address(router), liquidityBalance); approve without approving 0 first ERC20Permit.sol, 57, _approve(target, spender, value); approve without approving 0 first ERC20Permit.sol, 111, function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool) { approve without approving 0 first ERC20Permit.sol, 112, _approve(msg.sender, spender, value); approve without approving 0 first IBurnMintableERC20.sol, 55, function approve(address spender, uint256 amount) external returns (bool); approve without approving 0 first IMalt.sol, 6, function approve(address spender, uint256 amount) external returns (bool); approve without approving 0 first SafeBurnMintableERC20.sol, 36, function safeApprove(IBurnMintableERC20 token, address spender, uint256 value) internal { approve without approving 0 first SafeBurnMintableERC20.sol, 37, safeApprove should only be called when setting an initial allowance, approve without approving 0 first SafeBurnMintableERC20.sol, 42, "SafeERC20: approve from non-zero to non-zero allowance" approve without approving 0 first SafeBurnMintableERC20.sol, 44, _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); approve without approving 0 first SafeBurnMintableERC20.sol, 49, _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); approve without approving 0 first SafeBurnMintableERC20.sol, 54, _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); approve without approving 0 first RewardReinvestor.sol, 106, stakeToken.approve(address(bonding), liquidityCreated); approve without approving 0 first StabilizerNode.sol, 251, rewardToken.approve(address(auction), rewarded);
#0 - 0xScotch
2021-12-08T16:22:13Z
#41
#1 - GalloDaSballo
2022-01-18T14:50:13Z
Duplicate of #41
🌟 Selected for report: pmerkleplant
Also found by: 0x0x0x, GiveMeTestEther, WatchPug, pauliax, robee, ye0lde
robee
There are places in the code (especially in for-each loops) that loads the same array element more than once. In such cases, only one array boundaries check should take place, and the rest are unnecessary. Therefore, this array element should be cached in a local variable and then be loaded again using this local variable, skipping the redundent second array boundaries check:
Auction.sol, variable name: epochCommitments times: 5 at: getAccountCommitments UniswapHandler.sol, variable name: buyers times: 2 at: removeBuyer UniswapV2Library.sol, variable name: path times: 2 at: getAmountsOut UniswapV2Library.sol, variable name: amounts times: 2 at: getAmountsOut UniswapV2Library.sol, variable name: path times: 2 at: getAmountsIn UniswapV2Library.sol, variable name: amounts times: 2 at: getAmountsIn MiningService.sol, variable name: mines times: 2 at: removeRewardMine TransferService.sol, variable name: verifierList times: 2 at: removeVerifier
#0 - 0xScotch
2021-12-08T18:39:02Z
#106
15.2616 USDC - $15.26
robee
The following functions could be set external to save gas and improve code quality. External call cost is less expensive than of public functions.
The function getRewardOwnershipFraction in AbstractRewardMine.sol could be set external The function balanceOfStakePadding in AbstractRewardMine.sol could be set external The function withdrawnBalance in AbstractRewardMine.sol could be set external The function earned in AbstractRewardMine.sol could be set external The function setMiningService in AbstractRewardMine.sol could be set external The function totalReleasedReward in AbstractRewardMine.sol could be set external The function totalStakePadding in AbstractRewardMine.sol could be set external The function totalBonded in AbstractRewardMine.sol could be set external The function totalWithdrawn in AbstractRewardMine.sol could be set external The function setRewardToken in AbstractRewardMine.sol could be set external The function onBond in AbstractRewardMine.sol could be set external The function withdrawAll in AbstractRewardMine.sol could be set external The function onUnbond in AbstractRewardMine.sol could be set external The function balanceOfBonded in AbstractRewardMine.sol could be set external The function verifyTransfer in AbstractTransferVerification.sol could be set external The function getAuctionCore in Auction.sol could be set external The function getAuctionPrices in Auction.sol could be set external The function getAuction in Auction.sol could be set external The function auctionActive in Auction.sol could be set external The function getAuctionCommitments in Auction.sol could be set external The function isAuctionFinalized in Auction.sol could be set external The function balanceOfArbTokens in Auction.sol could be set external The function auctionExists in Auction.sol could be set external The function consult in AuctionBurnReserveSkew.sol could be set external The function getAverageParticipation in AuctionBurnReserveSkew.sol could be set external The function addAbovePegObservation in AuctionBurnReserveSkew.sol could be set external The function getPegDeltaFrequency in AuctionBurnReserveSkew.sol could be set external The function addBelowPegObservation in AuctionBurnReserveSkew.sol could be set external The function getRealBurnBudget in AuctionBurnReserveSkew.sol could be set external The function earlyExitReturn in AuctionEscapeHatch.sol could be set external The function setReplenishingIndex in AuctionParticipant.sol could be set external The function usableBalance in AuctionParticipant.sol could be set external The function outstandingArbTokens in AuctionParticipant.sol could be set external The function getAllAuctionIds in AuctionParticipant.sol could be set external The function setupParticipant in AuctionParticipant.sol could be set external The function usableBalance in AuctionPool.sol could be set external The function setBonding in AuctionPool.sol could be set external The function totalReleasedReward in AuctionPool.sol could be set external The function totalBonded in AuctionPool.sol could be set external The function setForfeitDestination in AuctionPool.sol could be set external The function onUnbond in AuctionPool.sol could be set external The function totalDeclaredReward in AuctionPool.sol could be set external The function balanceOfBonded in AuctionPool.sol could be set external The function setMiningService in Bonding.sol could be set external The function epochData in Bonding.sol could be set external The function totalBonded in Bonding.sol could be set external The function bondedEpoch in Bonding.sol could be set external The function setDAO in Bonding.sol could be set external The function setDexHandler in Bonding.sol could be set external The function setCurrentEpoch in Bonding.sol could be set external The function bondToAccount in Bonding.sol could be set external The function averageBondedValue in Bonding.sol could be set external The function balanceOfBonded in Bonding.sol could be set external The function deploy in Create2Deployer.sol could be set external The function getEpochStartTime in DAO.sol could be set external The function epochsPerYear in DAO.sol could be set external The function mint in DAO.sol could be set external The function setEpochLength in DAO.sol could be set external The function setMaltToken in DAO.sol could be set external The function maltMarketPrice in UniswapHandler.sol could be set external The function reserves in UniswapHandler.sol could be set external The function constructor in ERC20Permit.sol could be set external The function setBonding in ERC20VestedMine.sol could be set external The function totalReleasedReward in ERC20VestedMine.sol could be set external The function totalBonded in ERC20VestedMine.sol could be set external The function setDistributor in ERC20VestedMine.sol could be set external The function onUnbond in ERC20VestedMine.sol could be set external The function totalDeclaredReward in ERC20VestedMine.sol could be set external The function balanceOfBonded in ERC20VestedMine.sol could be set external The function handleForfeit in ForfeitHandler.sol could be set external The function totalUsefulCollateral in ImpliedCollateralService.sol could be set external The function getCollateralValueInMalt in ImpliedCollateralService.sol could be set external The function collateralDeficit in LiquidityExtension.sol could be set external The function reserveRatio in LiquidityExtension.sol could be set external The function hasMinimumReserves in LiquidityExtension.sol could be set external The function constructor in Malt.sol could be set external The function burn in Malt.sol could be set external The function mint in Malt.sol could be set external The function smoothedMaltPrice in MaltDataLab.sol could be set external The function trackReserveRatio in MaltDataLab.sol could be set external The function maltInPoolAverage in MaltDataLab.sol could be set external The function smoothedMaltInPool in MaltDataLab.sol could be set external The function reserveRatioAverage in MaltDataLab.sol could be set external The function maltPriceAverage in MaltDataLab.sol could be set external The function smoothedReserves in MaltDataLab.sol could be set external The function smoothedReserveRatio in MaltDataLab.sol could be set external The function balanceOfRewards in MiningService.sol could be set external The function removeRewardMine in MiningService.sol could be set external The function earned in MiningService.sol could be set external The function withdrawRewardsForAccount in MiningService.sol could be set external The function setReinvestor in MiningService.sol could be set external The function withdrawAccountRewards in MiningService.sol could be set external The function setBonding in MiningService.sol could be set external The function isMineActive in MiningService.sol could be set external The function onBond in MiningService.sol could be set external The function numberOfMines in MiningService.sol could be set external The function onUnbond in MiningService.sol could be set external The function addRewardMine in MiningService.sol could be set external The function getValue in MovingAverage.sol could be set external The function getValueWithLookback in MovingAverage.sol could be set external The function isWhitelisted in PoolTransferVerification.sol could be set external The function setPool in PoolTransferVerification.sol could be set external The function addToWhitelist in PoolTransferVerification.sol could be set external The function verifyTransfer in PoolTransferVerification.sol could be set external The function setThreshold in PoolTransferVerification.sol could be set external The function setPriceLookback in PoolTransferVerification.sol could be set external The function removeFromWhitelist in PoolTransferVerification.sol could be set external The function setForfeitor in RewardDistributor.sol could be set external The function addFocalLengthUpdater in RewardDistributor.sol could be set external The function setRewardMine in RewardDistributor.sol could be set external The function setBonding in RewardDistributor.sol could be set external The function forfeit in RewardDistributor.sol could be set external The function vest in RewardDistributor.sol could be set external The function setThrottler in RewardDistributor.sol could be set external The function setRewardToken in RewardDistributor.sol could be set external The function removeFocalLengthUpdater in RewardDistributor.sol could be set external The function decrementRewards in RewardDistributor.sol could be set external The function totalDeclaredReward in RewardDistributor.sol could be set external The function setFocalLength in RewardDistributor.sol could be set external The function averageAPR in RewardThrottle.sol could be set external The function targetEpochProfit in RewardThrottle.sol could be set external The function epochData in RewardThrottle.sol could be set external The function targetAPR in RewardThrottle.sol could be set external The function getTargets in RewardThrottle.sol could be set external The function handleReward in RewardThrottle.sol could be set external The function checkRewardUnderflow in RewardThrottle.sol could be set external The function epochAPR in RewardThrottle.sol could be set external The function costBasis in SwingTrader.sol could be set external The function setLpProfitCut in SwingTrader.sol could be set external The function addVerifier in TransferService.sol could be set external The function numberOfVerifiers in TransferService.sol could be set external The function verifyTransfer in TransferService.sol could be set external The function removeVerifier in TransferService.sol could be set external
10.3016 USDC - $10.30
robee
The following structs could change the order of their stored elements to decrease memory uses. and number of occupied slots. Therefore will save gas at every store and load from memory.
In Auction.sol, AuctionData{
is optimized to: 16 slots from: 17 slots. The new order of types (you choose the actual variables):
#0 - 0xScotch
2021-12-09T22:09:09Z
#38
#1 - GalloDaSballo
2021-12-31T16:55:11Z
Duplicate of #38
🌟 Selected for report: robee
Also found by: GiveMeTestEther, WatchPug, hyh, ye0lde
7.4171 USDC - $7.42
robee
Reading a storage variable is gas costly (SLOAD). In cases of multiple read of a storage variable in the same scope, caching the first read (i.e saving as a local variable) can save gas and decrease the overall gas uses. The following is a list of functions and the storage variables that you read twice:
AbstractRewardMine.sol Variable miningService is read 3 times in the function: setMiningService Auction.sol Variable currentAuctionId is read 3 times in the function: purchaseArbitrageTokens Auction.sol Variable nextCommitmentId is read 2 times in the function: purchaseArbitrageTokens Auction.sol Variable currentAuctionId is read 4 times in the function: _checkAuctionFinalization Auction.sol Variable replenishingAuctionId is read 4 times in the function: allocateArbRewards Auction.sol Variable stabilizerNode is read 2 times in the function: setStabilizerNode Auction.sol Variable amender is read 2 times in the function: setAuctionAmender AuctionBurnReserveSkew.sol Variable auctionAverageLookback is read 2 times in the function: getPegDeltaFrequency AuctionBurnReserveSkew.sol Variable count is read 2 times in the function: addAbovePegObservation AuctionBurnReserveSkew.sol Variable count is read 2 times in the function: addBelowPegObservation AuctionBurnReserveSkew.sol Variable stabilizerNode is read 2 times in the function: setNewStabilizerNode AuctionParticipant.sol Variable replenishingIndex is read 2 times in the function: claim AuctionParticipant.sol Variable replenishingIndex is read 2 times in the function: setReplenishingIndex AuctionPool.sol Variable forfeitedRewards is read 2 times in the function: _checkForForfeit AuctionPool.sol Variable forfeitedRewards is read 3 times in the function: _handleRewardDistribution UniswapHandler.sol Variable router is read 2 times in the function: addLiquidity MaltDataLab.sol Variable UPDATER_ROLE is read 2 times in the function: initialize MiningService.sol Variable reinvestor is read 2 times in the function: setReinvestor MiningService.sol Variable bonding is read 2 times in the function: setBonding MovingAverage.sol Variable UPDATER_ROLE is read 2 times in the function: initialize MovingAverage.sol Variable cumulativeValue is read 11 times in the function: update MovingAverage.sol Variable blockTimestampLast is read 5 times in the function: update MovingAverage.sol Variable activeSamples is read 2 times in the function: update MovingAverage.sol Variable sampleLength is read 3 times in the function: update MovingAverage.sol Variable cumulativeValue is read 10 times in the function: updateCumulative MovingAverage.sol Variable blockTimestampLast is read 4 times in the function: updateCumulative MovingAverage.sol Variable activeSamples is read 2 times in the function: updateCumulative MovingAverage.sol Variable sampleLength is read 2 times in the function: updateCumulative MovingAverage.sol Variable activeSamples is read 2 times in the function: _createNewSample MovingAverage.sol Variable counter is read 2 times in the function: setSampleMemory RewardReinvestor.sol Variable dexHandler is read 2 times in the function: _bondAccount RewardReinvestor.sol Variable treasury is read 2 times in the function: _bondAccount RewardDistributor.sol Variable FOCAL_LENGTH_UPDATER_ROLE is read 2 times in the function: initialize RewardDistributor.sol Variable focalLength is read 2 times in the function: _resetFocalPoint RewardDistributor.sol Variable focalID is read 3 times in the function: _incrementFocalPoint RewardDistributor.sol Variable throttler is read 2 times in the function: setThrottler RewardDistributor.sol Variable rewardMine is read 2 times in the function: setRewardMine RewardOverflowPool.sol Variable throttler is read 2 times in the function: setThrottler RewardThrottle.sol Variable _activeEpoch is read 3 times in the function: handleReward StabilizerNode.sol Variable stabilizeWindowEnd is read 2 times in the function: stabilize StabilizerNode.sol Variable lastStabilize is read 2 times in the function: stabilize StabilizerNode.sol Variable liquidityExtension is read 2 times in the function: _replenishLiquidityExtension StabilizerNode.sol Variable auction is read 2 times in the function: setAuctionContract SwingTrader.sol Variable deployedCapital is read 2 times in the function: buyMalt SwingTrader.sol Variable deployedCapital is read 2 times in the function: sellMalt
#0 - GalloDaSballo
2021-12-28T00:34:56Z
Finding is valid, anytime you perform more than one SLOAD on the same storage variable, you can save it's value in memory and then read from the local copy to save gas
25.436 USDC - $25.44
robee
Unused local variables are gas consuming, since the initial value assignment costs gas. And are a bad code practice. Removing those variables will decrease the gas cost and improve code quality. This is a full list of all the unused storage variables we found in your code base. The format is <solidity file>, <in which function we found it>, <unused local variable name>:
AbstractRewardMine.sol, _handleStakePadding, totalRewardedWithStakePadding AbstractRewardMine.sol, _handleStakePadding, INITIAL_STAKE_SHARE_MULTIPLE AbstractRewardMine.sol, _handleStakePadding, bondedTotal Auction.sol, _finalizeAuction, avgMaltPrice AuctionParticipant.sol, claim, replenishingId AuctionParticipant.sol, claim, claimableTokens AuctionParticipant.sol, claim, claimable Create2Deployer.sol, deploy, addr UniswapHandler.sol, removeBuyer, buyer MaltDataLab.sol, trackPoolReserves, rewardDecimals MovingAverage.sol, update, elapsedSamples MovingAverage.sol, updateCumulative, elapsedSamples StabilizerNode.sol, stabilize, exchangeRate StabilizerNode.sol, _startAuction, decimals SwingTrader.sol, sellMalt, maltDecimals SwingTrader.sol, costBasis, maltDecimals TransferService.sol, removeVerifier, verifier
#0 - GalloDaSballo
2021-12-31T16:58:03Z
Finding is valid, mitigation is by removing unused variables
robee
In the following files there are state variables that could be set immutable to save gas. The list of format <solidity file>, <state variable name that could be immutable>:
AbstractRewardMine.sol, _userStakePadding AbstractRewardMine.sol, _userWithdrawn Auction.sol, collateralToken Auction.sol, malt Auction.sol, currentAuctionId Auction.sol, nextCommitmentId Auction.sol, auctionStartController Auction.sol, idToAuction Auction.sol, accountCommitmentEpochs AuctionBurnReserveSkew.sol, pegObservations AuctionEscapeHatch.sol, auction AuctionEscapeHatch.sol, collateralToken AuctionEscapeHatch.sol, malt AuctionEscapeHatch.sol, auctionEarlyExits AuctionParticipant.sol, auction AuctionParticipant.sol, auctionRewardToken AuctionParticipant.sol, auctionIds AuctionParticipant.sol, claimableRewards AuctionParticipant.sol, setupCompleted Bonding.sol, malt Bonding.sol, rewardToken Bonding.sol, stakeToken Bonding.sol, maltDataLab Bonding.sol, _globalBonded Bonding.sol, offering Bonding.sol, userState Bonding.sol, epochState DAO.sol, malt DAO.sol, epochLength DAO.sol, genesisTime UniswapHandler.sol, malt UniswapHandler.sol, rewardToken UniswapHandler.sol, lpToken UniswapHandler.sol, router UniswapHandler.sol, uniswapV2Factory UniswapHandler.sol, buyers UniswapHandler.sol, buyersActive ERC20Permit.sol, DOMAIN_SEPARATOR ERC20Permit.sol, nonces Faucet.sol, token FaucetTwo.sol, faucetContract FaucetTwo.sol, token ForfeitHandler.sol, rewardToken ForfeitHandler.sol, swingTrader ImpliedCollateralService.sol, collateralToken ImpliedCollateralService.sol, malt ImpliedCollateralService.sol, swingTrader ImpliedCollateralService.sol, liquidityExtension ImpliedCollateralService.sol, maltDataLab LiquidityExtension.sol, collateralToken LiquidityExtension.sol, malt LiquidityExtension.sol, uniswapV2Factory MaltDataLab.sol, rewardToken MaltDataLab.sol, malt MaltDataLab.sol, stakeToken MiningService.sol, mines MiningService.sol, mineActive MovingAverage.sol, defaultValue MovingAverage.sol, samples PoolTransferVerification.sol, maltDataLab PoolTransferVerification.sol, whitelist RewardReinvestor.sol, malt RewardReinvestor.sol, rewardToken RewardReinvestor.sol, stakeToken RewardDistributor.sol, _globals RewardDistributor.sol, focalPoints RewardThrottle.sol, rewardToken RewardThrottle.sol, _activeEpoch RewardThrottle.sol, _state StabilizerNode.sol, stabilizeWindowEnd StabilizerNode.sol, daoRewardCut StabilizerNode.sol, rewardToken StabilizerNode.sol, malt StabilizerNode.sol, uniswapV2Factory StabilizerNode.sol, supplyDistributionController StabilizerNode.sol, auctionStartController SwingTrader.sol, collateralToken SwingTrader.sol, malt SwingTrader.sol, dexHandler SwingTrader.sol, rewardThrottle TransferService.sol, verifierList TransferService.sol, verifiers
There are some variables that I was not sure if are assigned actually twice in real use. I added them anyway.
#0 - 0xScotch
2021-12-08T18:41:56Z
#105
#1 - GalloDaSballo
2022-01-25T01:44:27Z
After talking to the sponsor they confirmed the protocol doesn't use upgradeable proxies. As such the recommendation of using immutable to save gas is valid
🌟 Selected for report: robee
367.919 USDC - $367.92
robee
This issue is about arithmetic computation that could have been done more percise. The following are places in the codebase in which you multiplied after the divisions. Doing the multiplications at start lead to more accurate calculations. This is a list of places in the code that this appears (Solidity file, line number, actual line):
DAO.sol, 105, /* Internal methods */ UniswapHandler.sol, 265, buyBase.div(priceTarget).mul(buyBase).mul(997) RewardDistributor.sol, 113, /* PUBLIC VIEW FUNCTIONS */ RewardDistributor.sol, 118, /* INTERNAL VIEW FUNCTIONS */ RewardDistributor.sol, 129, /* INTERNAL FUNCTIONS */
#0 - GalloDaSballo
2022-01-18T14:51:48Z
Agreed that due to integer precision, you always want to multiply first and then divide