Platform: Code4rena
Start Date: 31/08/2023
Pot Size: $55,000 USDC
Total HM: 5
Participants: 30
Period: 6 days
Judge: hickuphh3
Total Solo HM: 2
Id: 282
League: ETH
Rank: 28/30
Findings: 1
Award: $27.00
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: c3phas
Also found by: 0x11singh99, 0x3b, 0xta, JCK, K42, ReyAdmirado, SAQ, Sathish9098, hunter_w3b, kaveyjoe, lsaudit, turvy_fuzz
27.0048 USDC - $27.00
Number | Gas | Context |
---|---|---|
[G-01] | Gas savings can be achieved by changing the model for assigning value to the structure | 3 |
[G-02] | ADD UNCHECKED {} FOR SUBTRACTIONS WHERE THE OPERANDS CANNOT UNDERFLOW BECAUSE OF A PREVIOUS REQUIRE() OR IF-STATEMENT | 2 |
[G-03] | internal functions not called by the contract should be removed to save deployment gas | 2 |
[G-04] | Do not calculate constants | 1 |
[G-05] | Use uint256(1)/uint256(2) instead of true/false to save gas for changes | 1 |
[G-06] | Use assembly to perform efficient back-to-back calls | 4 |
[G-07] | A modifier used only once and not being inherited should be inlined to save gas | 3 |
[G-08] | Remove the initializer modifier | 1 |
[G-09] | Use Modifiers Instead of Functions To Save Gas | 2 |
[G-10] | Using delete instead of setting info struct/array to 0 saves gas | 3 |
By directly assigning the values to the individual fields of the bond struct, you can save gas compared to using the struct initialization syntax. This approach avoids the overhead of creating a new BondingCheckpoint instance and assigning the values using the initialization syntax.
File: contracts/bonding/BondingManager.sol 706 newDel.unbondingLocks[newDelUnbondingLockId] = UnbondingLock({ amount: _amount, withdrawRound: withdrawRound }); 763 del.unbondingLocks[unbondingLockId] = UnbondingLock({ amount: _amount, withdrawRound: withdrawRound });
https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/bonding/BondingManager.sol#L706
File: contracts/bonding/BondingVotes.sol 280 BondingCheckpoint memory bond = BondingCheckpoint({ bondedAmount: _bondedAmount, delegateAddress: _delegateAddress, delegatedAmount: _delegatedAmount, lastClaimRound: _lastClaimRound, lastRewardRound: _lastRewardRound });
https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/bonding/BondingVotes.sol#L280
Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn’t possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block.
File: contracts/treasury/GovernorCountingOverridable.sol 188 return _weight - _voter.deductions;
When you define an internal function in a Solidity contract, Solidity generates code for that function and includes it in the contract bytecode. If the function is not called by the contract itself or any of its external functions, then
File: contracts/treasury/GovernorCountingOverridable.sol 64 function __GovernorCountingOverridable_init(uint256 _quota) internal onlyInitializing { 107 function _quorumReached(uint256 _proposalId) internal view virtual override returns (bool) {
Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a constant variable is recomputed each time that the variable is used, which wastes some gas
File: contracts/bonding/BondingManager.sol 32 uint256 constant MAX_FUTURE_ROUND = 2**256 - 1;
https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/bonding/BondingManager.sol#L32
Avoids a Gsset (20000 gas) when changing from false to true, after having been true in the past
File: contracts/treasury/GovernorCountingOverridable.sol 148 voter.hasVoted = true;
If a similar external call is performed back-to-back, we can use assembly to reuse any function signatures and function parameters that stay the same. In addition, we can also reuse the same memory space for each function call (scratch space + free memory pointer + zero slot), which can potentially allow us to avoid memory expansion costs. Note: In order to do this optimization safely we will cache the free memory pointer value and restore it once we are done with our function calls. We will also set the zero slot back to 0 if neccessary.
File: contracts/bonding/BondingManager.sol 1323 if (transcoderPool.contains(_delegate)) { 1324 transcoderPool.updateKey(_delegate, newStake, _newPosPrev, _newPosNext);
1401 if (transcoderPool.isFull()) { 1402 address lastTranscoder = transcoderPool.getLast();
When you use a modifier in Solidity, Solidity generates code to check the conditions of the modifier and execute the modified function if the conditions are met. This generated code can consume gas, especially if the modifier is used frequently or if the modified function is called multiple times.
By inlining a modifier that is used only once and not being inherited, you can eliminate the overhead of the generated code and reduce the gas cost of your contract.
File: contracts/bonding/BondingManager.sol 106 modifier onlyTicketBroker() { 112 modifier onlyRoundsManager() { 118 modifier onlyVerifier() {
https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/bonding/BondingManager.sol#L106
If we can just ensure that the initialize() function could only be called from within the constructor, we shouldn’t need to worry about it getting called again.
File: contracts/treasury/Treasury.sol 21 ) external initializer {
https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/treasury/Treasury.sol#L21
This with 0.8.9 compiler and optimization enabled. As you can see it's cheaper to deploy with a modifier, and it will save you about 30 gas. But sometimes modifiers increase code size of the contract.
File: contracts/bonding/BondingVotes.sol 553 function _onlyBondingManager() internal view { if (msg.sender != address(bondingManager())) { revert InvalidCaller(msg.sender, address(bondingManager())); } }
Using delete instead of setting a struct or array to zero can save gas when clearing the entire data structure. The delete keyword is more gas-efficient because it clears the data structure in a single operation, whereas setting each individual element to zero in a loop can incur additional gas costs.
File: contracts/bonding/BondingManager.sol 773 del.startRound = 0; 1535 t.cumulativeFees = 0; 1536 t.cumulativeRewards = 0;
https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/bonding/BondingManager.sol#L773
#0 - c4-judge
2023-09-21T10:46:21Z
HickupHH3 marked the issue as grade-b