Popcorn contest - atharvasama's results

A multi-chain regenerative yield-optimizing protocol.

General Information

Platform: Code4rena

Start Date: 31/01/2023

Pot Size: $90,500 USDC

Total HM: 47

Participants: 169

Period: 7 days

Judge: LSDan

Total Solo HM: 9

Id: 211

League: ETH

Popcorn

Findings Distribution

Researcher Performance

Rank: 89/169

Findings: 1

Award: $69.82

Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

69.8247 USDC - $69.82

Labels

bug
G (Gas Optimization)
grade-b
G-05

External Links

Gas Optimizations list

NumberDetailsInstances
1x += y costs more gas than x = x + y for state variables2
2Can declare the variable outside the loop to save gas34
3Multiple address/id mappings can be combined into a single mapping to a struct7
4Internal functions only called once can be inlined to save gas8
5keccak256() should only need to be called on a specific string literal once9

Gas Optimizations

[G-01] x += y costs more gas than x = x + y for state variables.

Using the addition operator instead of plus-equals saves some gas for state variables.

src/utils/MultiRewardStaking.sol

408:     rewardInfos[_rewardToken].index += deltaIndex;

431:     accruedRewards[_user][_rewardToken] += supplierDelta;

[G-02] Can make variable outside loop to save gas

src/utils/MultiRewardEscrow.sol

156:       bytes32 escrowId = escrowIds[i];
157:       Escrow memory escrow = escrows[escrowId];
159:       uint256 claimable = _getClaimableAmount(escrow);

src/utils/MultiRewardStaking.sol

172:       uint256 rewardAmount = accruedRewards[user][_rewardTokens[i]];
176:       EscrowInfo memory escrowInfo = escrowInfos[_rewardTokens[i]];
375:       RewardInfo memory rewards = rewardInfos[rewardToken];
374:       IERC20 rewardToken = _rewardTokens[i];

src/vault/VaultController.sol

438:       (
439:         address rewardsToken,
440:         uint160 rewardsPerSecond,
441:         uint256 amount,
442:         bool useEscrow,
443:         uint224 escrowDuration,
444:         uint24 escrowPercentage,
445:         uint256 offset
446:       ) = abi.decode(rewardTokenData[i], (address, uint160, uint256, bool, uint224, uint24, uint256));
447:       _verifyToken(rewardsToken);

450:       (bool success, bytes memory returnData) = adminProxy.execute(
451:         rewardsToken,
452:         abi.encodeWithSelector(IERC20.approve.selector, staking, type(uint256).max)
453:       );

497:       (bool success, bytes memory returnData) = adminProxy.execute(
498:         staking,
499:         abi.encodeWithSelector(IMultiRewardStaking.changeRewardSpeed.selector, rewardTokens[i], rewardsSpeeds[i])
500:       );

565:       (bool success, bytes memory returnData) = adminProxy.execute(
566:         _deploymentController,
567:         abi.encodeWithSelector(IDeploymentController.addTemplateCategory.selector, templateCategories[i])
568:       );

588:       (bool success, bytes memory returnData) = adminProxy.execute(
589:         address(_deploymentController),
590:         abi.encodeWithSelector(
591:           ITemplateRegistry.toggleTemplateEndorsement.selector,
592:           templateCategories[i],
593:           templateIds[i]
594:         )
595:       );

609:       (bool success, bytes memory returnData) = adminProxy.execute(
610:         IVault(vaults[i]).adapter(),
611:         abi.encodeWithSelector(IPausable.pause.selector)
612:       );

622:       (bool success, bytes memory returnData) = adminProxy.execute(
623:         vaults[i],
624:         abi.encodeWithSelector(IPausable.pause.selector)
625:       );

635:       (bool success, bytes memory returnData) = adminProxy.execute(
636:         IVault(vaults[i]).adapter(),
637:         abi.encodeWithSelector(IPausable.unpause.selector)
638:       );

