Platform: Code4rena
Start Date: 29/04/2022
Pot Size: $22,000 USDC
Total HM: 6
Participants: 40
Period: 3 days
Judge: Justin Goro
Total Solo HM: 2
Id: 114
League: ETH
Rank: 30/40
Findings: 1
Award: $31.86
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0x4non, 0xDjango, 0xf15ers, 0xkatana, 242, Dravee, GimelSec, MaratCerby, Tadashi, TrungOre, WatchPug, defsec, fatherOfBlocks, gzeon, hake, horsefacts, joestakey, miguelmtzinf, pauliax, pedroais, peritoflores, rotcivegaf, simon135, slywaters, tabish, throttle, z3s
31.859 USDC - $31.86
> 0
is less gas efficient than != 0
if you enable the optimizer at 10k AND you’re in a require statement.
Detailed explanation with the opcodes here
Instances include:
AaveV3YieldSource.sol:179: require(decimals_ > 0, "AaveV3YS/decimals-gt-zero"); AaveV3YieldSource.sol:233: require(_shares > 0, "AaveV3YS/shares-gt-zero");
Manual Analysis
Replace > 0
with != 0
Constructor parameters are expensive. The contract deployment will be cheaper in gas if they are hard coded instead of using constructor parameters.
Instances include:
AaveV3YieldSource.sol:160 IAToken _aToken AaveV3YieldSource.sol:161 IRewardsController _rewardsController AaveV3YieldSource.sol:162 IPoolAddressesProviderRegistry _poolAddressesProviderRegistry AaveV3YieldSource.sol:165 uint8 decimals_
Manual Analysis
Hardcode aToken
, rewardsController
, poolAddressesProviderRegistry
and _decimals
with their values instead of writing them during contract deployment with constructor parameters.
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met) while providing the same amount of information, as explained here
Custom errors are defined using the error statement
Instances include:
AaveV3YieldSource.sol:168: require(address(_aToken) != address(0), "AaveV3YS/aToken-not-zero-address"); AaveV3YieldSource.sol:171: require(address(_rewardsController) != address(0), "AaveV3YS/RC-not-zero-address"); AaveV3YieldSource.sol:174: require(address(_poolAddressesProviderRegistry) != address(0), "AaveV3YS/PR-not-zero-address"); AaveV3YieldSource.sol:177: require(_owner != address(0), "AaveV3YS/owner-not-zero-address"); AaveV3YieldSource.sol:179: require(decimals_ > 0, "AaveV3YS/decimals-gt-zero"); AaveV3YieldSource.sol:233: require(_shares > 0, "AaveV3YS/shares-gt-zero"); AaveV3YieldSource.sol:276: require(_to != address(0), "AaveV3YS/payee-not-zero-address"); AaveV3YieldSource.sol:337: require(address(_token) != address(aToken), "AaveV3YS/forbid-aToken-transfer"); AaveV3YieldSource.sol:349: require(_token != address(aToken), "AaveV3YS/forbid-aToken-allowance");
Manual Analysis
Replace require and revert statements with custom errors.
For instance:
Replace
require(decimals_ > 0, "AaveV3YS/decimals-gt-zero");
with
if (decimals_ == 0) { revert ErrorDecimalNull(decimals_); }
and define the custom error in the contract
error ErrorDecimalNull(uint8 decimals_);
When we define internal functions to perform computation:
When it does not affect readability, it is recommended to inline functions in order to save gas
Instances include:
AaveV3YieldSource.sol: 348: function _requireNotAToken(address _token) AaveV3YieldSource.sol: 380: function _tokenAddress()
Manual Analysis
Inline these functions where they are called:
AaveV3YieldSource.sol:183 AaveV3YieldSource.sol:212 AaveV3YieldSource.sol:235 AaveV3YieldSource.sol:252 AaveV3YieldSource.sol:301 AaveV3YieldSource.sol:320
As of Solidity 0.8.0, overflow and underflow checks are performed automatically. Using the SafeMath library methods for subtractions and additions is hence superfluous and cost additional gas upon deployment and function calls
Instances include:
AaveV3YieldSource.sol:262: uint256 _balanceDiff = _afterBalance.sub(_beforeBalance);
Manual Analysis
Replace the sub()
call with the regular -
operation
Solidity contracts have contiguous 32 bytes (256 bits) slots used in storage. By arranging the variables, it is possible to minimize the number of slots used within a contract's storage and therefore reduce deployment costs.
uint8 variables are each of 1 byte size (way less than 32 bytes). However, here it takes up a whole 32 bytes slot, as it is followed in storage by a uint256 variable.
By rearranging the storage variables, we can save one slot
Instances include:
AaveV3YieldSource.sol:136-145 uint8 private immutable _decimals; /** * @dev Aave genesis market PoolAddressesProvider's ID. * @dev This variable could evolve in the future if we decide to support other markets. */ uint256 private constant ADDRESSES_PROVIDER_ID = uint256(0); /// @dev PoolTogether's Aave Referral Code uint16 private constant REFERRAL_CODE = uint16(188);
Manual Analysis
Place REFERRAL_CODE
after _decimals
to save one storage slot
uint8 private immutable _decimals; +uint16 private constant REFERRAL_CODE = uint16(188); /** * @dev Aave genesis market PoolAddressesProvider's ID. * @dev This variable could evolve in the future if we decide to support other markets. */ uint256 private constant ADDRESSES_PROVIDER_ID = uint256(0);
#0 - PierrickGT
2022-05-03T23:42:18Z
Comparisons with zero for unsigned integers Safemath library redundant
Duplicate of https://github.com/code-423n4/2022-04-pooltogether-findings/issues/11
Constructor parameters should be avoided when possible
This parameters need to be passed to the initialized function.
Custom Errors
Duplicate of https://github.com/code-423n4/2022-04-pooltogether-findings/issues/13
Inline functions
Duplicate of https://github.com/code-423n4/2022-04-pooltogether-findings/issues/19
Tight Variable Packing
The compiler is smart enough to properly allocate memory. I didn't notice any gas saving when testing the change proposed by the warden.