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 #134

Open
code423n4 opened this issue May 20, 2022 · 0 comments
Open

Gas Optimizations #134

code423n4 opened this issue May 20, 2022 · 0 comments
Labels
bug Something isn't working G (Gas Optimization)

Comments

@code423n4
Copy link
Contributor

Gas Report

Table of Contents

Caching storage variables in memory to save gas

IMPACT

Anytime you are reading from storage more than once, it is cheaper in gas cost to cache the variable in memory: a SLOAD cost 100gas, while MLOAD and MSTORE cost 3 gas.

In particular, in for loops, when using the length of a storage array as the condition being checked after each loop, caching the array length in memory can yield significant gas savings if the array length is high

PROOF OF CONCEPT

Instances include:

Aura.sol

scope: mint()

  • _totalSupply is read twice (storage variable read every time totalSupply() is called)
Aura.sol:92
Aura.sol:101

AuraMerkleDrop.sol

scope: withdrawExpired()

  • dao is read twice
AuraMerkleDrop.sol:97
AuraMerkleDrop.sol:100

scope: claim()

  • auraLocker is read twice
AuraMerkleDrop.sol:131
AuraMerkleDrop.sol:132

AuraStakingProxy.sol

scope: applyPendingOwner()

  • pendingOwner is read twice
AuraStakingProxy.sol:117
AuraStakingProxy.sol:119

scope: setApprovals()

  • crvDepositorWrapper is read twice
AuraStakingProxy.sol:147
AuraStakingProxy.sol:148
  • crewards is read twice
AuraStakingProxy.sol:150
AuraStakingProxy.sol:151

scope: distribute()

  • keeper is read twice
AuraStakingProxy.sol:171
AuraStakingProxy.sol:172
  • crvDepositorWrapper is read twice
AuraStakingProxy.sol:178
AuraStakingProxy.sol:179

scope: distributeOther()

  • rewards is read three times
AuraStakingProxy.sol:215
AuraStakingProxy.sol:216
AuraStakingProxy.sol:219

AuraVestedEscrow.sol

scope: cancel()

  • admin is read twice
AuraVestedEscrow.sol:117
AuraVestedEscrow.sol:123

scope: _claim()

  • auraLocker is read three times
AuraVestedEscrow.sol:185
AuraVestedEscrow.sol:186
AuraVestedEscrow.sol:187

ClaimFeesHelper.sol

scope: claimFees()

  • feeDistro is read three times
ClaimFeesHelper.sol:44
ClaimFeesHelper.sol:48
ClaimFeesHelper.sol:52
  • lastTokenTimes is read twice
ClaimFeesHelper.sol:45
ClaimFeesHelper.sol:56

BaseRewardPool.sol

scope: queueNewRewards()

  • periodFinish is read twice
BaseRewardPool.sol:327
BaseRewardPool.sol:336

scope: notifyRewardAmount()

  • periodFinish is read twice
BaseRewardPool.sol:356
BaseRewardPool.sol:359

Booster.sol

scope: setFeeInfo()

  • lockRewards is read four times
Booster.sol:220
Booster.sol:232
Booster.sol:235
Booster.sol:238
  • rewardFactory is read twice
Booster.sol:220
Booster.sol:238

scope: addPool()

  • rewardFactory is read twice
Booster.sol:323
Booster.sol:345

scope: shutdownSystem()

  • poolInfo is read (poolInfo.length) times:
    number of reads depending on poolInfo as it is in a for loop
Booster.sol:379

scope: _earmarkRewards()

  • treasury is read three times
Booster.sol:602
Booster.sol:602
Booster.sol:606
  • platformFee is read twice
Booster.sol:602
Booster.sol:604
  • lockRewards is read twice
Booster.sol:621
Booster.sol:622

BoosterOwner.sol

scope: acceptOwnership()

  • pendingowner is read twice
BoosterOwner.sol:96
BoosterOwner.sol:97

CrvDepositor.sol

scope: lookCurve()

  • incentiveCrv is read twice
CrvDepositor.sol:145
CrvDepositor.sol:146

scope: depositFor()

  • incentiveCrv is read three times
CrvDepositor.sol:175
CrvDepositor.sol:177
CrvDepositor.sol:188

ExtraRewardStashV3.sol

scope: claimRewards()

  • operator is read three times
ExtraRewardStashV3.sol:97
ExtraRewardStashV3.sol:104
ExtraRewardStashV3.sol:111
  • rewardHook is read twice
ExtraRewardStashV3.sol:114
ExtraRewardStashV3.sol:115

scope: processStash()

  • operator is read (1 + tokenList.length) times:
    number of reads depending on tokenList as it is in a for loop
ExtraRewardStashV3.sol:196
ExtraRewardStashV3.sol:209

StashFactoryV2.sol

scope: CreateStash()

  • v3Implementation is read six times
StashFactoryV2.sol:60
StashFactoryV2.sol:61
StashFactoryV2.sol:67
StashFactoryV2.sol:68
StashFactoryV2.sol:74
StashFactoryV2.sol:76

VirtualBalanceRewardPool.sol

scope: queueNewRewards()

  • periodFinish is read twice
VirtualBalanceRewardPool.sol:217
VirtualBalanceRewardPool.sol:224

VoterProxy.sol

scope: setOperator()

  • operator is read twice
VoterProxy.sol:107
VoterProxy.sol:107

scope: withdraw()

  • rewardDeposit is read three times
VoterProxy.sol:193
VoterProxy.sol:194
VoterProxy.sol:195

scope: claimCrv()

  • operator is read twice
VoterProxy.sol:306
VoterProxy.sol:311

scope: claimFees()

  • operator is read twice
VoterProxy.sol:334
VoterProxy.sol:337

TOOLS USED

Manual Analysis

MITIGATION

cache these storage variables in memory

Calldata instead of memory for RO function parameters

PROBLEM

If a reference type function parameter is read-only, it is cheaper in gas to use calldata instead of memory.
Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory.

Try to use calldata as a data location because it will avoid copies and also makes sure that the data cannot be modified.

PROOF OF CONCEPT

Instances include:

BalLiquidityProvider.sol

scope: provideLiquidity()

BalLiquidityProvider.sol:46: IVault.JoinPoolRequest memory _request

PoolManagerSecondaryProxy.sol

scope: setUsedAddress()

PoolManagerSecondaryProxy.sol:68: address[] memory usedList

TOOLS USED

Manual Analysis

MITIGATION

Replace memory with calldata

Comparisons with zero for unsigned integers

IMPACT

>0 is less gas efficient than != 0 if you enable the optimizer at 10k AND you’re in a require statement.
Detailed explanation with the opcodes here

PROOF OF CONCEPT

Instances include:

Aura.sol

Aura.sol:68

AuraBalRewardPool.sol

AuraBalRewardPool.sol:121
AuraBalRewardPool.sol:139
AuraBalRewardPool.sol:157
AuraBalRewardPool.sol:210

AuraLocker.sol

AuraLocker.sol:210
AuraLocker.sol:259
AuraLocker.sol:359
AuraLocker.sol:385
AuraLocker.sol:431
AuraLocker.sol:471
AuraLocker.sol:822
AuraLocker.sol:851

AuraMerkleDrop.sol

AuraMerkleDrop.sol:122

AuraPenaltyForwarder.sol

AuraPenaltyForwarder.sol:52

AuraVestedEscrow.sol

AuraVestedEscrow.sol:118

BalLiquidityProvider.sol

BalLiquidityProvider.sol:57
BalLiquidityProvider.sol:70

ExtraRewardsDistributor.sol

ExtraRewardsDistributor.sol:171

BaseRewardPool.sol

BaseRewardPool.sol:211
BaseRewardPool.sol:227

Booster.sol

Booster.sol:223

CrvDepositor.sol

CrvDepositor.sol:169

PoolManagerSecondaryProxy.sol

PoolManagerSecondaryProxy.sol:104

TOOLS USED

Manual Analysis

MITIGATION

Replace > 0 with != 0

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

PROOF OF CONCEPT

Instances include:

AuraLocker.sol

AuraLocker.sol:216
AuraLocker.sol:217
AuraLocker.sol:389
AuraLocker.sol:531
AuraLocker.sol:589
AuraLocker.sol:602
AuraLocker.sol:730
AuraLocker.sol:829
AuraLocker.sol:863

Aura.sol

Aura.sol:40
Aura.sol:45
Aura.sol:50
Aura.sol:55
Aura.sol:60

AuraStakingProxy.sol

AuraStakingProxy.sol:129

AuraVestedEscrow.sol

AuraVestedEscrow.sol:56
AuraVestedEscrow.sol:66

ClaimFeesHelper.sol

ClaimFeesHelper.sol:51

ExtraRewardsDistributor.sol

ExtraRewardsDistributor.sol:68
ExtraRewardsDistributor.sol:172
ExtraRewardsDistributor.sol:227

TOOLS USED

Manual Analysis

MITIGATION

Replace <= with <, and >= with >. Do not forget to increment/decrement the compared variable

example:

-require(_rate <= 500, "over max rate")
+require(_rate < 501, "over max rate")

However, if 1 is negligible compared to the value of the variable, we can omit the increment.

Constructor parameters should be avoided when possible

IMPACT

Constructor parameters are expensive. The contract deployment will be cheaper in gas if they are hard coded instead of using constructor parameters.

PROOF OF CONCEPT

Instances include:

Aura.sol

Aura.sol:45 vecrvProxy = _proxy;

AuraBalRewardPool.sol

AuraBalRewardPool.sol:72  rewardManager = _rewardManager;
AuraBalRewardPool.sol:74  penaltyForwarder = _penaltyForwarder;

AuraClaimZap.sol

AuraClaimZap.sol:76 crv = _crv;
AuraClaimZap.sol:77 cvx = _cvx;
AuraClaimZap.sol:78 cvxCrv = _cvxCrv;
AuraClaimZap.sol:79 crvDepositWrapper = _crvDepositWrapper;
AuraClaimZap.sol:80 cvxCrvRewards = _cvxCrvRewards;
AuraClaimZap.sol:81 ocker = _locker;

AuraLocker.sol

AuraLocker.sol:154 _name = _nameArg;
AuraLocker.sol:155 _symbol = _symbolArg;
AuraLocker.sol:156 _decimals = 18;
AuraLocker.sol:158 cvxCrv = _cvxCrv;
AuraLocker.sol:159 cvxcrvStaking = _cvxCrvStaking;

AuraMerkleDrop.sol

AuraMerkleDrop.sol:62 dao = _dao;
AuraMerkleDrop.sol:63 merkleRoot = _merkleRoot;
AuraMerkleDrop.sol:66 penaltyForwarder = _penaltyForwarder;

AuraPenaltyForward.sol

AuraPenaltyForward.sol:37 distributionDelay = _delay;

AuraStakingProxy.sol

AuraStakingProxy.sol:74 rewards = _rewards;
AuraStakingProxy.sol:76 crv = _crv;
AuraStakingProxy.sol:77 cvx = _cvx;
AuraStakingProxy.sol:78 cvxCrv = _cvxCrv;
AuraStakingProxy.sol:79 crvDepositorWrapper = _crvDepositorWrapper;
AuraStakingProxy.sol:80 outputBps = _outputBps;

AuraVestedEscrow.sol

AuraVestedEscrow.sol:60 admin = admin_;
AuraVestedEscrow.sol:61 auraLocker = IAuraLocker(auraLocker_);
AuraVestedEscrow.sol:62 startTime = starttime_;
AuraVestedEscrow.sol:63 endTime = endtime_;
AuraVestedEscrow.sol:64 totalTime = endTime - startTime;

BalLiquidityProvider.sol

BalLiquidityProvider.sol:36 minPairAmount = _minPairAmount;
BalLiquidityProvider.sol:37 provider = msg.sender;
BalLiquidityProvider.sol:38 dao = _dao;
BalLiquidityProvider.sol:39 bVault = IVault(_bVault);

ClaimFeesHelper.sol

ClaimFeesHelper.sol:36 voterProxy = _voterProxy;

ArbitartorVault.sol

ArbitartorVault.sol:34 depositor = _depositor;

BaseRewardPool.sol

BaseRewardPool.sol:106 pid = pid_;
BaseRewardPool.sol:109 operator = operator_;
BaseRewardPool.sol:110 rewardManager = rewardManager_;

BaseRewardPool4626.sol

BaseRewardPool4626.sol:39 asset = lptoken_;

Booster.sol

Booster.sol:103 staker = _staker;
Booster.sol:104 minter = _minter;
Booster.sol:105 crv = _crv;
Booster.sol:106 voteOwnership = _voteOwnership;
Booster.sol:107 voteParameter = _voteParameter;
Booster.sol:108 isShutdown = false;

BoosterOwner.sol

BoosterOwner.sol:77 owner = _owner;
BoosterOwner.sol:77 poolManager = _poolManager;
BoosterOwner.sol:77 booster = _booster;
BoosterOwner.sol:77 stashFactory = _stashFactory;
BoosterOwner.sol:77 rescueStash = _rescueStash;
BoosterOwner.sol:77 isSealed = _seal;

ConvexMasterChef.sol

ConvexMasterChef.sol:84 cvx = _cvx;
ConvexMasterChef.sol:85 rewardPerBlock = _rewardPerBlock;
ConvexMasterChef.sol:86 startBlock = _startBlock;
ConvexMasterChef.sol:87 endBlock = _endBlock;

CrvDepositor.sol

CrvDepositor.sol:54 staker = _staker;
CrvDepositor.sol:55 minter = _minter;
CrvDepositor.sol:56 crvBpt = _crvBpt;
CrvDepositor.sol:57 escrow = _escrow;
CrvDepositor.sol:58 daoOperator = _daoOperator;

DepositToken.sol

DepositToken.sol:44 operator = _operator;

DepositToken.sol

DepositToken.sol:59 crv = _crv;

PoolManagerProxy.sol

PoolManagerProxy.sol:27 pools = _pools;
PoolManagerProxy.sol:28 owner = _owner;

PoolManagerSecondaryProxy.sol

PoolManagerSecondaryProxy.sol:40 gaugeController = _gaugeController;
PoolManagerSecondaryProxy.sol:41 pools = _pools;
PoolManagerSecondaryProxy.sol:42 booster = _booster;
PoolManagerSecondaryProxy.sol:43 owner = _owner; 

PoolManagerV3.sol

PoolManagerV3.sol:34 pools = _pools;
PoolManagerV3.sol:35 gaugeController = _gaugeController;
PoolManagerV3.sol:36 operator = _operator;
PoolManagerV3.sol:37 protectAddPool = true; 

RewardFactory.sol

RewardFactory.sol:41 operator = _operator;
RewardFactory.sol:42 crv = _crv;

RewardHook.sol

RewardHook.sol:33 stash = _stash;
RewardHook.sol:34 rewardToken = _reward;

StashFactoryV2.sol

StashFactoryV2.sol:40 operator = _operator;
StashFactoryV2.sol:41 rewardFactory = _rewardFactory;
StashFactoryV2.sol:42 proxyFactory = _proxyFactory;

TokenFactory.sol

TokenFactory.sol:36 operator = _operator;
TokenFactory.sol:37 namePostfix = _namePostfix;
TokenFactory.sol:38 symbolPrefix = _symbolPrefix;

VirtualBalanceRewardPool.sol

VirtualBalanceRewardPool.sol:116 operator = op_;

VoterProxy.sol

VoterProxy.sol:58 mintr = _mintr; 
VoterProxy.sol:59 crv = _crv;
VoterProxy.sol:60 crvBpt = _crvBpt;
VoterProxy.sol:61 escrow = _escrow;
VoterProxy.sol:62 gaugeController = _gaugeController;

TOOLS USED

Manual Analysis

MITIGATION

Hardcode state variables with their initial value instead of writing them during contract deployment with constructor parameters.

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

PROOF OF CONCEPT

Instances include:

Aura.sol

Aura.sol:66
Aura.sol:67
Aura.sol:68
Aura.sol:69
Aura.sol:92
Aura.sol:129

AuraBalRewardPool.sol

AuraBalRewardPool.sol:77
AuraBalRewardPool.sol:121
AuraBalRewardPool.sol:139
AuraBalRewardPool.sol:157
AuraBalRewardPool.sol:206
AuraBalRewardPool.sol:207
AuraBalRewardPool.sol:210

AuraClaimZap.sol

AuraClaimZap.sol:96
AuraClaimZap.sol:137

AuraLocker.sol

AuraLocker.sol:196
AuraLocker.sol:197
AuraLocker.sol:210
AuraLocker.sol:216
AuraLocker.sol:217
AuraLocker.sol:232
AuraLocker.sol:233
AuraLocker.sol:259
AuraLocker.sol:260
AuraLocker.sol:353
AuraLocker.sol:359
AuraLocker.sol:385
AuraLocker.sol:431
AuraLocker.sol:471
AuraLocker.sol:472
AuraLocker.sol:476
AuraLocker.sol:598
AuraLocker.sol:616
AuraLocker.sol:655
AuraLocker.sol:719
AuraLocker.sol:821
AuraLocker.sol:822
AuraLocker.sol:849
AuraLocker.sol:850
AuraLocker.sol:851

AuraMath.sol

AuraMath.sol:40
AuraMath.sol:45
AuraMath.sol:50
AuraMath.sol:55
AuraMath.sol:60

AuraMerkleDrop.sol

AuraMerkleDrop.sol:69
AuraMerkleDrop.sol:78
AuraMerkleDrop.sol:84
AuraMerkleDrop.sol:85
AuraMerkleDrop.sol:91
AuraMerkleDrop.sol:97
AuraMerkleDrop.sol:98
AuraMerkleDrop.sol:105
AuraMerkleDrop.sol:119
AuraMerkleDrop.sol:120
AuraMerkleDrop.sol:121
AuraMerkleDrop.sol:122
AuraMerkleDrop.sol:123
AuraMerkleDrop.sol:126
AuraMerkleDrop.sol:152

AuraMinter.sol

AuraMinter.sol:32

AuraPenaltyForward.sol

AuraPenaltyForward.sol:48
AuraPenaltyForward.sol:52

AuraStakingProxy.sol

AuraStakingProxy.sol:89
AuraStakingProxy.sol:90
AuraStakingProxy.sol:100
AuraStakingProxy.sol:108
AuraStakingProxy.sol:116
AuraStakingProxy.sol:117
AuraStakingProxy.sol:128
AuraStakingProxy.sol:129
AuraStakingProxy.sol:138
AuraStakingProxy.sol:158
AuraStakingProxy.sol:159
AuraStakingProxy.sol:172
AuraStakingProxy.sol:203

AuraVestedEscrow.sol

AuraVestedEscrow.sol:56
AuraVestedEscrow.sol:57
AuraVestedEscrow.sol:66
AuraVestedEscrow.sol:78
AuraVestedEscrow.sol:87
AuraVestedEscrow.sol:97
AuraVestedEscrow.sol:117
AuraVestedEscrow.sol:118
AuraVestedEscrow.sol:185

BalLiquidityProvider.sol

BalLiquidityProvider.sol:47
BalLiquidityProvider.sol:48
BalLiquidityProvider.sol:49
BalLiquidityProvider.sol:53
BalLiquidityProvider.sol:57
BalLiquidityProvider.sol:65
BalLiquidityProvider.sol:70
BalLiquidityProvider.sol:79
BalLiquidityProvider.sol:89

ClaimFeesHelper.sol

ClaimFeesHelper.sol:45

CrvDepositorWrapper.sol

CrvDepositorWrapper.sol:42
CrvDepositorWrapper.sol:119

ExtraRewardsDistributor.sol

ExtraRewardsDistributor.sol:68
ExtraRewardsDistributor.sol:74
ExtraRewardsDistributor.sol:171
ExtraRewardsDistributor.sol:172

ArbitartorVault.sol

ArbitartorVault.sol:38
ArbitartorVault.sol:47
ArbitartorVault.sol:54

ArbitartorVault.sol

ArbitartorVault.sol:126
ArbitartorVault.sol:127
ArbitartorVault.sol:133
ArbitartorVault.sol:211
ArbitartorVault.sol:227
ArbitartorVault.sol:325

BaseRewardPool4626.sol

BaseRewardPool4626.sol:63
BaseRewardPool4626.sol:90

Booster.sol

Booster.sol:129
Booster.sol:139
Booster.sol:149
Booster.sol:159
Booster.sol:192
Booster.sol:202
Booster.sol:218
Booster.sol:219
Booster.sol:220
Booster.sol:222
Booster.sol:223
Booster.sol:226
Booster.sol:256
Booster.sol:258
Booster.sol:273
Booster.sol:276
Booster.sol:278
Booster.sol:279
Booster.sol:280
Booster.sol:281
Booster.sol:295
Booster.sol:313
Booster.sol:314
Booster.sol:315
Booster.sol:357
Booster.sol:376
Booster.sol:398
Booster.sol:400
Booster.sol:408
Booster.sol:505
Booster.sol:515
Booster.sol:525
Booster.sol:526
Booster.sol:536
Booster.sol:549
Booster.sol:560
Booster.sol:574
Booster.sol:635
Booster.sol:645
Booster.sol:648
Booster.sol:649
Booster.sol:670

BoosterOwner.sol

BoosterOwner.sol:96
BoosterOwner.sol:109
BoosterOwner.sol:140
BoosterOwner.sol:146
BoosterOwner.sol:159
BoosterOwner.sol:160
BoosterOwner.sol:170
BoosterOwner.sol:171
BoosterOwner.sol:185

cCrv.sol

cCrv.sol:39
cCrv.sol:47
cCrv.sol:56

ConvexMasterChef.sol

ConvexMasterChef.sol:242

CrvDepositor.sol

CrvDepositor.sol:63
CrvDepositor.sol:68
CrvDepositor.sol:73
CrvDepositor.sol:81
CrvDepositor.sol:89
CrvDepositor.sol:90
CrvDepositor.sol:141
CrvDepositor.sol:169

DepositToken.sol

DepositToken.sol:48
DepositToken.sol:54

ExtraRewardStashV3.sol

