Platform: Code4rena
Start Date: 10/03/2022
Pot Size: $75,000 USDT
Total HM: 25
Participants: 54
Period: 7 days
Judge: pauliax
Total Solo HM: 10
Id: 97
League: ETH
Rank: 20/54
Findings: 2
Award: $662.35
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: CertoraInc
Low-level calls call/delegatecall/staticcall return true even if the account called is non-existent (per EVM design). Account existence must be checked prior to calling if needed.
https://github.com/code-423n4/2022-03-biconomy/blob/main/contracts/hyphen/LiquidityFarming.sol#L122
#0 - pauliax
2022-05-09T12:22:24Z
🌟 Selected for report: Dravee
Also found by: 0v3rf10w, 0x1f8b, 0xDjango, 0xNazgul, 0xngndev, 0xwags, Cantor_Dust, CertoraInc, IllIllI, Jujic, Kenshin, Kiep, PPrieditis, TerrierLover, Tomio, WatchPug, antonttc, benk10, berndartmueller, bitbopper, csanuragjain, defsec, gzeon, hagrid, hickuphh3, kenta, minhquanym, oyc_109, pedroais, peritoflores, rfa, robee, saian, samruna, sirhashalot, throttle, wuwe1, z3s
102.0428 USDT - $102.04
Caching the array length first saves an SLOAD
on each iteration of the loop.
https://github.com/code-423n4/2022-03-biconomy/blob/main/contracts/hyphen/ExecutorManager.sol#L31
https://github.com/code-423n4/2022-03-biconomy/blob/main/contracts/hyphen/ExecutorManager.sol#L47
Caching the array length first saves an MLOAD
on each iteration of the loop.
https://github.com/code-423n4/2022-03-biconomy/blob/main/contracts/hyphen/token/TokenManager.sol#L78
https://github.com/code-423n4/2022-03-biconomy/blob/main/contracts/hyphen/token/LPToken.sol#L77
If a variable is not set/initialized, it is assumed to have the default value. Explicitly initializing it with its default value is an anti-pattern and wastes gas
contracts/hyphen/LiquidityPool.sol 355: fee = 0; contracts/hyphen/LiquidityFarming.sol 233: for (index = 0; index < nftsStakedLength; ++index) { 266: uint256 accumulator = 0; contracts/hyphen/WhitelistPeriodManager.sol 180: for (uint256 i = 0; i < _addresses.length; ++i) { 228: for (uint256 i = 0; i < _tokens.length; ++i) { 247: uint256 maxLp = 0; contracts/hyphen/LiquidityProviders.sol 215: lpFeeAccumulated = 0; 367: lpFeeAccumulated = 0; contracts/hyphen/ExecutorManager.sol 31: for (uint256 i = 0; i < executorArray.length; ++i) { 47: for (uint256 i = 0; i < executorArray.length; ++i) { contracts/hyphen/token/LPToken.sol 77: for (uint256 i = 0; i < nftIds.length; ++i) { contracts/hyphen/token/TokenManager.sol 78: for (uint256 index = 0; index < tokenConfig.length; ++index) { contracts/hyphen/token/svg-helpers/SvgHelperBase.sol 29: uint256 count = 0; 45: for (uint256 i = 0; i < _length; ++i) { 68: uint256 leadingZeroesToAddBeforeDecimal = 0;
contracts/hyphen/LiquidityFarming.sol 132: if (amount > 0) { 318: if (totalSharesStaked[_baseToken] > 0) { contracts/hyphen/LiquidityProviders.sol 182: if (supply > 0) { 239: require(_amount > 0, "ERR__AMOUNT_IS_0"); 283: require(_amount > 0, "ERR__AMOUNT_IS_0"); 410: require(lpFeeAccumulated > 0, "ERR__NO_REWARDS_TO_CLAIM"); contracts/hyphen/token/svg-helpers/SvgHelperBase.sol 30: while (_number > 0) { 76: if (powerRemaining > 0) {
Use calldata
instead of memory
for external functions where the function argument is read-only. This can save some MLOAD
and stack operations.
https://github.com/code-423n4/2022-03-biconomy/blob/main/contracts/hyphen/LiquidityPool.sol#L154
https://github.com/code-423n4/2022-03-biconomy/blob/main/contracts/hyphen/LiquidityPool.sol#L245
https://github.com/code-423n4/2022-03-biconomy/blob/main/contracts/hyphen/LiquidityPool.sol#L267
https://github.com/code-423n4/2022-03-biconomy/blob/main/contracts/hyphen/LiquidityPool.sol#L365
Removing unused named return variables can reduce gas usage (MSTOREs/MLOADs) and improve code clarity. To save gas and improve code quality: consider using only one of those.
contracts/hyphen/token/TokenManager.sol 139: function _msgSender() internal view virtual override(Context, ERC2771Context) returns (address sender) { contracts/hyphen/LiquidityFarming.sol 351: returns (address sender) contracts/hyphen/LiquidityFarming.sol 351: returns (address sender) contracts/hyphen/metatx/ERC2771ContextUpgradeable.sol 29: function _msgSender() internal view virtual override returns (address sender) {
Checking non-zero transfer values can avoid an external call to save gas.
consider add require(msg.value>0)
contracts/hyphen/token/TokenManager.sol:53
function changeFee( address tokenAddress, uint256 _equilibriumFee, uint256 _maxFee ) external override onlyOwner whenNotPaused { require(_equilibriumFee != 0, "Equilibrium Fee cannot be 0"); require(_maxFee != 0, "Max Fee cannot be 0"); tokensInfo[tokenAddress].equilibriumFee = _equilibriumFee; tokensInfo[tokenAddress].maxFee = _maxFee; emit FeeChanged(tokenAddress, tokensInfo[tokenAddress].equilibriumFee, tokensInfo[tokenAddress].maxFee); }
can be changed to
emit FeeChanged(tokenAddress, _equilibriumFee, _maxFee);
contracts/hyphen/token/TokenManager.sol:79
depositConfig[toChainId[index]][tokenAddresses[index]].min = tokenConfig[index].min; depositConfig[toChainId[index]][tokenAddresses[index]].max = tokenConfig[index].max;
can be changed to
address token = tokenAddress[index]; uint256 chainId = toChainId[index]; depositConfig[chainId][token].min = tokenConfig[index].min; depositConfig[chainId][token].max = tokenConfig[index].max;
contracts/hyphen/LiquidityProviders.sol 183: return totalSharesMinted[_baseToken] / totalReserve[_baseToken];
consider change to
return supply / totalReserve[_baseToken];
The only place this field is used is in contracts/hyphen/token/TokenManager.sol:96
tokensInfo[tokenAddress].tokenConfig = transferConfig[tokenAddress];
Consider remove this field to save a SSTORE
// Handle edge cases where eligibleLiquidity is less than what was supplied by very small amount if(nftSuppliedLiquidity > eligibleLiquidity) { lpFeeAccumulated = 0; } else { unchecked { lpFeeAccumulated = eligibleLiquidity - nftSuppliedLiquidity; } }
Consider change to
if(eligibleLiquidity >= nftSuppliedLiquidity) { unchecked { lpFeeAccumulated = eligibleLiquidity - nftSuppliedLiquidity; } }
to avoid calculation when eligibleLiquidity==nftSuppliedLiquidity
contracts/hyphen/LiquidityPool.sol 157: tokenManager.getDepositConfig(toChainId, tokenAddress).min <= amount && 158: tokenManager.getDepositConfig(toChainId, tokenAddress).max >= amount, 248: tokenManager.getDepositConfig(toChainId, NATIVE).min <= msg.value && 249: tokenManager.getDepositConfig(toChainId, NATIVE).max >= msg.value,
Consider cache the result using stack variable to save SLOAD
removeLiquidity
of LiquidityProviders
invokes getTokenPriceInLPShares
twice. Consider cache the result on a stack variable.
function getAmountToTransfer( uint256 initialGas, address tokenAddress, uint256 amount, uint256 tokenGasPrice ) internal returns (uint256 amountToTransfer) { uint256 transferFeePerc = getTransferFee(tokenAddress, amount); uint256 lpFee; if (transferFeePerc > tokenManager.getTokensInfo(tokenAddress).equilibriumFee) { //@<- 1 // Here add some fee to incentive pool also lpFee = (amount * tokenManager.getTokensInfo(tokenAddress).equilibriumFee) / BASE_DIVISOR; //@<- 2 incentivePool[tokenAddress] = (incentivePool[tokenAddress] + // @vvvvvvvvvv 3 (amount * (transferFeePerc - tokenManager.getTokensInfo(tokenAddress).equilibriumFee))) / BASE_DIVISOR; } else { lpFee = (amount * transferFeePerc) / BASE_DIVISOR; } uint256 transferFeeAmount = (amount * transferFeePerc) / BASE_DIVISOR; liquidityProviders.addLPFee(tokenAddress, lpFee); uint256 totalGasUsed = initialGas - gasleft(); // @vvvvv 4 totalGasUsed = totalGasUsed + tokenManager.getTokensInfo(tokenAddress).transferOverhead; totalGasUsed = totalGasUsed + baseGas; uint256 gasFee = totalGasUsed * tokenGasPrice; gasFeeAccumulatedByToken[tokenAddress] = gasFeeAccumulatedByToken[tokenAddress] + gasFee; gasFeeAccumulated[tokenAddress][_msgSender()] = gasFeeAccumulated[tokenAddress][_msgSender()] + gasFee; amountToTransfer = amount - (transferFeeAmount + gasFee); emit FeeDetails(lpFee, transferFeeAmount, gasFee); }
getAmountToTransfer
calls getTokensInfo
4 times.
Consider cache the used field into stack variable.
contracts/hyphen/LiquidityPool.sol 336: gasFeeAccumulated[tokenAddress][_msgSender()] = gasFeeAccumulated[tokenAddress][_msgSender()] + gasFee;
Consider cache _msgSender
using stack variable when invoke more than once.