Livepeer Onchain Treasury Upgrade - 0xta's results

Decentralized video infrastructure protocol powering video in web3's leading social and media applications.

General Information

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

Livepeer

Findings Distribution

Researcher Performance

Rank: 28/30

Findings: 1

Award: $27.00

Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: c3phas

Also found by: 0x11singh99, 0x3b, 0xta, JCK, K42, ReyAdmirado, SAQ, Sathish9098, hunter_w3b, kaveyjoe, lsaudit, turvy_fuzz

Labels

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

Awards

27.0048 USDC - $27.00

External Links

Gas Optimization

Summary

NumberGasContext
[G-01]Gas savings can be achieved by changing the model for assigning value to the structure3
[G-02]ADD UNCHECKED {} FOR SUBTRACTIONS WHERE THE OPERANDS CANNOT UNDERFLOW BECAUSE OF A PREVIOUS REQUIRE() OR IF-STATEMENT2
[G-03]internal functions not called by the contract should be removed to save deployment gas2
[G-04]Do not calculate constants1
[G-05]Use uint256(1)/uint256(2) instead of true/false to save gas for changes1
[G-06]Use assembly to perform efficient back-to-back calls4
[G-07]A modifier used only once and not being inherited should be inlined to save gas3
[G-08]Remove the initializer modifier1
[G-09]Use Modifiers Instead of Functions To Save Gas2
[G-10]Using delete instead of setting info struct/array to 0 saves gas3

[G-01] Gas savings can be achieved by changing the model for assigning value to the structure

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

[G-02] ADD UNCHECKED {} FOR SUBTRACTIONS WHERE THE OPERANDS CANNOT UNDERFLOW BECAUSE OF A PREVIOUS REQUIRE() OR IF-STATEMENT

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;

https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/treasury/GovernorCountingOverridable.sol#L188

[G-03] internal functions not called by the contract should be removed to save deployment gas

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) {

https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/treasury/GovernorCountingOverridable.sol#L64

[G-04] Do not calculate constants

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

[G‑05] Use uint256(1)/uint256(2) instead of true/false to save gas for changes

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;

https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/treasury/GovernorCountingOverridable.sol#L148

[G-06] Use assembly to perform efficient back-to-back calls

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);

https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/bonding/BondingManager.sol#L1323-L1324

1401     if (transcoderPool.isFull()) {
1402     address lastTranscoder = transcoderPool.getLast();

https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/bonding/BondingManager.sol#L1401-L1402

[G-07] A modifier used only once and not being inherited should be inlined to save gas

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

[G-08] Remove the initializer modifier

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

[G-09] Use Modifiers Instead of Functions To Save Gas

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()));
        }
    }

https://github.com/code-423n4/2023-08-livepeer/blob/main/contracts/bonding/BondingVotes.sol#L553-L557

[G-10] Using delete instead of setting info struct/array to 0 saves gas

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

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