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 |
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;
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++) {
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
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++) {
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";
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;
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";
Instances (1):
File: src/CVXStaker.sol
191: for (uint i = 0; i < rewardTokens.length; i++) {
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 {
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 {
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) {
Saves 5 gas per loop
Instances (1):
File: src/CVXStaker.sol
191: for (uint i = 0; i < rewardTokens.length; i++) {
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;
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;
File: src/xETH.sol
16: bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
18: bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
Instances (1):
File: src/CVXStaker.sol
150: if (toUnstake > 0) {
Issue | Instances | |
---|---|---|
NC-1 | Return values of approve() not checked |
1 |
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);
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 |
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);
Instances (1):
File: src/wxETH.sol
198: function accrueDrip() external drip {}
Instances (1):
File: src/AMO2.sol
309: IERC20(address(xETH)).approve(address(curvePool), quote.xETHAmount);
Issue | Instances | |
---|---|---|
M-1 | Centralization Risk for trusted owners | 32 |
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) {
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 {
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 {
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) {