ExtraRewardStashV3.sol:70
ExtraRewardStashV3.sol:97
ExtraRewardStashV3.sol:141
ExtraRewardStashV3.sol:147
ExtraRewardStashV3.sol:196

PoolManagerProxy.sol

PoolManagerProxy.sol:68
PoolManagerProxy.sol:69
PoolManagerProxy.sol:73
PoolManagerProxy.sol:78

PoolManagerSecondaryProxy.sol

PoolManagerSecondaryProxy.sol:87
PoolManagerSecondaryProxy.sol:95
PoolManagerSecondaryProxy.sol:104
PoolManagerSecondaryProxy.sol:111
PoolManagerSecondaryProxy.sol:118

PoolManagerV3.sol

PoolManagerV3.sol:41
PoolManagerV3.sol:49
PoolManagerV3.sol:71
PoolManagerV3.sol:81
PoolManagerV3.sol:88

RewardFactory.sol

RewardFactory.sol:47
RewardFactory.sol:57
RewardFactory.sol:72

StashFactory.sol

StashFactory.sol:46
StashFactory.sol:56
StashFactory.sol:60
StashFactory.sol:67
StashFactory.sol:74
StashFactory.sol:83

TokenFactory.sol

TokenFactory.sol:42

VirtualBalanceRewardPool.sol

VirtualBalanceRewardPool.sol:169
VirtualBalanceRewardPool.sol:170
VirtualBalanceRewardPool.sol:182
VirtualBalanceRewardPool.sol:183
VirtualBalanceRewardPool.sol:213

VoterProxy.sol

VoterProxy.sol:74
VoterProxy.sol:84
VoterProxy.sol:95
VoterProxy.sol:106
VoterProxy.sol:107
VoterProxy.sol:117
VoterProxy.sol:123
VoterProxy.sol:138
VoterProxy.sol:167
VoterProxy.sol:189
VoterProxy.sol:190
VoterProxy.sol:207
VoterProxy.sol:224
VoterProxy.sol:243
VoterProxy.sol:254
VoterProxy.sol:266
VoterProxy.sol:276
VoterProxy.sol:285
VoterProxy.sol:294
VoterProxy.sol:306
VoterProxy.sol:322
VoterProxy.sol:334
VoterProxy.sol:350
VoterProxy.sol:353

TOOLS USED

Manual Analysis

MITIGATION

Replace require and revert statements with custom errors.

For instance, in Aura.sol:

Replace

require(msg.sender == operator, "Only operator");

with

if (msg.sender != operator) {
		revert IsNotOperator();
}

and define the custom error in the contract

error IsNotOperator();

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 gas.

PROOF OF CONCEPT

Instances include:

AuraBalRewardPool.sol

AuraBalRewardPool.sol:35: uint256 public pendingPenalty = 0;
AuraBalRewardPool.sol:38: uint256 public periodFinish = 0;
AuraBalRewardPool.sol:39: uint256 public rewardRate = 0;

AuraClaimZap.sol

AuraClaimZap.sol:143: uint256 i = 0;
AuraClaimZap.sol:147: uint256 i = 0;
AuraClaimZap.sol:151: uint256 i = 0;

AuraClaimZap.sol

AuraClaimZap.sol:72: uint256 public queuedCvxCrvRewards = 0;
AuraClaimZap.sol:174: uint256 i = 0;
AuraClaimZap.sol:381: uint256 reward = 0;
AuraClaimZap.sol:485: uint256 futureUnlocksSum = 0;
AuraClaimZap.sol:540: uint256 unlocksSinceLatestCkpt = 0;
AuraClaimZap.sol:630: uint256 low = 0;
AuraClaimZap.sol:773: uint256 i = 0;

AuraLocker.sol

AuraLocker.sol:114: bool public isShutdown = false;

AuraMerkleDrop.sol

AuraMerkleDrop.sol:29: uint256 public pendingPenalty = 0;

AuraVestedEscrow.sol

AuraVestedEscrow.sol:33: bool public initialised = false;
AuraVestedEscrow.sol:99: uint256 public totalAmount = 0;
AuraVestedEscrow.sol:100: uint256 i = 0;

BalLiquidityProvider.sol

BalLiquidityProvider.sol:51: uint256 i = 0;

ExtraRewardsDistributor.sol

ExtraRewardsDistributor.sol:231: uint256 claimableTokens = 0;

ArbitartorVault.sol

ArbitartorVault.sol:49: uint256 i = 0;

BaseRewardPool.sol

