Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gas Optimizations #121

Open
code423n4 opened this issue Jun 18, 2022 · 1 comment
Open

Gas Optimizations #121

code423n4 opened this issue Jun 18, 2022 · 1 comment
Labels
bug Something isn't working G (Gas Optimization) sponsor acknowledged Technically the issue is correct, but we're not going to resolve it for XYZ reasons valid

Comments

@code423n4
Copy link
Contributor

Gas Report

Table of Contents

Comparison Operators

IMPACT

In the EVM, there is no opcode for >= or <=.
When using greater than or equal, two operations are performed: > and =.

Using strict comparison operators hence saves gas, approximately 20 gas per comparison.

PROOF OF CONCEPT

Instances include:

MyStrategy.sol

max >= _amount.mul(9_980).div(MAX_BPS)

TOOLS USED

Manual Analysis

MITIGATION

Replace >= with >. In this case 1 is negligible compared to the value compared, we can omit the increment.

Custom Errors

IMPACT

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.

It not only saves gas upon deployment - ~5500 gas saved per custom error instead of a require statement, but it is also cheaper in a function call, 22 gas saved per require statement replaced with a custom error.

PROOF OF CONCEPT

Instances include:

MyStrategy.sol

TOOLS USED

Manual Analysis

MITIGATION

Replace require and revert statements with custom errors.

For instance:

Replace

require(beforeVaultBalance == _getBalance(), "Balance can't change");

with

if (beforeVaultBalance != _getBalance()) {
		revert ChangedBalance();
}

and define the custom error in the contract

error ChangedBalance();

Default value initialization

IMPACT

If a variable is not set/initialized, it is assumed to have the default value (0, false, 0x0 etc depending on the data type).
Explicitly initializing it with its default value is an anti-pattern and wastes 22 gas per variable initialized.

PROOF OF CONCEPT

Instances include:

MyStrategy.sol

uint i = 0
uint i = 0
uint i = 0

TOOLS USED

Manual Analysis

MITIGATION

Remove explicit initialization for default values.

Inline functions

PROBLEM

When we define internal functions to perform computation:

  • The contract’s code size gets bigger
  • the function call consumes more gas than executing it as an inlined function (part of the code, without the function call)

When it does not affect readability, it is recommended to inline functions in order to save gas. It saves ~3,000 gas per inlined function upon deployment, and 30 gas per function call.

PROOF OF CONCEPT

Instances include:

MyStrategy.sol

function _deposit(uint256 _amount)
function _getBalance()
function _getPricePerFullShare())
function _notifyBribesProcessor()

TOOLS USED

Manual Analysis

MITIGATION

Inline these functions where they are called

Prefix increments

IMPACT

Prefix increments are cheaper than postfix increments: it returns the incremented variable instead of returning a temporary variable storing the initial value of the variable. It saves 5 gas per iteration

PROOF OF CONCEPT

Instances include:

MyStrategy.sol

i++
i++
i++

TOOLS USED

Manual Analysis

MITIGATION

change variable++ to ++variable.

Require instead of AND

IMPACT

Require statements including conditions with the && operator can be broken down in multiple require statements to save gas. It saves 50 gas per require statement broken down.

PROOF OF CONCEPT

Instances include:

MyStrategy.sol

require(balanceOfPool() == 0 && LOCKER.balanceOf(address(this)) == 0,"You have to wait for unlock or have to manually rebalance out of it")

TOOLS USED

Manual Analysis

MITIGATION

Break down the statements in multiple require statements.

-require(balanceOfPool() == 0 && LOCKER.balanceOf(address(this)) == 0,"You have to wait for unlock or have to manually rebalance out of it");
+require(balanceOfPool() == 0) 
+require(LOCKER.balanceOf(address(this)) == 0);

You can also improve gas savings by using custom errors

Revert strings length

IMPACT

