Skip to content

Instantly share code, notes, and snippets.

@romeroadrian
Created May 12, 2023 20:10

Report

Gas Optimizations

Issue Instances
GAS-1 Using bools for storage incurs overhead 1
GAS-2 Cache array length outside of loop 1
GAS-3 For Operations that will not overflow, you could use unchecked 106
GAS-4 Don't initialize variables with default value 1
GAS-5 Functions guaranteed to revert when called by normal users can be marked payable 10
GAS-6 ++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too) 1
GAS-7 Using private rather than public for constants, saves gas 6
GAS-8 Use != 0 instead of > 0 for unsigned integer comparison 1

[GAS-1] Using bools for storage incurs overhead

Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas), and to avoid Gsset (20000 gas) when changing from ‘false’ to ‘true’, after having been ‘true’ in the past. See source.

Instances (1):

File: src/wxETH.sol

62:     bool public dripEnabled;

Link to code

[GAS-2] Cache array length outside of loop

If not cached, the solidity compiler will always read the length of the array during each iteration. That is, if it is a storage array, this is an extra sload operation (100 additional extra gas for each iteration except for the first) and if it is a memory array, this is an extra mload operation (3 additional gas for each iteration except for the first).

Instances (1):

File: src/CVXStaker.sol

191:             for (uint i = 0; i < rewardTokens.length; i++) {

Link to code

[GAS-3] For Operations that will not overflow, you could use unchecked

Instances (106):

File: src/AMO2.sol

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

6: import "@openzeppelin-contracts/access/AccessControl.sol";

6: import "@openzeppelin-contracts/access/AccessControl.sol";

6: import "@openzeppelin-contracts/access/AccessControl.sol";

7: import "./interfaces/ICurvePool.sol";

7: import "./interfaces/ICurvePool.sol";

8: import {IXETH} from "./interfaces/IXETH.sol";

8: import {IXETH} from "./interfaces/IXETH.sol";

9: import {CVXStaker} from "./CVXStaker.sol";

139:     uint256 public maxSlippageBPS = 100 * 1E14;

151:     uint256 public cooldownBlocks = 1800; /// (6 * 60 * 60) / 12

151:     uint256 public cooldownBlocks = 1800; /// (6 * 60 * 60) / 12

151:     uint256 public cooldownBlocks = 1800; /// (6 * 60 * 60) / 12

151:     uint256 public cooldownBlocks = 1800; /// (6 * 60 * 60) / 12

151:     uint256 public cooldownBlocks = 1800; /// (6 * 60 * 60) / 12

151:     uint256 public cooldownBlocks = 1800; /// (6 * 60 * 60) / 12

169:         if (lastRebalanceBlock + cooldownBlocks >= block.number)

210:         uint256 xEthPct = (xETHBal * BASE_UNIT) / (stETHBal + xETHBal);

210:         uint256 xEthPct = (xETHBal * BASE_UNIT) / (stETHBal + xETHBal);

210:         uint256 xEthPct = (xETHBal * BASE_UNIT) / (stETHBal + xETHBal);

324:         return (amount * (BASE_UNIT - maxSlippageBPS)) / BASE_UNIT;

324:         return (amount * (BASE_UNIT - maxSlippageBPS)) / BASE_UNIT;

324:         return (amount * (BASE_UNIT - maxSlippageBPS)) / BASE_UNIT;

344:             (vp * defenderQuote.lpBurn) / BASE_UNIT

344:             (vp * defenderQuote.lpBurn) / BASE_UNIT

369:             (BASE_UNIT * defenderQuote.xETHAmount) / vp

369:             (BASE_UNIT * defenderQuote.xETHAmount) / vp

Link to code

File: src/CVXStaker.sol

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

6: import "@openzeppelin-contracts/access/Ownable.sol";

6: import "@openzeppelin-contracts/access/Ownable.sol";

6: import "@openzeppelin-contracts/access/Ownable.sol";

7: import "./interfaces/ICurvePool.sol";

7: import "./interfaces/ICurvePool.sol";

8: import "./interfaces/ICVXBooster.sol";

8: import "./interfaces/ICVXBooster.sol";

9: import "./interfaces/IBaseRewardPool.sol";

9: import "./interfaces/IBaseRewardPool.sol";

149:         uint256 toUnstake = (amount < clpBalance) ? 0 : amount - clpBalance;

191:             for (uint i = 0; i < rewardTokens.length; i++) {

191:             for (uint i = 0; i < rewardTokens.length; i++) {

Link to code

File: src/interfaces/IXETH.sol

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

Link to code

File: src/wxETH.sol

3: import "@openzeppelin-contracts/token/ERC20/ERC20.sol";

3: import "@openzeppelin-contracts/token/ERC20/ERC20.sol";

3: import "@openzeppelin-contracts/token/ERC20/ERC20.sol";

3: import "@openzeppelin-contracts/token/ERC20/ERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/IERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

6: import "@openzeppelin-contracts/access/Ownable.sol";

6: import "@openzeppelin-contracts/access/Ownable.sol";

6: import "@openzeppelin-contracts/access/Ownable.sol";

7: import "solmate/utils/FixedPointMathLib.sol";

7: import "solmate/utils/FixedPointMathLib.sol";

47:     uint256 public constant INITIAL_EXCHANGE_RATE = 1E18; // exchange rate at begining is 1:1

47:     uint256 public constant INITIAL_EXCHANGE_RATE = 1E18; // exchange rate at begining is 1:1

87:         return (xETHAmount * BASE_UNIT) / exchangeRate();

87:         return (xETHAmount * BASE_UNIT) / exchangeRate();

119:         return (wxETHAmount * exchangeRate()) / BASE_UNIT;

119:         return (wxETHAmount * exchangeRate()) / BASE_UNIT;

154:         lockedFunds += amount;

212:         uint256 cashMinusLocked = xETH.balanceOf(address(this)) - lockedFunds;

215:         return (cashMinusLocked * BASE_UNIT) / _totalSupply;

215:         return (cashMinusLocked * BASE_UNIT) / _totalSupply;

227:         uint256 blockDelta = block.number - lastReport;

231:             uint256 dripAmount = blockDelta * dripRatePerBlock;

241:             lockedFunds -= dripAmount;

Link to code

File: src/xETH.sol

4: import "@openzeppelin-contracts/token/ERC20/ERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/ERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/ERC20.sol";

4: import "@openzeppelin-contracts/token/ERC20/ERC20.sol";

5: import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Burnable.sol";

5: import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Burnable.sol";

5: import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Burnable.sol";

5: import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Burnable.sol";

5: import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Burnable.sol";

6: import "@openzeppelin-contracts/security/Pausable.sol";

6: import "@openzeppelin-contracts/security/Pausable.sol";

6: import "@openzeppelin-contracts/security/Pausable.sol";

7: import "@openzeppelin-contracts/access/AccessControl.sol";

7: import "@openzeppelin-contracts/access/AccessControl.sol";

7: import "@openzeppelin-contracts/access/AccessControl.sol";

Link to code

[GAS-4] Don't initialize variables with default value

Instances (1):

File: src/CVXStaker.sol

191:             for (uint i = 0; i < rewardTokens.length; i++) {

Link to code

[GAS-5] Functions guaranteed to revert when called by normal users can be marked payable

If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided.

Instances (10):

File: src/CVXStaker.sol

78:     function setOperator(address _operator) external onlyOwner {

89:     function setRewardsRecipient(address _recipeint) external onlyOwner {

126:     function depositAndStake(uint256 amount) external onlyOperator {

Link to code

File: src/wxETH.sol

146:     function addLockedFunds(uint256 amount) external onlyOwner drip {

159:     function setDripRate(uint256 newDripRatePerBlock) external onlyOwner drip {

170:     function startDrip() external onlyOwner drip {

184:     function stopDrip() external onlyOwner drip {

Link to code

File: src/xETH.sol

37:     function setAMO(address newAMO) external onlyRole(DEFAULT_ADMIN_ROLE) {

86:     function pause() external whenNotPaused onlyRole(PAUSER_ROLE) {

93:     function unpause() external whenPaused onlyRole(PAUSER_ROLE) {

Link to code

[GAS-6] ++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too)

Saves 5 gas per loop

Instances (1):

File: src/CVXStaker.sol

191:             for (uint i = 0; i < rewardTokens.length; i++) {

Link to code

[GAS-7] Using private rather than public for constants, saves gas

If needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table

Instances (6):

File: src/AMO2.sol

116:     bytes32 public constant REBALANCE_DEFENDER_ROLE =

120:     uint256 public constant BASE_UNIT = 1E18;

Link to code

File: src/wxETH.sol

47:     uint256 public constant INITIAL_EXCHANGE_RATE = 1E18; // exchange rate at begining is 1:1

50:     uint256 public constant BASE_UNIT = 1E18;

Link to code

File: src/xETH.sol

16:     bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

18:     bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

Link to code

[GAS-8] Use != 0 instead of > 0 for unsigned integer comparison

Instances (1):

File: src/CVXStaker.sol

150:         if (toUnstake > 0) {

Link to code

Non Critical Issues

Issue Instances
NC-1 Return values of approve() not checked 1

[NC-1] Return values of approve() not checked

Not all IERC20 implementations revert() when there's a failure in approve(). The function signature has a boolean return value and they indicate errors that way instead. By not checking the return value, operations that should have marked as failed, may potentially go through without actually approving anything

Instances (1):

File: src/AMO2.sol

309:         IERC20(address(xETH)).approve(address(curvePool), quote.xETHAmount);

Link to code

Low Issues

Issue Instances
L-1 Do not use deprecated library functions 3
L-2 Empty Function Body - Consider commenting why 1
L-3 Unsafe ERC20 operation(s) 1

[L-1] Do not use deprecated library functions

Instances (3):

File: src/AMO2.sol

545:         IERC20(address(xETH)).safeApprove(address(curvePool), xETHAmount);

546:         stETH.safeApprove(address(curvePool), stETHAmount);

571:         stETH.safeApprove(address(curvePool), stETHAmount);

Link to code

[L-2] Empty Function Body - Consider commenting why

Instances (1):

File: src/wxETH.sol

198:     function accrueDrip() external drip {}

Link to code

[L-3] Unsafe ERC20 operation(s)

Instances (1):

File: src/AMO2.sol

309:         IERC20(address(xETH)).approve(address(curvePool), quote.xETHAmount);

Link to code

Medium Issues

Issue Instances
M-1 Centralization Risk for trusted owners 32

[M-1] Centralization Risk for trusted owners

Impact:

Contracts have owners with privileged rights to perform admin tasks and need to be trusted to not perform malicious updates or drain funds.

Instances (32):

File: src/AMO2.sol

11: contract xETH_AMO is AccessControl {

243:         onlyRole(REBALANCE_DEFENDER_ROLE)

290:         onlyRole(REBALANCE_DEFENDER_ROLE)

388:     ) external onlyRole(DEFAULT_ADMIN_ROLE) {

411:     ) external onlyRole(DEFAULT_ADMIN_ROLE) {

431:     ) external onlyRole(DEFAULT_ADMIN_ROLE) {

448:     ) external onlyRole(DEFAULT_ADMIN_ROLE) {

464:     ) external onlyRole(DEFAULT_ADMIN_ROLE) {

481:     ) external onlyRole(DEFAULT_ADMIN_ROLE) {

497:     ) external onlyRole(DEFAULT_ADMIN_ROLE) {

514:     ) external onlyRole(DEFAULT_ADMIN_ROLE) {

536:     ) external onlyRole(DEFAULT_ADMIN_ROLE) returns (uint256 lpOut) {

564:     ) external onlyRole(DEFAULT_ADMIN_ROLE) returns (uint256 lpOut) {

596:         onlyRole(DEFAULT_ADMIN_ROLE)

630:     ) external onlyRole(DEFAULT_ADMIN_ROLE) {

Link to code

File: src/CVXStaker.sol

11: contract CVXStaker is Ownable {

66:     ) external onlyOwner {

78:     function setOperator(address _operator) external onlyOwner {

89:     function setRewardsRecipient(address _recipeint) external onlyOwner {

105:     ) external onlyOwner {

173:     ) external onlyOwner {

Link to code

File: src/wxETH.sol

9: contract WrappedXETH is ERC20, Ownable {

146:     function addLockedFunds(uint256 amount) external onlyOwner drip {

159:     function setDripRate(uint256 newDripRatePerBlock) external onlyOwner drip {

170:     function startDrip() external onlyOwner drip {

184:     function stopDrip() external onlyOwner drip {

Link to code

File: src/xETH.sol

9: contract xETH is ERC20, ERC20Burnable, Pausable, AccessControl {

37:     function setAMO(address newAMO) external onlyRole(DEFAULT_ADMIN_ROLE) {

63:     ) public onlyRole(MINTER_ROLE) whenNotPaused {

75:     ) public onlyRole(MINTER_ROLE) whenNotPaused {

86:     function pause() external whenNotPaused onlyRole(PAUSER_ROLE) {

93:     function unpause() external whenPaused onlyRole(PAUSER_ROLE) {

Link to code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment