Platform: Code4rena
Start Date: 14/10/2021
Pot Size: $50,000 USDC
Total HM: 3
Participants: 14
Period: 7 days
Judge: 0xean
Total Solo HM: 3
Id: 37
League: ETH
Rank: 4/14
Findings: 2
Award: $2,662.87
🌟 Selected for report: 3
🚀 Solo Findings: 0
🌟 Selected for report: pants
2272.7273 USDC - $2,272.73
pants
These files has open TODOs:
TempusPool.sol
unusedAMMImportOnly.sol
Fixed256xVar.sol
LidoTempusPool.sol
Open TODOs can hint at programming or architectural errors that still need to be fixed.
Manual code review.
Resolve these TODOs and bubble up the errors.
#0 - RedFox20
2021-10-21T20:13:55Z
Relatively low impact, but a good idea to remove these stale TODO comments which were mostly solved already Fixed in https://github.com/tempus-finance/tempus-protocol/pull/378
71.6561 USDC - $71.66
pants
There are many for loops that follows this for-each pattern:
for (uint256 i = 0; i < array.length; i++) { // do something with `array[i]` }
In such for loops, the array.length
is read on every iteration, instead of caching it once in a local variable and read it again using the local variable.
Memory reads are a bit more expensive than reading local variables.
Manual code review.
Read these values from memory once, cache them in local variables and then read them again using the local variables. For example:
uint256 length = array.length; for (uint256 i = 0; i < length; i++) { // do something with `array[i]` }
#0 - mijovic
2021-10-21T10:24:17Z
Duplicate of https://github.com/code-423n4/2021-10-tempus-findings/issues/31 However, same comment here. It saves very little gas as this is in our case run on 2 elements (and a maximum of 6 in theory if someone inherits from TempusAMM). Gas saving is so little, we don't plan to fix it.
🌟 Selected for report: pants
159.2357 USDC - $159.24
pants
These functions use prefix increaments (x++
) instead of postfix increaments (++x
):
TempusAMM.getSwapAmountToEndWithEqualShares()
ComptrollerMock.enterMarkets()
ComptrollerMock.exitMarket()
Prefix increaments are cheaper than postfix increaments.
Manual code review.
Change all prefix increaments to postfix increaments where it doesn't affects the functionality.
#0 - RedFox20
2021-10-21T20:02:55Z
Indeed, this is the case, however it would be good if actual gas usage was checked by the report.
i++ gas used: 95 ++i gas used: 87
It uses 8 gas less, so it's a very small difference, also it is only relevant for one single function TempusAMM.getSwapAmountToEndWithEqualShares
, so the impact is extremely low.
🌟 Selected for report: pants
159.2357 USDC - $159.24
pants
These public
functions are never called by their contract:
TempusAMM.getSwapAmountToEndWithEqualShares()
TempusAMM.getRate()
AaveTempusPool.currentInterestRate()
AaveTempusPool.numAssetsPerYieldToken()
AaveTempusPool.numYieldTokensPerAsset()
CompoundTempusPool.currentInterestRate()
CompoundTempusPool.numAssetsPerYieldToken()
CompoundTempusPool.numYieldTokensPerAsset()
LidoTempusPool.currentInterestRate()
LidoTempusPool.numAssetsPerYieldToken()
LidoTempusPool.numYieldTokensPerAsset()
ERC20FixedSupply.decimals()
ERC20OwnerMintableToken.burn()
ERC20OwnerMintableToken.burnFrom()
PoolShare.decimals()
PermanentlyOwnable.renounceOwnership()
TempusController.depositYieldBearing()
TempusController.depositBacking()
TempusController.redeemToYieldBearing()
TempusController.redeemToBacking()
TempusPool.estimatedMintedShares()
TempusPool.estimatedRedeem()
Therefore, their visibility can be reduced to external
.
external
functions are cheaper than public
functions.
https://gus-tavo-guim.medium.com/public-vs-external-functions-in-solidity-b46bcf0ba3ac
Manual code review.
Define these functions as external
.
#0 - RedFox20
2021-10-21T18:08:15Z
Went through the list to verify, only 10 / 22 could be changed from public
to external
with a tiny improvement to function call costs. Please run npx hardhat compile
with your changes before suggesting a list. I think this is low impact, but it can be added.
TempusAMM.getSwapAmountToEndWithEqualShares
✅
TempusAMM.getRate
✅
ERC20OwnerMintableToken.burn()
✅
ERC20OwnerMintableToken.burnFrom()
✅
TempusController.depositYieldBearing()
✅
TempusController.depositBacking()
✅
TempusController.redeemToYieldBearing()
✅
TempusController.redeemToBacking()
✅
TempusPool.estimatedMintedShares()
✅
TempusPool.estimatedRedeem()
✅
AaveTempusPool.currentInterestRate() AaveTempusPool.numAssetsPerYieldToken() AaveTempusPool.numYieldTokensPerAsset() CompoundTempusPool.currentInterestRate() CompoundTempusPool.numAssetsPerYieldToken() CompoundTempusPool.numYieldTokensPerAsset() LidoTempusPool.currentInterestRate() LidoTempusPool.numAssetsPerYieldToken() LidoTempusPool.numYieldTokensPerAsset()
â›” These are all used internally and externally, thus public
is the best option for them.
ERC20FixedSupply.decimals()
â›” because OpenZeppelin's ERC20 decimals() is public
PoolShare.decimals()
â›” also ERC20 override
PermanentlyOwnable.renounceOwnership()
â›” this was removed from another report
#1 - RedFox20
2021-10-21T18:08:40Z