648:       (bool success, bytes memory returnData) = adminProxy.execute(
649:         vaults[i],
650:         abi.encodeWithSelector(IPausable.unpause.selector)
651:       );

767:       (bool success, bytes memory returnData) = adminProxy.execute(
768:         adapters[i],
769:         abi.encodeWithSelector(IAdapter.setPerformanceFee.selector, performanceFee)
770:       );

807:       (bool success, bytes memory returnData) = adminProxy.execute(
808:         adapters[i],
809:         abi.encodeWithSelector(IAdapter.setHarvestCooldown.selector, harvestCooldown)
810:       );

[G-03] Multiple address/id mappings can be combined into a single mapping to a struct

If both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key’s keccak256 hash (Gkeccak256 - 30 gas) and that calculation’s associated stack operations. Depending on the circumstances and sizes of types, can avoid a Gsset per mapping combined.

src/utils/MultiRewardEscrow.sol

66:   // User => Escrows
67:   mapping(address => bytes32[]) public userEscrowIds;
68:   // User => RewardsToken => Escrows
69:   mapping(address => mapping(IERC20 => bytes32[])) public userEscrowIdsByToken;

src/utils/MultiRewardStaking.sol

215:   // user => rewardToken -> rewardsIndex
216:   mapping(address => mapping(IERC20 => uint256)) public userIndex;
217:   // user => rewardToken -> accruedRewards
218:   mapping(address => mapping(IERC20 => uint256)) public accruedRewards;

src/vault/TemplateRegistry.sol

30:   // TemplateCategory => TemplateId => Template
31:   mapping(bytes32 => mapping(bytes32 => Template)) public templates;
32:   mapping(bytes32 => bytes32[]) public templateIds;
33:   mapping(bytes32 => bool) public templateExists;

[G-04] Internal functions only called once can be inlined to save gas

src/utils/MultiRewardStaking.sol

191:   function _lockToken(
192:     address user,
193:     IERC20 rewardToken,
194:     uint256 rewardAmount,
195:     EscrowInfo memory escrowInfo
196:   ) internal {

src/vault/VaultController.sol

120:   function _deployVault(VaultInitParams memory vaultData, IDeploymentController _deploymentController)
121:     internal
122:     returns (address vault)
123:   {

141:   function _registerCreatedVault(
142:     address vault,
143:     address staking,
144:     VaultMetadata memory metadata
145:   ) internal {
146:     metadata.vault = vault;
147:     metadata.staking = staking;
148:     metadata.creator = msg.sender;
149: 
150:     _registerVault(vault, metadata);
151:   }

154:   function _handleVaultStakingRewards(address vault, bytes memory rewardsData) internal {

242:   function _encodeAdapterData(DeploymentArgs memory adapterData, bytes memory baseAdapterData)
243:     internal
244:     returns (bytes memory)

256:   function _deployStrategy(DeploymentArgs memory strategyData, IDeploymentController _deploymentController)
257:     internal
258:     returns (address strategy)
259:   {

390:   function _registerVault(address vault, VaultMetadata memory metadata) internal {

692:   function _verifyAdapterConfiguration(address adapter, bytes32 adapterId) internal view {

[G-05] keccak256() should only need to be called on a specific string literal once

It should be saved to an immutable variable, and the variable used instead. If the hash is being used as a part of a function selector, the cast to bytes4 should also only be done once

src/utils/MultiRewardStaking.sol

466:                 keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"),

495:           keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
497:           keccak256("1"),

src/vault/Vault.sol

685:                                 keccak256(
686:                                     "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
687:                                 ),

720:                     keccak256(
721:                         "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
722:                     ),
724:                     keccak256("1"),

src/vault/adapter/abstracts/AdapterBase.sol

653:                                 keccak256(
654:                                     "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
655:                                 ),

688:                     keccak256(
689:                         "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
690:                     ),
692:                     keccak256("1"),

#0 - c4-judge

2023-02-28T14:53:10Z

dmvt marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter