Platform: Code4rena
Start Date: 27/01/2022
Pot Size: $90,000 USDC
Total HM: 21
Participants: 33
Period: 7 days
Judge: Jack the Pug
Total Solo HM: 14
Id: 78
League: ETH
Rank: 32/33
Findings: 1
Award: $8.37
🌟 Selected for report: 0
🚀 Solo Findings: 0
8.369 USDC - $8.37
Jujic
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition has been met. ##Proof of Concept
require(morgothApprover.approved(token), "MORGOTH: token not approved for listing on Behodler")
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/ProposalFactory.sol#L34 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/Flan.sol#L96-L97
Shorten the revert strings to fit in 32 bytes.
!= 0 is a cheaper operation compared to > 0, when dealing with uint.
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/FlashGovernanceArbiter.sol#L145 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/FlashGovernanceArbiter.sol#L168-L169 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/LimboDAO.sol#L240 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/LimboDAO.sol#L280 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/LimboDAO.sol#L287 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/LimboDAO.sol#L357 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/LimboDAO.sol#L393 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/Limbo.sol#L326 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/Limbo.sol#L387-L388 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/Limbo.sol#L457 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/Limbo.sol#L460 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/Limbo.sol#L514 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/Limbo.sol#L532
require(actualEyeBalance > 0, "LimboDAO: No EYE");
Remix
Replace > 0 with !=
for (uint256 i = 0; i < sushiLPs.length; i++)
Remix
Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack. Caching the array length in the stack saves around 3 gas per iteration.
function execute() internal override returns (bool) { for (uint256 i = 0; i < params.length; i++) {
Remix
Caching par = params.length
and using the len instead will save gas.
For the arithmetic operations that will never over/underflow, using the unchecked directive (Solidity v0.8 has default overflow/underflow checks) can save some gas from the unnecessary internal over/underflow checks.
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/FlashGovernanceArbiter.sol#L74 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/LimboDAO.sol#L181 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/LimboDAO.sol#L284 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/LimboDAO.sol#L422-L427 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/Flan.sol#L107 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/Limbo.sol#L225 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/Limbo.sol#L327 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/UniswapHelper.sol#L232
function timeRemainingOnProposal() public view returns (uint256) { require(currentProposalState.decision == ProposalDecision.voting, "LimboDAO: proposal finished."); uint256 elapsed = block.timestamp - currentProposalState.start; if (elapsed > proposalConfig.votingDuration) return 0; return proposalConfig.votingDuration - elapsed; }
Remix
Consider using 'unchecked' where it is safe to do so. Example:
unchecked { amt = amount - feeAmt; }
Contracts FlashGovernanceArbiter.sol
and FlanBackstop.sol
makes no use of these imports:
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/FlashGovernanceArbiter.sol#L4 https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/FlanBackstop.sol#L8
import "hardhat/console.sol";
Remix
Consider reviewing all the unused imports and removing them to reduce the size of the contract and thus save some deployment gas.
burnOnTransferFee
. It does not give any efficiency, actually, it is the opposite as EVM operates on default of 256-bit values so uint8 is more expensive in this case as it needs a conversion. It only gives improvements in cases where you can pack variables together, e.g. structs.uint8 public burnOnTransferFee = 0;
Remix
In the ProposalFactory
, declaring the description
with type bytes32 can save gas.
string public description;
https://medium.com/layerx/how-to-reduce-gas-cost-in-solidity-f2e5321e0395#2a78 ##Recommended Mitigation Steps Change to:
bytes32 public description;
Having an array in memory, there is an unnecessary copy from calldata to memory. In the proposed patch, this unnecessary copy is avoided and values are directly read from calldata by using calldataload(...) instead of going via calldatacopy(...), then mload(...)). Saves memory expansion cost, and cost of copying from calldata.
function seed( address limbo, address flan, address eye, address proposalFactory, address sushiFactory, address uniFactory, address flashGoverner, uint256 precisionOrderOfMagnitude, address[] memory sushiLPs, address[] memory uniLPs ) public onlyOwner {
Remix
Change to:
address[] calldata sushiLPs, address[] calldata uniLPs
SecurityParameters
struct in FlashGovernanceArbiter.sol
can be optimized to reduce 2 storage slot
##Proof of Concept
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/FlashGovernanceArbiter.sol#L36-L41
The same in:
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/UniswapHelper.sol#L22-L33
struct SecurityParameters { uint8 maxGovernanceChangePerEpoch; //prevents flash governance from wrecking the incentives. uint256 epochSize; //only one flash governance action can happen per epoch to prevent governance DOS uint256 lastFlashGovernanceAct; uint8 changeTolerance; //1-100 maximum percentage any numeric variable can be changed through flash gov }
https://docs.soliditylang.org/en/v0.8.0/internals/layout_in_storage.html?highlight=Structs#layout-of-state-variables-in-storage ##Recommended Mitigation Steps
struct SecurityParameters { uint128 maxGovernanceChangePerEpoch; //prevents flash governance from wrecking the incentives. uint128 changeTolerance; //1-100 maximum percentage any numeric variable can be changed through flash gov uint256 epochSize; //only one flash governance action can happen per epoch to prevent governance DOS uint256 lastFlashGovernanceAct; }
Change the struct as suggested above.
#11. Gas optimization 2**256 - 1
when using 2**256 - 1 it takes more gas, than using type(uint256).max
IERC20(flan).approve(pyroFlan, 2**256 - 1);
Remix
Use type(uint256).max
Some of the variable can be cached to slightly reduce gas usage.
flashGovernanceConfig.asset
can be cached.
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/FlashGovernanceArbiter.sol#L66
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/FlashGovernanceArbiter.sol#L77
eye
can be cached.
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/LimboDAO.sol#L214
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/LimboDAO.sol#L219
domainConfig.eye
can be cached.
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/DAO/LimboDAO.sol#L383-L401
config.flan
and config.pyroFlan
can be cached.
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/FlanBackstop.sol#L75-L114
crossingConfig.behodler
can be cached.
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/Limbo.sol#L203-L241
flashGoverner
can be cahed.
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/Limbo.sol#L380-L386
VARS.DAI
can be cahed.
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/UniswapHelper.sol#L147-L157
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/UniswapHelper.sol#L217-L235
VARS.behodler
, VARS.Flan_SCX_tokenPair
and VARS.flan
can be cahed.
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/UniswapHelper.sol#L162-L197
Remix
Consider caching those variable for read and make sure write back to storage.
when using 1 days * 365 it takes more gas, than using 365 days.
uint256 constant year = (1 days * 365);
Remix
Use uint256 constant year = 365 days;
#13. Gas optimization for normalize()
function
##Impact
It is possible save some gas in normalize()
function.
##Proof of Concept
https://github.com/code-423n4/2022-01-behodler/blob/cedb81273f6daf2ee39ec765eef5ba74f21b2c6e/contracts/FlanBackstop.sol#L121-L125
function normalize(address token, uint256 amount) internal view returns (uint256) { uint256 places = config.decimalPlaces[token]; uint256 bump = 10**(18 - places); return amount * bump; }
Remix
Change to:
function normalize(address token, uint256 amount) internal view returns (uint256) { return amount * 10** (18 - config.decimalPlaces[token]); }
Total : 13
#0 - gititGoro
2022-02-12T02:10:56Z
Of the above list, one suggestion is not a duplicate: the byteX instead of string suggestion. It earns the "sponsor acknowledged" label. The rest are duplicates. The following list links them to the issues they duplicate: revert messages: 94 strict inequality: 15 prefix: 10 array: 12 unchecked 116 unused imports: 2 defaults: 20 calldata 73 struct layout 3 caching: all variants of the same. So linking to first mention: 12 days: 197
#1 - jack-the-pug
2022-02-16T07:51:20Z
This is using the new Gas Report format which should not used be for this project yet, making this a dup with G-01.