BaseRewardPool.sol:71: uint256 public periodFinish = 0;
BaseRewardPool.sol:72: uint256 public rewardRate = 0;
BaseRewardPool.sol:75: uint256 public queuedRewards = 0;
BaseRewardPool.sol:76: uint256 public currentRewards = 0;
BaseRewardPool.sol:77: uint256 public historicalRewards = 0;

Booster.sol

Booster.sol:29: uint256 public platformFee = 0;
Booster.sol:538: uint256 i = 0;

BoosterOwner.sol

BoosterOwner.sol:144: uint256 i = 0;

ConvexMasterChef.sol

ConvexMasterChef.sol:63: uint256 public totalAllocPoint = 0;
ConvexMasterChef.sol:180: uint256 pid = 0;

CrvDepositor.sol

CrvDepositor.sol:36: uint256 public incentiveCrv = 0;

ExtraRewardStashV3.sol

ExtraRewardStashV3.sol:125: uint256 i = 0;

VirtualBalanceRewardPool.sol

VirtualBalanceRewardPool.sol:89: uint256 public periodFinish = 0;
VirtualBalanceRewardPool.sol:90: uint256 public rewardRate = 0;;
VirtualBalanceRewardPool.sol:93: uint256 public queuedRewards = 0;
VirtualBalanceRewardPool.sol:94: uint256 public currentRewards = 0;
VirtualBalanceRewardPool.sol:95: uint256 public historicalRewards = 0;

VoterProxy.sol

VoterProxy.sol:308: uint256 _balance = 0;

TOOLS USED

Manual Analysis

MITIGATION

Remove explicit initialization for default values.

Event emitting of local variable

PROBLEM

When emitting an event, using a local variable instead of a storage variable saves gas.

PROOF OF CONCEPT

Instances include:

Booster.sol

Booster.sol:235 emit FeeInfoUpdated(_feeDistro, lockRewards, crv) //lockRewards can be cached

BoosterOwner.sol

BoosterOwner.sol:99 emit AcceptedOwnership(owner); //can emit using a cached version of pendingowner before setting pendingowner to 0
BoosterOwner.sol:163 emit ShutdownStarted(forceTimestamp); //can emit block.timestamp + FORCE_DELAY

ConvexMasterChef.sol

ConvexMasterChef.sol:287 emit EmergencyWithdraw(msg.sender, _pid, user.amount); //user.amount can be cached

TOOLS USED

Manual Analysis

MITIGATION

In most instances, the storage variable is read multiple times in the function and it is recommended to cache them into memory, then passing these cached variables in the emit statement, as explained in the cache paragraph

Prefix increments

IMPACT

Prefix increments are cheaper than postfix increments.

PROOF OF CONCEPT

Instances include:

AuraClaimZap.sol

AuraClaimZap.sol:143: i++
AuraClaimZap.sol:147: i++
AuraClaimZap.sol:151: i++

AuraLocker.sol

AuraLocker.sol:174: i++
AuraLocker.sol:306: i++
AuraLocker.sol:410: i++
AuraLocker.sol:426: nextUnlockIndex++
AuraLocker.sol:696: i++
AuraLocker.sol:702: idx++
AuraLocker.sol:773: i++

AuraVestedEscrow.sol

AuraVestedEscrow.sol:100: i++

BalLiquidityProvider.sol

BalLiquidityProvider.sol:51: i++

ExtraRewardsDistributor.sol

ExtraRewardsDistributor.sol:233: i++

ArbitartorVault.sol

ArbitartorVault.sol:49: i++

BaseRewardPool.sol

BaseRewardPool.sol:214: i++
BaseRewardPool.sol:230: i++
BaseRewardPool.sol:262: i++
BaseRewardPool.sol:296: i++

Booster.sol

Booster.sol:379: i++
Booster.sol:538: i++

BoosterOwner.sol

BoosterOwner.sol:144: i++

ExtraRewardStashV3.sol

ExtraRewardStashV3.sol:125: i++
ExtraRewardStashV3.sol:199: i++

PoolManagerSecondaryProxy.sol

PoolManagerSecondaryProxy.sol:69: 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.

PROOF OF CONCEPT

Instances include:

AuraStakingProxy.sol

AuraStakingProxy:90: require(_outputBps > 9000 && _outputBps < 10000, "Invalid output bps");
AuraStakingProxy:159: require(_token != crv && _token != cvx && _token != cvxCrv, "not allowed");
AuraStakingProxy:203: require(address(_token) != crv && address(_token) != cvxCrv, "not allowed");

BalLiquidityProvider.sol

BalLiquidityProvider:48: require(_request.assets.length == 2 && _request.maxAmountsIn.length == 2, "!valid");
BalLiquidityProvider:57: require(bal > 0 && bal == _request.maxAmountsIn[i], "!bal");

