Report
Gas Optimizations
Issue | Instances | |
---|---|---|
[GAS-1] | Cache array length outside of loop | 16 |
[GAS-2] | Use != 0 instead of > 0 for unsigned integer comparison | 37 |
[GAS-3] | Using bools for storage incurs overhead | 9 |
[GAS-4] | Use Custom Errors | 202 |
[GAS-5] | ++i costs less gas than i++ , especially when it's used in for -loops (--i /i-- too) |
1 |
[GAS-6] | Using private rather than public for constants, saves gas |
4 |
[GAS-1] 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 (16):
File: liquid-staking/GiantMevAndFeesPool.sol
90: for (uint256 i; i < _stakingFundsVaults.length; ++i) {
183: for (uint256 i; i < _lpTokens.length; ++i) {
File: liquid-staking/GiantSavETHVaultPool.sol
82: for (uint256 j; j < _lpTokens[i].length; ++j) {
148: for (uint256 j; j < _lpTokens[i].length; ++j) {
File: liquid-staking/LiquidStakingManager.sol
392: for(uint256 i; i < _blsPubKeys.length; ++i) {
File: liquid-staking/StakingFundsVault.sol
203: for (uint256 i; i < _blsPubKeys.length; ++i) {
266: for (uint256 i; i < _blsPublicKeys.length; ++i) {
276: for (uint256 i; i < _blsPubKeys.length; ++i) {
286: for (uint256 i; i < _token.length; ++i) {
File: syndicate/Syndicate.sol
211: for (uint256 i; i < _blsPubKeys.length; ++i) {
259: for (uint256 i; i < _blsPubKeys.length; ++i) {
301: for (uint256 i; i < _blsPubKeys.length; ++i) {
346: for (uint256 i; i < _blsPubKeys.length; ++i) {
585: for (uint256 i; i < _priorityStakers.length; ++i) {
598: for (uint256 i; i < _blsPublicKeys.length; ++i) {
648: for (uint256 i; i < _blsPubKeys.length; ++i) {
[GAS-2] Use != 0 instead of > 0 for unsigned integer comparison
Instances (37):
File: liquid-staking/ETHPoolLPFactory.sol
59: require(numOfRotations > 0, "Empty arrays");
File: liquid-staking/GiantMevAndFeesPool.sol
35: require(numOfVaults > 0, "Zero vaults");
62: require(numOfVaults > 0, "Empty array");
112: require(numOfRotations > 0, "Empty arrays");
132: require(numOfVaults > 0, "Empty arrays");
File: liquid-staking/GiantPoolBase.sol
71: require(amountOfTokens > 0, "Empty arrays");
File: liquid-staking/GiantSavETHVaultPool.sol
36: require(numOfSavETHVaults > 0, "Empty arrays");
72: require(numOfVaults > 0, "Empty arrays");
123: require(numOfRotations > 0, "Empty arrays");
143: require(numOfVaults > 0, "Empty arrays");
File: liquid-staking/LiquidStakingManager.sol
386: require(_blsPubKeys.length > 0, "No BLS keys specified");
414: if (daoAmount > 0) {
532: require(numOfValidators > 0, "No data");
583: require(numOfKnotsToProcess > 0, "Empty array");
922: require(_received > 0, "Nothing received");
924: if (daoCommissionPercentage > 0) {
File: liquid-staking/SavETHVault.sol
59: require(numOfValidators > 0, "Empty arrays");
101: require(numOfTokens > 0, "Empty arrays");
114: require(numOfTokens > 0, "Empty arrays");
File: liquid-staking/StakingFundsVault.sol
71: require(numOfValidators > 0, "Empty arrays");
150: require(numOfTokens > 0, "Empty arrays");
164: require(numOfTokens > 0, "Empty arrays");
320: require(blsPubKey.length > 0, "Invalid token");
346: require(KnotAssociatedWithLPToken[token].length > 0, "Invalid token");
File: liquid-staking/SyndicateRewardsProcessor.sol
37: if (_balanceOfSender > 0) {
59: if (balance > 0) {
62: if (due > 0) {
77: if (_numOfShares > 0) {
81: if (unprocessed > 0) {
File: syndicate/Syndicate.sol
177: if (numberOfRegisteredKnots > 0) {
183: if (totalFreeFloatingShares > 0) {
315: if (unclaimedUserShare > 0) {
500: if (unprocessedETHForCurrentKnot > 0 && !isNoLongerPartOfSyndicate[_blsPubKey]) {
551: return totalFreeFloatingShares > 0 ? (_ethSinceLastUpdate * PRECISION) / totalFreeFloatingShares : 0;
588: if (i > 0 && staker < _priorityStakers[i-1]) revert DuplicateArrayElements();
634: lastAccumulatedETHPerFreeFloatingShare[_blsPublicKey] > 0 ?
656: if (unclaimedUserShare > 0) {
[GAS-3] 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 (9):
File: liquid-staking/LSDNFactory.sol
39: mapping(address => bool) public isLiquidStakingManager;
File: liquid-staking/LiquidStakingManager.sol
120: bool public enableWhitelisting;
123: mapping(address => bool) public isNodeRunnerWhitelisted;
149: mapping(address => bool) public bannedNodeRunners;
File: smart-wallet/OwnableSmartWallet.sol
22: mapping(address => mapping(address => bool)) internal _isTransferApproved;
File: smart-wallet/OwnableSmartWalletFactory.sol
20: mapping(address => bool) public walletExists;
File: syndicate/Syndicate.sol
90: mapping(bytes => bool) public isKnotRegistered;
96: mapping(address => bool) public isPriorityStaker;
117: mapping(bytes => bool) public isNoLongerPartOfSyndicate;
[GAS-4] Use Custom Errors
Source Instead of using error strings, to reduce deployment and runtime cost, you should use Custom Errors. This would save both deployment and runtime cost.
Instances (202):
File: liquid-staking/ETHPoolLPFactory.sol
59: require(numOfRotations > 0, "Empty arrays");
60: require(numOfRotations == _newLPTokens.length, "Inconsistent arrays");
61: require(numOfRotations == _amounts.length, "Inconsistent arrays");
77: require(address(_oldLPToken) != address(0), "Zero address");
78: require(address(_newLPToken) != address(0), "Zero address");
79: require(_oldLPToken != _newLPToken, "Incorrect rotation to same token");
80: require(_amount >= MIN_STAKING_AMOUNT, "Amount cannot be zero");
81: require(_amount <= _oldLPToken.balanceOf(msg.sender), "Not enough balance");
82: require(_oldLPToken.lastInteractedTimestamp(msg.sender) + 30 minutes < block.timestamp, "Liquidity is still fresh");
83: require(_amount + _newLPToken.totalSupply() <= 24 ether, "Not enough mintable tokens");
88: require(blsPublicKeyOfPreviousKnot.length == 48, "Incorrect BLS public key");
89: require(blsPublicKeyOfNewKnot.length == 48, "Incorrect BLS public key");
111: require(_amount >= MIN_STAKING_AMOUNT, "Min amount not reached");
112: require(_blsPublicKeyOfKnot.length == 48, "Invalid BLS public key");
122: require(lpToken.totalSupply() + _amount <= maxStakingAmountPerValidator, "Amount exceeds the staking limit for the validator");
130: require(_amount <= maxStakingAmountPerValidator, "Amount exceeds the staking limit for the validator");
File: liquid-staking/GiantLP.sol
30: require(msg.sender == pool, "Only pool");
35: require(msg.sender == pool, "Only pool");
File: liquid-staking/GiantMevAndFeesPool.sol
35: require(numOfVaults > 0, "Zero vaults");
36: require(numOfVaults == _blsPublicKeyOfKnots.length, "Inconsistent lengths");
37: require(numOfVaults == _amounts.length, "Inconsistent lengths");
62: require(numOfVaults > 0, "Empty array");
63: require(numOfVaults == _blsPublicKeysForKnots.length, "Inconsistent array lengths");
87: require(_stakingFundsVaults.length == _lpTokens.length, "Inconsistent array lengths");
112: require(numOfRotations > 0, "Empty arrays");
113: require(numOfRotations == _oldLPTokens.length, "Inconsistent arrays");
114: require(numOfRotations == _newLPTokens.length, "Inconsistent arrays");
115: require(numOfRotations == _amounts.length, "Inconsistent arrays");
116: require(lpTokenETH.balanceOf(msg.sender) >= 0.5 ether, "No common interest");
132: require(numOfVaults > 0, "Empty arrays");
133: require(numOfVaults == _lpTokens.length, "Inconsistent arrays");
134: require(numOfVaults == _amounts.length, "Inconsistent arrays");
147: require(msg.sender == address(lpTokenETH), "Caller is not giant LP");
171: require(msg.sender == address(lpTokenETH), "Caller is not giant LP");
File: liquid-staking/GiantPoolBase.sol
35: require(msg.value >= MIN_STAKING_AMOUNT, "Minimum not supplied");
36: require(msg.value == _amount, "Value equal to amount");
53: require(_amount >= MIN_STAKING_AMOUNT, "Invalid amount");
54: require(lpTokenETH.balanceOf(msg.sender) >= _amount, "Invalid balance");
55: require(idleETH >= _amount, "Come back later or withdraw less ETH");
61: require(success, "Failed to transfer ETH");
71: require(amountOfTokens > 0, "Empty arrays");
72: require(amountOfTokens == _amounts.length, "Inconsistent array lengths");
94: require(_amount >= MIN_STAKING_AMOUNT, "Invalid amount");
95: require(_token.balanceOf(address(this)) >= _amount, "Pool does not own specified LP");
96: require(lpTokenETH.lastInteractedTimestamp(msg.sender) + 1 days < block.timestamp, "Too new");
File: liquid-staking/GiantSavETHVaultPool.sol
36: require(numOfSavETHVaults > 0, "Empty arrays");
37: require(numOfSavETHVaults == _ETHTransactionAmounts.length, "Inconsistent array lengths");
38: require(numOfSavETHVaults == _blsPublicKeys.length, "Inconsistent array lengths");
39: require(numOfSavETHVaults == _stakeAmounts.length, "Inconsistent array lengths");
72: require(numOfVaults > 0, "Empty arrays");
73: require(numOfVaults == _lpTokens.length, "Inconsistent arrays");
74: require(numOfVaults == _amounts.length, "Inconsistent arrays");
89: require(vault.isDETHReadyForWithdrawal(address(token)), "dETH is not ready for withdrawal");
92: require(lpTokenETH.balanceOf(msg.sender) >= amount, "User does not own enough LP");
123: require(numOfRotations > 0, "Empty arrays");
124: require(numOfRotations == _oldLPTokens.length, "Inconsistent arrays");
125: require(numOfRotations == _newLPTokens.length, "Inconsistent arrays");
126: require(numOfRotations == _amounts.length, "Inconsistent arrays");
127: require(lpTokenETH.balanceOf(msg.sender) >= 0.5 ether, "No common interest");
143: require(numOfVaults > 0, "Empty arrays");
144: require(numOfVaults == _lpTokens.length, "Inconsistent arrays");
145: require(numOfVaults == _amounts.length, "Inconsistent arrays");
File: liquid-staking/LPToken.sol
23: require(msg.sender == deployer, "Only savETH vault");
File: liquid-staking/LPTokenFactory.sol
19: require(_lpTokenImplementation != address(0), "Address cannot be zero");
33: require(address(_deployer) != address(0), "Zero address");
34: require(bytes(_tokenSymbol).length != 0, "Symbol cannot be zero");
35: require(bytes(_tokenName).length != 0, "Name cannot be zero");
File: liquid-staking/LSDNFactory.sol
51: require(_liquidStakingManagerImplementation != address(0), "Zero Address");
52: require(_syndicateFactory != address(0), "Zero Address");
53: require(_lpTokenFactory != address(0), "Zero Address");
54: require(_smartWalletFactory != address(0), "Zero Address");
55: require(_brand != address(0), "Zero Address");
56: require(_savETHVaultDeployer != address(0), "Zero Address");
57: require(_stakingFundsVaultDeployer != address(0), "Zero Address");
58: require(_optionalGatekeeperDeployer != address(0), "Zero Address");
File: liquid-staking/LiquidStakingManager.sol
161: require(msg.sender == dao, "Must be DAO");
209: require(smartWallet != address(0), "No wallet found");
240: require(_newAddress != address(0), "Zero address");
241: require(_newAddress != dao, "Same address");
250: require(_commissionPercentage != daoCommissionPercentage, "Same commission percentage");
256: require(bytes(_newTicker).length >= 3, "String must be 3-5 characters long");
257: require(bytes(_newTicker).length <= 5, "String must be 3-5 characters long");
258: require(numberOfKnots == 0, "Cannot change ticker once house is created");
268: require(_changeWhitelist != enableWhitelisting, "Unnecessary update to same status");
279: require(_nodeRunner != address(0), "Zero address");
280: require(isNodeRunnerWhitelisted[_nodeRunner] != isNodeRunnerWhitelisted[_nodeRunner], "Unnecessary update to same status");
290: require(_newRepresentative != address(0), "Zero address");
291: require(isNodeRunnerBanned(msg.sender) == false, "Node runner is banned from LSD network");
294: require(smartWallet != address(0), "No smart wallet");
295: require(stakedKnotsOfSmartWallet[smartWallet] == 0, "Not all KNOTs are minted");
296: require(smartWalletRepresentative[smartWallet] != _newRepresentative, "Invalid rotation to same EOA");
309: require(_newRepresentative != address(0), "Zero address");
312: require(smartWallet != address(0), "No smart wallet");
313: require(stakedKnotsOfSmartWallet[smartWallet] == 0, "Not all KNOTs are minted");
314: require(smartWalletRepresentative[smartWallet] != _newRepresentative, "Invalid rotation to same EOA");
327: require(_recipient != address(0), "Zero address");
328: require(isBLSPublicKeyBanned(_blsPublicKeyOfKnot) == false, "BLS public key has already withdrawn or not a part of LSD network");
331: require(smartWalletOfNodeRunner[msg.sender] == associatedSmartWallet, "Not the node runner for the smart wallet ");
332: require(isNodeRunnerBanned(nodeRunnerOfSmartWallet[associatedSmartWallet]) == false, "Node runner is banned from LSD network");
333: require(associatedSmartWallet.balance >= 4 ether, "Insufficient balance");
357: require(_new != address(0) && _current != _new, "New is zero or current");
360: require(wallet != address(0), "Wallet does not exist");
361: require(_current == msg.sender || dao == msg.sender, "Not current owner or DAO");
364: require(newRunnerCurrentWallet == address(0), "New runner has a wallet");
386: require(_blsPubKeys.length > 0, "No BLS keys specified");
387: require(_recipient != address(0), "Zero address");
390: require(smartWallet != address(0), "Unknown node runner");
393: require(isBLSPublicKeyBanned(_blsPubKeys[i]) == false, "BLS public key is banned or not a part of LSD network");
396: require(smartWalletOfKnot[_blsPubKeys[i]] == smartWallet, "BLS public key doesn't belong to the node runner");
412: require(transferResult, "Failed to transfer");
416: require(transferResult, "Failed to transfer");
432: require(len >= 1, "No value provided");
433: require(len == _blsSignatures.length, "Unequal number of array values");
434: require(msg.value == len * 4 ether, "Insufficient ether provided");
435: require(!Address.isContract(_eoaRepresentative), "Only EOA representative permitted");
436: require(_isNodeRunnerValid(msg.sender) == true, "Unrecognised node runner");
437: require(isNodeRunnerBanned(msg.sender) == false, "Node runner is banned from LSD network");
455: require(smartWalletRepresentative[smartWallet] == _eoaRepresentative, "Different EOA specified - rotate outside");
461: require(result, "Transfer failed");
469: require(isBLSPublicKeyPartOfLSDNetwork(_blsPublicKey) == false, "BLS public key is banned or not a part of LSD network");
532: require(numOfValidators > 0, "No data");
533: require(numOfValidators == _ciphertexts.length, "Inconsistent array lengths");
534: require(numOfValidators == _aesEncryptorKeys.length, "Inconsistent array lengths");
535: require(numOfValidators == _encryptionSignatures.length, "Inconsistent array lengths");
536: require(numOfValidators == _dataRoots.length, "Inconsistent array lengths");
541: require(isBLSPublicKeyBanned(blsPubKey) == false, "BLS public key is banned or not a part of LSD network");
544: require(associatedSmartWallet != address(0), "Unknown BLS public key");
583: require(numOfKnotsToProcess > 0, "Empty array");
584: require(numOfKnotsToProcess == _beaconChainBalanceReports.length, "Inconsistent array lengths");
585: require(numOfKnotsToProcess == _reportSignatures.length, "Inconsistent array lengths");
589: require(isBLSPublicKeyBanned(_blsPublicKeyOfKnots[i]) == false, "BLS public key is banned or not a part of LSD network");
658: require(_dao != address(0), "Zero address");
659: require(_syndicateFactory != address(0), "Zero address");
660: require(_smartWalletFactory != address(0), "Zero address");
661: require(_brand != address(0), "Zero address");
662: require(bytes(_stakehouseTicker).length >= 3, "String must be 3-5 characters long");
663: require(bytes(_stakehouseTicker).length <= 5, "String must be 3-5 characters long");
685: require(_nodeRunner != address(0), "Zero address");
688: require(isNodeRunnerWhitelisted[_nodeRunner] == true, "Invalid node runner");
734: revert("Unexpected state");
922: require(_received > 0, "Nothing received");
936: require(associatedSmartWallet.balance >= 4 ether, "Smart wallet balance must be at least 4 ether");
939: require(address(stakingFundsLP) != address(0), "No funds staked in staking funds vault");
940: require(stakingFundsLP.totalSupply() == 4 ether, "DAO staking funds vault balance must be at least 4 ether");
943: require(address(savETHVaultLP) != address(0), "No funds staked in savETH vault");
944: require(savETHVaultLP.totalSupply() == 24 ether, "KNOT must have 24 ETH in savETH vault");
949: require(_commissionPercentage <= MODULO, "Invalid commission");
File: liquid-staking/SavETHVault.sol
50: require(msg.sender == address(liquidStakingManager), "Not the savETH vault manager");
59: require(numOfValidators > 0, "Empty arrays");
60: require(numOfValidators == _amounts.length, "Inconsistent array lengths");
64: require(liquidStakingManager.isBLSPublicKeyBanned(_blsPublicKeyOfKnots[i]) == false, "BLS public key is not part of LSD network");
76: require(msg.value == totalAmount, "Invalid ETH amount attached");
84: require(liquidStakingManager.isBLSPublicKeyBanned(_blsPublicKeyOfKnot) == false, "BLS public key is banned or not a part of LSD network");
90: require(msg.value == _amount, "Must provide correct amount of ETH");
101: require(numOfTokens > 0, "Empty arrays");
102: require(numOfTokens == _amounts.length, "Inconsistent array length");
114: require(numOfTokens > 0, "Empty arrays");
115: require(numOfTokens == _amounts.length, "Inconsisent array length");
127: require(_amount >= MIN_STAKING_AMOUNT, "Amount cannot be zero");
128: require(_amount <= _lpToken.balanceOf(msg.sender), "Not enough balance");
161: require(dETHBalance >= 24 ether, "Nothing to withdraw");
186: require(isStaleLiquidity, "Liquidity is still fresh");
190: require(result, "Transfer failed");
204: require(_amount >= 24 ether, "Amount cannot be less than 24 ether");
205: require(address(this).balance >= _amount, "Insufficient withdrawal amount");
206: require(_smartWallet != address(0), "Zero address");
207: require(_smartWallet != address(this), "This address");
210: require(result, "Transfer failed");
236: require(_liquidStakingManagerAddress != address(0), "Zero address");
237: require(address(_lpTokenFactory) != address(0), "Zero address");
File: liquid-staking/StakingFundsVault.sol
51: require(msg.sender == address(liquidStakingNetworkManager), "Only network manager");
71: require(numOfValidators > 0, "Empty arrays");
72: require(numOfValidators == _amounts.length, "Inconsistent array lengths");
79: require(liquidStakingNetworkManager.isBLSPublicKeyBanned(_blsPublicKeyOfKnots[i]) == false, "BLS public key is not part of LSD network");
107: require(msg.value == totalAmount, "Invalid ETH amount attached");
114: require(liquidStakingNetworkManager.isBLSPublicKeyBanned(_blsPublicKeyOfKnot) == false, "BLS public key is banned or not a part of LSD network");
120: require(msg.value == _amount, "Must provide correct amount of ETH");
150: require(numOfTokens > 0, "Empty arrays");
151: require(numOfTokens == _amounts.length, "Inconsistent array length");
154: require(address(token) != address(0), "No ETH staked for specified BLS key");
164: require(numOfTokens > 0, "Empty arrays");
165: require(numOfTokens == _amounts.length, "Inconsistent array length");
175: require(_amount >= MIN_STAKING_AMOUNT, "Amount cannot be zero");
176: require(_amount <= _lpToken.balanceOf(msg.sender), "Not enough balance");
177: require(address(_lpToken) != address(0), "Zero address specified");
184: require(_lpToken.lastInteractedTimestamp(msg.sender) + 30 minutes < block.timestamp, "Too new");
191: require(result, "Transfer failed");
229: require(address(token) != address(0), "Invalid BLS key");
230: require(token.lastInteractedTimestamp(msg.sender) + 30 minutes < block.timestamp, "Last transfer too recent");
240: require(_amount >= 4 ether, "Amount cannot be less than 4 ether");
241: require(_amount <= address(this).balance, "Not enough ETH to withdraw");
242: require(_wallet != address(0), "Zero address");
245: require(result, "Transfer failed");
267: require(syndicate.isNoLongerPartOfSyndicate(_blsPublicKeys[i]), "Knot is still active in syndicate");
320: require(blsPubKey.length > 0, "Invalid token");
346: require(KnotAssociatedWithLPToken[token].length > 0, "Invalid token");
361: require(_syndicate != address(0), "Invalid configuration");
372: require(address(_liquidStakingNetworkManager) != address(0), "Zero Address");
373: require(address(_lpTokenFactory) != address(0), "Zero Address");
File: liquid-staking/SyndicateRewardsProcessor.sol
57: require(_recipient != address(0), "Zero address");
68: require(success, "Failed to transfer");
File: smart-wallet/OwnableSmartWallet.sol
79: require(result, "Failed to execute");
[GAS-5] ++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: liquid-staking/ETHPoolLPFactory.sol
144: numberOfLPTokensIssued++;
[GAS-6] 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 (4):
File: liquid-staking/ETHPoolLPFactory.sol
38: uint256 public constant MIN_STAKING_AMOUNT = 0.001 ether;
File: liquid-staking/GiantPoolBase.sol
22: uint256 public constant MIN_STAKING_AMOUNT = 0.001 ether;
File: liquid-staking/SyndicateRewardsProcessor.sol
15: uint256 public constant PRECISION = 1e24;
File: syndicate/Syndicate.sol
66: uint256 public constant PRECISION = 1e24;
Low Issues
Issue | Instances | |
---|---|---|
[L-1] | Unsafe ERC20 operation(s) | 6 |
[L-1] Unsafe ERC20 operation(s)
Instances (6):
File: liquid-staking/GiantMevAndFeesPool.sol
184: _lpTokens[i].transfer(address(this), _lpTokens[i].balanceOf(address(this)));
File: liquid-staking/GiantPoolBase.sol
86: token.transfer(msg.sender, amount);
File: liquid-staking/GiantSavETHVaultPool.sol
108: getDETH().transfer(msg.sender, dETHReceivedFromAllSavETHVaults);
File: liquid-staking/LiquidStakingManager.sol
870: sETH.approve(syndicate, (2 ** 256) - 1);
File: syndicate/Syndicate.sol
233: bool transferResult = sETH.transferFrom(msg.sender, address(this), _sETHAmount);
275: bool transferResult = sETH.transfer(_sETHRecipient, _sETHAmount);