Revert strings larger than 32-bytes will incur an extra cost upon deployment. It costs ~9,500 extra gas per extra 32-byte upon deployment.

PROOF OF CONCEPT

Instances include:

MyStrategy.sol

require(balanceOfPool() == 0 && LOCKER.balanceOf(address(this)) == 0,"You have to wait for unlock or have to manually rebalance out of it")

  • This string has 67 bytes.

TOOLS USED

Manual Analysis

MITIGATION

Reduce the string message to something smaller than 32-byte to save approximately 19,000 gas upon deployment

Tight Variable Packing

PROBLEM

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.

address type variables are each of 20 bytes size (way less than 32 bytes). However, they here take up a whole 32 bytes slot (they are contiguous).

As bool type variables are of size 1 byte, there's a slot here that can get saved by moving one address closer to a bool. This saves a gsset - 20,000 gas.

PROOF OF CONCEPT

Instances include:

MyStrategy.sol

bool public withdrawalSafetyCheck;
// If nothing is unlocked, processExpiredLocks will revert
bool public processLocksOnReinvest; 

bool private isClaimingBribes;

IBribesProcessor public bribesProcessor;

IBalancerVault public constant BALANCER_VAULT = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);

address public constant BADGER = 0x3472A5A71965499acd81997a54BBA8D852C6E53d;
address public constant BADGER_TREE = 0x660802Fc641b154aBA66a62137e71f331B6d787A

TOOLS USED

Manual Analysis

MITIGATION

Place BADGER before withdrawalSafetyCheck to save one storage slot

+address public constant BADGER = 0x3472A5A71965499acd81997a54BBA8D852C6E53d;
bool public withdrawalSafetyCheck;
// If nothing is unlocked, processExpiredLocks will revert
bool public processLocksOnReinvest; 

bool private isClaimingBribes;

IBribesProcessor public bribesProcessor;

IBalancerVault public constant BALANCER_VAULT = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);

address public constant BADGER_TREE = 0x660802Fc641b154aBA66a62137e71f331B6d787A

Use a more recent version of Solidity

PROBLEM

Use a more recent version of solidity. The advantages here are: built-in overflow/underflow checks (>= 0.8.0) Low level inliner (>= 0.8.2) Cheaper runtime gas (especially relevant when the contract has small functions) Optimizer improvements in packed structs (>= 0.8.3) custom errors (>= 0.8.4), external calls skipping contract existence (>= 0.8.10).

PROOF OF CONCEPT

Instances include:

MyStrategy.sol

pragma solidity 0.6.12

TOOLS USED

Manual Analysis

MITIGATION

Update the solidity version.

@code423n4 code423n4 added bug Something isn't working G (Gas Optimization) labels Jun 18, 2022
code423n4 added a commit that referenced this issue Jun 18, 2022
@GalloDaSballo
Copy link
Collaborator

Comparison Operators

At most should be 6 gas for the == and the AND, also what if we want both comparisons?

Custom Errors

I appreciate the math but in lack of POC call your bluff

Default value initialization

An MSTORE is 3 gas not 22

Inline functions

You'd have to break the Strategy Mix which has helped build almost 60 strategies over a year

Prefix increments

Agree

Tight Variable Packing

Disagree
`
bool public withdrawalSafetyCheck;
// If nothing is unlocked, processExpiredLocks will revert
bool public processLocksOnReinvest;

bool private isClaimingBribes;

IBribesProcessor public bribesProcessor;
`
This will fill a single slot

BADGER is a constant, it has no SLOT, same for rest of code

Use a more recent version of Solidity

I'm good

@GalloDaSballo GalloDaSballo added the sponsor acknowledged Technically the issue is correct, but we're not going to resolve it for XYZ reasons label Jun 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working G (Gas Optimization) sponsor acknowledged Technically the issue is correct, but we're not going to resolve it for XYZ reasons valid
Projects
None yet
Development

No branches or pull requests

3 participants