Booster.sol

Booster:220: require(lockRewards != address(0) && rewardFactory != address(0), "!initialised");
Booster:221: require(_feeToken != address(0) && _feeDistro != address(0), "!addresses");
Booster:278: require(_lockFees >= 300 && _lockFees <= 1500, "!lockFees");
Booster:279: require(_stakerFees >= 300 && _stakerFees <= 1500, "!stakerFees");
Booster:280: require(_callerFees >= 10 && _callerFees <= 100, "!callerFees");
Booster:313: require(msg.sender==poolManager && !isShutdown, "!add");
Booster:314: require(_gauge != address(0) && _lptoken != address(0),"!param");

StashFactoryV2.sol

StashFactoryV2:111: require(!usedMap[_lptoken] && !usedMap[_gauge], "cant force used pool");
StashFactoryV2:83: require(!isV1 && !isV2 && !isV3,"stash version mismatch");

TOOLS USED

Manual Analysis

MITIGATION

-require(address(_token) != crv && address(_token) != cvxCrv, "not allowed");
+require(address(_token) != crv);  +require(address(_token) != cvxCrv, "not allowed"));

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 them closer to an address

PROOF OF CONCEPT

Instances include:

AuraLocker.sol

AuraLocker.sol:109:
address public immutable cvxcrvStaking;
//     Incentives
uint256 public kickRewardPerEpoch = 100;
uint256 public kickRewardEpochDelay = 3;
//     Shutdown
bool public isShutdown = false;

AuraVestedEscrow.sol

AuraVestedEscrow.sol:26:
address public admin;
IAuraLocker public auraLocker;

uint256 public immutable startTime;
uint256 public immutable endTime;
uint256 public immutable totalTime;

bool public initialised = false;

Booster.sol

Booster.sol:45: address public lockRewards; //cvxCrv rewards(crv)

mapping(address => FeeDistro) public feeTokens;
struct FeeDistro {
    address distro;
    address rewards;
    bool active;
}

bool public isShutdown;

ExtraRewardStashV3.sol

ExtraRewardStashV3.sol:37: address public rewardFactory;
   
mapping(address => uint256) public historicalRewards;
bool public hasRedirected;

CrvDepositor.sol

CrvDepositor.sol:35: address public immutable minter;
uint256 public incentiveCrv = 0;
uint256 public unlockTime;

bool public cooldown;

TOOLS USED

Manual Analysis

MITIGATION

Place every boolean right after an address to save one storage slot

Unchecked arithmetic

IMPACT

The default "checked" behavior costs more gas when adding/diving/multiplying, because under-the-hood those checks are implemented as a series of opcodes that, prior to performing the actual arithmetic, check for under/overflow and revert if it is detected.

if it can statically be determined there is no possible way for your arithmetic to under/overflow (such as a condition in an if statement), surrounding the arithmetic in an unchecked block will save gas

PROOF OF CONCEPT

Instances include:

AuraBalRewardPool.sol

AuraBalRewardPool.sol:78: _startDelay bounded, startTime cannot overflow

AuraMinter.sol

AuraMinter.sol:78: inflationProtectionTime cannot overflow

AuraVestedEscrow.sol

AuraVestedEscrow.sol:65: because of the require statement line 57, totalTime cannot underflow

AuraVestedEscrow.sol

AuraVestedEscrow.sol:163: forceTimestamp cannot overflow

CrvDepositor.sol

CrvDepositor.sol:94: unlockAt cannot overflow
CrvDepositor.sol:127: unlockAt cannot overflow

TOOLS USED

Manual Analysis

MITIGATION

Place the arithmetic operations in an unchecked block

Unnecessary library

IMPACT

As of Solidity 0.8.0, arithmetic operations revert on underflow and overflow by default. The use of external libraries to perform additions and subtractions should be avoided as it costs extra gas.

PROOF OF CONCEPT

Most of the contracts in scope use the add and sub functions of the AuraMath.sol.

TOOLS USED

Manual Analysis

MITIGATION

Use Solidity native additions and subtractions for uint256 variables

@code423n4 code423n4 added bug Something isn't working G (Gas Optimization) labels May 20, 2022
code423n4 added a commit that referenced this issue May 20, 2022
@0xMaharishi 0xMaharishi added the duplicate This issue or pull request already exists label May 28, 2022
@dmvt dmvt removed the duplicate This issue or pull request already exists label Jul 4, 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)
Projects
None yet
Development

No branches or pull requests

3 participants