Lodestar-Finance / lodestar-protocol

Houses the code for the Lodestar Finance DeFi protocol.
BSD 3-Clause "New" or "Revised" License
10 stars 7 forks source link

Gas Optimisations Report #29

Open rajatbeladiya opened 1 year ago

rajatbeladiya commented 1 year ago

Report

Gas Optimizations

Issue Instances
GAS-1 Comparing booleans to true or false 11
GAS-2 Using bools for storage incurs overhead 22
GAS-3 Bytes constants are more efficient than string constants 4
GAS-4 Cache array length outside of loop 16
GAS-5 State variables should be cached in stack variables rather than re-reading them from storage 1
GAS-6 Use Custom Errors 169
GAS-7 Don't initialize variables with default value 33
GAS-8 Long revert strings 117
GAS-9 Pre-increments and pre-decrements are cheaper than post-increments and post-decrements 29
GAS-10 Using private rather than public for constants, saves gas 60
GAS-11 Use shift Right/Left instead of division/multiplication if possible 5
GAS-12 Incrementing with a smaller type than uint256 incurs overhead 1
GAS-13 Splitting require() statements that use && saves gas 16
GAS-14 Use storage instead of memory for structs/arrays 81
GAS-15 Increments can be unchecked in for-loops 28
GAS-16 Use != 0 instead of > 0 for unsigned integer comparison 26
GAS-17 <x> += <y> costs more gas than <x> = <x> + <y> for state variables 2
GAS-18 Using immutable on variables that are only set in the constructor and never after 1

[GAS-1] Comparing booleans to true or false

true and false are constants and it is more expensive comparing a boolean against them than directly checking the returned boolean value

Instances (11):

File: lodestar-protocol/contracts/Comptroller.sol

165:         if (marketToJoin.accountMembership[borrower] == true) {

1173:         require(msg.sender == admin || state == true, "only admin can unpause");

1183:         require(msg.sender == admin || state == true, "only admin can unpause");

1192:         require(msg.sender == admin || state == true, "only admin can unpause");

1201:         require(msg.sender == admin || state == true, "only admin can unpause");

1428:             if (borrowers == true) {

1435:             if (suppliers == true) {

1537:             borrowGuardianPaused[address(cToken)] == true &&
File: lodestar-protocol/contracts/Governance/GovernorAlpha.sol

266:         require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");
File: lodestar-protocol/contracts/Governance/GovernorBravoDelegate.sol

275:         require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
File: lodestar-protocol/contracts/Oracle/PriceOracleProxyETH.sol

108:             if (sequencerStatus == false) {

[GAS-2] 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 (22):

File: lodestar-protocol/contracts/CTokenInterfaces.sol

13:     bool internal _notEntered;

118:     bool public constant isCToken = true;
File: lodestar-protocol/contracts/ComptrollerInterface.sol

6:     bool public constant isComptroller = true;
File: lodestar-protocol/contracts/ComptrollerStorage.sol

65:         mapping(address => bool) accountMembership;

82:     bool public _mintGuardianPaused;

83:     bool public _borrowGuardianPaused;

84:     bool public transferGuardianPaused;

85:     bool public seizeGuardianPaused;

86:     mapping(address => bool) public mintGuardianPaused;

87:     mapping(address => bool) public borrowGuardianPaused;

149:     bool public proposal65FixExecuted;
File: lodestar-protocol/contracts/InterestRateModel.sol

10:     bool public constant isInterestRateModel = true;
File: lodestar-protocol/contracts/Oracle/Interfaces/PlvGLPOracleInterface.sol

5:     bool public constant isGLPOracle = true;
File: lodestar-protocol/contracts/Oracle/Interfaces/SushiOracleInterface.sol

5:     bool public constant isSushiOracle = true;
File: lodestar-protocol/contracts/Oracle/PriceOracle.sol

6:     bool public constant isPriceOracle = true;
File: lodestar-protocol/contracts/Oracle/PriceOracleProxyETH.sol

19:     bool public constant isPriceOracle = true;
File: lodestar-protocol/contracts/Oracle/SushiOracle.sol

9:     bool public constant isSushiOracle = true;
File: lodestar-protocol/contracts/PriceOracle.sol

8:     bool public constant isPriceOracle = true;
File: lodestar-protocol/contracts/Timelock.sol

24:     mapping (bytes32 => bool) public queuedTransactions;
File: lodestar-protocol/contracts/Whitelist.sol

8:     mapping(address => bool) public isWhitelisted;
File: plvglp-oracle/contracts/Whitelist.sol

7:     mapping(address => bool) public isWhitelisted;
File: plvglp-oracle/contracts/plvGLPOracle.sol

31:     bool public constant isGLPOracle = true;

[GAS-3] Bytes constants are more efficient than string constants

Instances (4):

File: lodestar-protocol/contracts/Governance/Comp.sol

6:     string public constant name = "Lodestar";

9:     string public constant symbol = "LODE";
File: lodestar-protocol/contracts/Governance/GovernorAlpha.sol

6:     string public constant name = "Compound Governor Alpha";
File: lodestar-protocol/contracts/Governance/GovernorBravoDelegate.sol

9:     string public constant name = "Lodestar Governor Bravo";

[GAS-4] 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: lodestar-protocol/contracts/Comptroller.sol

794:         for (uint i = 0; i < assets.length; i++) {

1037:         for (uint i = 0; i < allMarkets.length; i++) {

1425:         for (uint i = 0; i < cTokens.length; i++) {

1431:                 for (uint j = 0; j < holders.length; j++) {

1437:                 for (uint j = 0; j < holders.length; j++) {

1442:         for (uint j = 0; j < holders.length; j++) {
File: lodestar-protocol/contracts/Governance/GovernorAlpha.sol

181:         for (uint i = 0; i < proposal.targets.length; i++) {

197:         for (uint i = 0; i < proposal.targets.length; i++) {

211:         for (uint i = 0; i < proposal.targets.length; i++) {
File: lodestar-protocol/contracts/Governance/GovernorBravoDelegate.sol

127:         for (uint i = 0; i < proposal.targets.length; i++) {

147:         for (uint i = 0; i < proposal.targets.length; i++) {

174:         for (uint i = 0; i < proposal.targets.length; i++) {
File: lodestar-protocol/contracts/Lens/CompoundLens.sol

380:         for (uint i = 0; i < proposalIds.length; i++) {

448:         for (uint i = 0; i < proposalIds.length; i++) {

532:         for (uint i = 0; i < blockNumbers.length; i++) {
File: lodestar-protocol/contracts/Oracle/PriceOracleProxyETH.sol

237:         for (uint256 i = 0; i < cTokenAddresses.length; i++) {

[GAS-5] State variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second+ access of a state variable within a function. Caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.

Saves 100 gas per instance

Instances (1):

File: lodestar-token-fix/contracts/TokenFIx.sol

51:         emit adminUpdated(oldAdmin, admin);

[GAS-6] 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 (169):

File: lodestar-protocol/contracts/BaseJumpRateModelV2.sol

68:         require(msg.sender == owner, "only the owner may call this function.");
File: lodestar-protocol/contracts/CErc20.sol

154:         require(msg.sender == admin, "CErc20::sweepToken: only admin can sweep tokens");

155:         require(address(token) != underlying, "CErc20::sweepToken: can not sweep underlying token");

214:         require(success, "TOKEN_TRANSFER_IN_FAILED");

251:         require(success, "TOKEN_TRANSFER_OUT_FAILED");

260:         require(msg.sender == admin, "only the admin may set the comp-like delegate");
File: lodestar-protocol/contracts/CErc20Delegate.sol

30:         require(msg.sender == admin, "only the admin may call _becomeImplementation");

42:         require(msg.sender == admin, "only the admin may call _resignImplementation");
File: lodestar-protocol/contracts/CErc20Delegator.sol

73:         require(msg.sender == admin, "CErc20Delegator::_setImplementation: Caller must be admin");

532:         require(msg.value == 0, "CErc20Delegator:fallback: cannot send value to fallback");
File: lodestar-protocol/contracts/CEther.sol

157:         require(msg.sender == from, "sender mismatch");

158:         require(msg.value == amount, "value mismatch");
File: lodestar-protocol/contracts/CToken.sol

35:         require(msg.sender == admin, "only admin may initialize the market");

36:         require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once");

40:         require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero.");

44:         require(err == NO_ERROR, "setting comptroller failed");

52:         require(err == NO_ERROR, "setting interest rate model failed");

343:         require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high");

473:         require(Whitelist(0x67E57A0ec37768eaF99a364975ec4E1f98920D01).isWhitelisted(msg.sender), "NOT_AUTHORIZED");

498:         require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero");

585:         require(Whitelist(0xB371BB8c9073E84FD54e0d8697816329397E743b).isWhitelisted(msg.sender), "NOT_AUTHORIZED");

811:         require(amountSeizeError == NO_ERROR, "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED");

814:         require(cTokenCollateral.balanceOf(borrower) >= seizeTokens, "LIQUIDATE_SEIZE_TOO_MUCH");

820:             require(cTokenCollateral.seize(liquidator, borrower, seizeTokens) == NO_ERROR, "token seizure failed");

960:         require(newComptroller.isComptroller(), "marker method returned false");

1160:         require(newInterestRateModel.isInterestRateModel(), "marker method returned false");

1199:         require(_notEntered, "re-entered");
File: lodestar-protocol/contracts/Comptroller.sol

194:         require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code

253:         require(!mintGuardianPaused[cToken], "mint is paused");

272:             require(nextTotalSupply < supplyCap, "market supply cap reached");

363:             revert("redeemTokens zero");

376:         require(!borrowGuardianPaused[cToken], "borrow is paused");

384:             require(msg.sender == cToken, "sender must be cToken");

405:             require(nextTotalBorrows < borrowCap, "market borrow cap reached");

531:             require(borrowBalance >= repayAmount, "Can not repay more than the total borrow");

598:         require(!seizeGuardianPaused, "seize is paused");

662:         require(!transferGuardianPaused, "transfer is paused");

930:         require(msg.sender == admin, "only admin can set close factor");

1038:             require(allMarkets[i] != CToken(cToken), "market already added");

1083:         require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input");

1096:         require(msg.sender == admin, "only admin can set borrow cap guardian");

1123:         require(numMarkets != 0 && numMarkets == numSupplyCaps, "invalid input");

1136:         require(msg.sender == admin, "only admin can set borrow cap guardian");

1171:         require(markets[address(cToken)].isListed, "cannot pause a market that is not listed");

1172:         require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");

1173:         require(msg.sender == admin || state == true, "only admin can unpause");

1181:         require(markets[address(cToken)].isListed, "cannot pause a market that is not listed");

1182:         require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");

1183:         require(msg.sender == admin || state == true, "only admin can unpause");

1191:         require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");

1192:         require(msg.sender == admin || state == true, "only admin can unpause");

1200:         require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");

1201:         require(msg.sender == admin || state == true, "only admin can unpause");

1209:         require(msg.sender == unitroller.admin(), "only unitroller admin can change brains");

1210:         require(unitroller._acceptImplementation() == 0, "change not authorized");

1230:         require(market.isListed, "comp market is not listed");

1427:             require(markets[address(cToken)].isListed, "market must be listed");

1473:         require(adminOrInitializing(), "only admin can grant comp");

1475:         require(amountLeft == 0, "insufficient comp for grant");

1486:         require(adminOrInitializing(), "only admin can set comp speed");

1505:         require(adminOrInitializing(), "only admin can set comp speed");
File: lodestar-protocol/contracts/Governance/Comp.sol

166:         require(signatory != address(0), "Comp::delegateBySig: invalid signature");

167:         require(nonce == nonces[signatory]++, "Comp::delegateBySig: invalid nonce");

168:         require(block.timestamp <= expiry, "Comp::delegateBySig: signature expired");

190:         require(blockNumber < block.number, "Comp::getPriorVotes: not yet determined");

234:         require(src != address(0), "Comp::_transferTokens: cannot transfer from the zero address");

235:         require(dst != address(0), "Comp::_transferTokens: cannot transfer to the zero address");
File: lodestar-protocol/contracts/Governance/GovernorAlpha.sol

137:         require(comp.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(), "GovernorAlpha::propose: proposer votes below proposal threshold");

138:         require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorAlpha::propose: proposal function information arity mismatch");

139:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");

140:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");

145:           require(proposersLatestProposalState != ProposalState.Active, "GovernorAlpha::propose: one live proposal per proposer, found an already active proposal");

146:           require(proposersLatestProposalState != ProposalState.Pending, "GovernorAlpha::propose: one live proposal per proposer, found an already pending proposal");

156:         require(newProposal.id == 0, "GovernorAlpha::propose: ProposalID collsion");

178:         require(state(proposalId) == ProposalState.Succeeded, "GovernorAlpha::queue: proposal can only be queued if it is succeeded");

189:         require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorAlpha::_queueOrRevert: proposal action already queued at eta");

194:         require(state(proposalId) == ProposalState.Queued, "GovernorAlpha::execute: proposal can only be executed if it is queued");

205:         require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");

208:         require(msg.sender == guardian || comp.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(), "GovernorAlpha::cancel: proposer above threshold");

228:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");

258:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");

263:         require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed");

266:         require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");

283:         require(msg.sender == guardian, "GovernorAlpha::__acceptAdmin: sender must be gov guardian");

288:         require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian");

293:         require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian");

298:         require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian");

304:         require(c >= a, "addition overflow");

309:         require(b <= a, "subtraction underflow");
File: lodestar-protocol/contracts/Governance/GovernorBravoDelegate.sol

50:         require(address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once");

51:         require(msg.sender == admin, "GovernorBravo::initialize: admin only");

52:         require(timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address");

53:         require(comp_ != address(0), "GovernorBravo::initialize: invalid comp address");

54:         require(votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, "GovernorBravo::initialize: invalid voting period");

55:         require(votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, "GovernorBravo::initialize: invalid voting delay");

56:         require(proposalThreshold_ >= MIN_PROPOSAL_THRESHOLD && proposalThreshold_ <= MAX_PROPOSAL_THRESHOLD, "GovernorBravo::initialize: invalid proposal threshold");

76:         require(initialProposalId != 0, "GovernorBravo::propose: Governor Bravo not active");

78:         require(comp.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold || isWhitelisted(msg.sender), "GovernorBravo::propose: proposer votes below proposal threshold");

79:         require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorBravo::propose: proposal function information arity mismatch");

80:         require(targets.length != 0, "GovernorBravo::propose: must provide actions");

81:         require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");

86:           require(proposersLatestProposalState != ProposalState.Active, "GovernorBravo::propose: one live proposal per proposer, found an already active proposal");

87:           require(proposersLatestProposalState != ProposalState.Pending, "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal");

97:         require(newProposal.id == 0, "GovernorBravo::propose: ProposalID collsion");

124:         require(state(proposalId) == ProposalState.Succeeded, "GovernorBravo::queue: proposal can only be queued if it is succeeded");

135:         require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta");

144:         require(state(proposalId) == ProposalState.Queued, "GovernorBravo::execute: proposal can only be executed if it is queued");

158:         require(state(proposalId) != ProposalState.Executed, "GovernorBravo::cancel: cannot cancel executed proposal");

166:                 require((comp.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold) && msg.sender == whitelistGuardian, "GovernorBravo::cancel: whitelisted proposer");

169:                 require((comp.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold), "GovernorBravo::cancel: proposer above threshold");

210:         require(proposalCount >= proposalId && proposalId > initialProposalId, "GovernorBravo::state: invalid proposal id");

259:         require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature");

271:         require(state(proposalId) == ProposalState.Active, "GovernorBravo::castVoteInternal: voting is closed");

272:         require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");

275:         require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");

307:         require(msg.sender == admin, "GovernorBravo::_setVotingDelay: admin only");

308:         require(newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, "GovernorBravo::_setVotingDelay: invalid voting delay");

320:         require(msg.sender == admin, "GovernorBravo::_setVotingPeriod: admin only");

321:         require(newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, "GovernorBravo::_setVotingPeriod: invalid voting period");

334:         require(msg.sender == admin, "GovernorBravo::_setProposalThreshold: admin only");

335:         require(newProposalThreshold >= MIN_PROPOSAL_THRESHOLD && newProposalThreshold <= MAX_PROPOSAL_THRESHOLD, "GovernorBravo::_setProposalThreshold: invalid proposal threshold");

348:         require(msg.sender == admin || msg.sender == whitelistGuardian, "GovernorBravo::_setWhitelistAccountExpiration: admin only");

359:         require(msg.sender == admin, "GovernorBravo::_setWhitelistGuardian: admin only");

372:         require(msg.sender == admin, "GovernorBravo::_initiate: admin only");

373:         require(initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once");

386:         require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only");

404:         require(msg.sender == pendingAdmin && msg.sender != address(0), "GovernorBravo:_acceptAdmin: pending admin only");

422:         require(c >= a, "addition overflow");

427:         require(b <= a, "subtraction underflow");
File: lodestar-protocol/contracts/Governance/GovernorBravoDelegator.sol

37:         require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only");

38:         require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address");
File: lodestar-protocol/contracts/Oracle/PriceOracleProxyETH.sol

110:                 revert("Chainlink feeds are not being updated");

121:         revert("Invalid Oracle Request");

133:         require(price > 0, "invalid price");

145:         require(price > 0, "invalid price");

187:         require(msg.sender == admin, "only the admin may set new guardian");

197:         require(msg.sender == admin, "only the admin may set new admin");

207:         require(msg.sender == admin, "only the admin may set new LODE Oracle");

208:         require(_newLodeOracle.isSushiOracle(), "Invalid Contract");

218:         require(msg.sender == admin, "only the admin may set new GLP Oracle");

219:         require(_newGlpOracle.isGLPOracle(), "Invalid Contract");

235:         require(msg.sender == admin || msg.sender == guardian, "only the admin or guardian may set the aggregators");

236:         require(cTokenAddresses.length == sources.length && cTokenAddresses.length == bases.length, "mismatched data");

239:                 require(msg.sender == admin, "Only the admin or guardian can clear the aggregators");
File: lodestar-protocol/contracts/Oracle/SushiOracle.sol

45:         require(msg.sender == admin, "Only the admin can update the pool contract.");

51:         require(msg.sender == admin, "Only the admin can update the admin");
File: lodestar-protocol/contracts/SafeMath.sol

32:         require(c >= a, "SafeMath: addition overflow");

98:         require(c / a == b, "SafeMath: multiplication overflow");
File: lodestar-protocol/contracts/Timelock.sol

28:         require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay.");

29:         require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");

38:         require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock.");

39:         require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay.");

40:         require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");

47:         require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin.");

55:         require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock.");

62:         require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin.");

63:         require(eta >= getBlockTimestamp().add(delay), "Timelock::queueTransaction: Estimated execution block must satisfy delay.");

73:         require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin.");

82:         require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");

85:         require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");

86:         require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");

87:         require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale.");

101:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");
File: lodestar-token-fix/contracts/TokenFIx.sol

33:             revert("Swap amount exceeds balance");

48:         require(msg.sender == admin, "Only the admin may update the admin");

55:         require(msg.sender == admin, "Only the admin may transfer tokens out");

61:         require(msg.sender == admin, "Only the admin may transfer tokens out");
File: plvglp-oracle/contracts/plvGLPOracle.sol

51:         require(index > 0, "First index cannot be zero.");

145:         require(Whitelist(whitelist).isWhitelisted(msg.sender), "NOT_AUTHORIZED");

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

Instances (33):

File: lodestar-protocol/contracts/CToken.sol

84:         uint startingAllowance = 0;
File: lodestar-protocol/contracts/Comptroller.sol

142:         for (uint i = 0; i < len; i++) {

222:         for (uint i = 0; i < len; i++) {

794:         for (uint i = 0; i < assets.length; i++) {

1037:         for (uint i = 0; i < allMarkets.length; i++) {

1085:         for (uint i = 0; i < numMarkets; i++) {

1125:         for (uint i = 0; i < numMarkets; i++) {

1425:         for (uint i = 0; i < cTokens.length; i++) {

1431:                 for (uint j = 0; j < holders.length; j++) {

1437:                 for (uint j = 0; j < holders.length; j++) {

1442:         for (uint j = 0; j < holders.length; j++) {

1494:         for (uint i = 0; i < numTokens; ++i) {
File: lodestar-protocol/contracts/ErrorReporter.sol

75:     uint public constant NO_ERROR = 0; // support legacy return codes
File: lodestar-protocol/contracts/Governance/Comp.sol

207:         uint32 lower = 0;
File: lodestar-protocol/contracts/Governance/GovernorAlpha.sol

181:         for (uint i = 0; i < proposal.targets.length; i++) {

197:         for (uint i = 0; i < proposal.targets.length; i++) {

211:         for (uint i = 0; i < proposal.targets.length; i++) {
File: lodestar-protocol/contracts/Governance/GovernorBravoDelegate.sol

127:         for (uint i = 0; i < proposal.targets.length; i++) {

147:         for (uint i = 0; i < proposal.targets.length; i++) {

174:         for (uint i = 0; i < proposal.targets.length; i++) {
File: lodestar-protocol/contracts/Lens/CompoundLens.sol

95:         uint compSupplySpeed = 0;

103:         uint compBorrowSpeed = 0;

127:         uint borrowCap = 0;

191:         for (uint i = 0; i < cTokenCount; i++) {

240:         for (uint i = 0; i < cTokenCount; i++) {

262:         for (uint i = 0; i < cTokenCount; i++) {

298:         for (uint i = 0; i < proposalCount; i++) {

324:         for (uint i = 0; i < proposalCount; i++) {

380:         for (uint i = 0; i < proposalIds.length; i++) {

448:         for (uint i = 0; i < proposalIds.length; i++) {

532:         for (uint i = 0; i < blockNumbers.length; i++) {
File: lodestar-protocol/contracts/Oracle/PriceOracleProxyETH.sol

237:         for (uint256 i = 0; i < cTokenAddresses.length; i++) {
File: plvglp-oracle/contracts/plvGLPOracle.sol

95:             for (uint256 i = 0; i < latestIndexing; i++) {

[GAS-8] Long revert strings

Instances (117):

File: lodestar-protocol/contracts/BaseJumpRateModelV2.sol

68:         require(msg.sender == owner, "only the owner may call this function.");
File: lodestar-protocol/contracts/CErc20.sol

154:         require(msg.sender == admin, "CErc20::sweepToken: only admin can sweep tokens");

155:         require(address(token) != underlying, "CErc20::sweepToken: can not sweep underlying token");

260:         require(msg.sender == admin, "only the admin may set the comp-like delegate");
File: lodestar-protocol/contracts/CErc20Delegate.sol

30:         require(msg.sender == admin, "only the admin may call _becomeImplementation");

42:         require(msg.sender == admin, "only the admin may call _resignImplementation");
File: lodestar-protocol/contracts/CErc20Delegator.sol

73:         require(msg.sender == admin, "CErc20Delegator::_setImplementation: Caller must be admin");

532:         require(msg.value == 0, "CErc20Delegator:fallback: cannot send value to fallback");
File: lodestar-protocol/contracts/CToken.sol

35:         require(msg.sender == admin, "only admin may initialize the market");

36:         require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once");

40:         require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero.");

52:         require(err == NO_ERROR, "setting interest rate model failed");

498:         require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero");

811:         require(amountSeizeError == NO_ERROR, "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED");
File: lodestar-protocol/contracts/Comptroller.sol

194:         require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code

531:             require(borrowBalance >= repayAmount, "Can not repay more than the total borrow");

1096:         require(msg.sender == admin, "only admin can set borrow cap guardian");

1136:         require(msg.sender == admin, "only admin can set borrow cap guardian");

1171:         require(markets[address(cToken)].isListed, "cannot pause a market that is not listed");

1172:         require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");

1181:         require(markets[address(cToken)].isListed, "cannot pause a market that is not listed");

1182:         require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");

1191:         require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");

1200:         require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause");

1209:         require(msg.sender == unitroller.admin(), "only unitroller admin can change brains");
File: lodestar-protocol/contracts/Governance/Comp.sol

166:         require(signatory != address(0), "Comp::delegateBySig: invalid signature");

167:         require(nonce == nonces[signatory]++, "Comp::delegateBySig: invalid nonce");

168:         require(block.timestamp <= expiry, "Comp::delegateBySig: signature expired");

190:         require(blockNumber < block.number, "Comp::getPriorVotes: not yet determined");

234:         require(src != address(0), "Comp::_transferTokens: cannot transfer from the zero address");

235:         require(dst != address(0), "Comp::_transferTokens: cannot transfer to the zero address");
File: lodestar-protocol/contracts/Governance/GovernorAlpha.sol

137:         require(comp.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(), "GovernorAlpha::propose: proposer votes below proposal threshold");

138:         require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorAlpha::propose: proposal function information arity mismatch");

139:         require(targets.length != 0, "GovernorAlpha::propose: must provide actions");

140:         require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions");

145:           require(proposersLatestProposalState != ProposalState.Active, "GovernorAlpha::propose: one live proposal per proposer, found an already active proposal");

146:           require(proposersLatestProposalState != ProposalState.Pending, "GovernorAlpha::propose: one live proposal per proposer, found an already pending proposal");

156:         require(newProposal.id == 0, "GovernorAlpha::propose: ProposalID collsion");

178:         require(state(proposalId) == ProposalState.Succeeded, "GovernorAlpha::queue: proposal can only be queued if it is succeeded");

189:         require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorAlpha::_queueOrRevert: proposal action already queued at eta");

194:         require(state(proposalId) == ProposalState.Queued, "GovernorAlpha::execute: proposal can only be executed if it is queued");

205:         require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal");

208:         require(msg.sender == guardian || comp.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(), "GovernorAlpha::cancel: proposer above threshold");

228:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");

258:         require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature");

263:         require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed");

266:         require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted");

283:         require(msg.sender == guardian, "GovernorAlpha::__acceptAdmin: sender must be gov guardian");

288:         require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian");

293:         require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian");

298:         require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian");

[GAS-9] Pre-increments and pre-decrements are cheaper than post-increments and post-decrements

Saves 5 gas per iteration

Instances (29):

File: lodestar-protocol/contracts/Comptroller.sol

142:         for (uint i = 0; i < len; i++) {

222:         for (uint i = 0; i < len; i++) {

794:         for (uint i = 0; i < assets.length; i++) {

1037:         for (uint i = 0; i < allMarkets.length; i++) {

1085:         for (uint i = 0; i < numMarkets; i++) {

1125:         for (uint i = 0; i < numMarkets; i++) {

1425:         for (uint i = 0; i < cTokens.length; i++) {

1431:                 for (uint j = 0; j < holders.length; j++) {

1437:                 for (uint j = 0; j < holders.length; j++) {

1442:         for (uint j = 0; j < holders.length; j++) {
File: lodestar-protocol/contracts/Governance/GovernorAlpha.sol

152:         proposalCount++;

181:         for (uint i = 0; i < proposal.targets.length; i++) {

197:         for (uint i = 0; i < proposal.targets.length; i++) {

211:         for (uint i = 0; i < proposal.targets.length; i++) {
File: lodestar-protocol/contracts/Governance/GovernorBravoDelegate.sol

93:         proposalCount++;

127:         for (uint i = 0; i < proposal.targets.length; i++) {

147:         for (uint i = 0; i < proposal.targets.length; i++) {

174:         for (uint i = 0; i < proposal.targets.length; i++) {
File: lodestar-protocol/contracts/Lens/CompoundLens.sol

191:         for (uint i = 0; i < cTokenCount; i++) {

240:         for (uint i = 0; i < cTokenCount; i++) {

262:         for (uint i = 0; i < cTokenCount; i++) {

298:         for (uint i = 0; i < proposalCount; i++) {

324:         for (uint i = 0; i < proposalCount; i++) {

380:         for (uint i = 0; i < proposalIds.length; i++) {

448:         for (uint i = 0; i < proposalIds.length; i++) {

532:         for (uint i = 0; i < blockNumbers.length; i++) {
File: lodestar-protocol/contracts/Oracle/PriceOracleProxyETH.sol

237:         for (uint256 i = 0; i < cTokenAddresses.length; i++) {
File: plvglp-oracle/contracts/plvGLPOracle.sol

95:             for (uint256 i = 0; i < latestIndexing; i++) {

102:             for (uint256 i = firstIndex; i <= latestIndexing; i++) {

[GAS-10] 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 (60):

File: lodestar-protocol/contracts/BaseJumpRateModelV2.sol

24:     uint public constant blocksPerYear = 2102400;
File: lodestar-protocol/contracts/CTokenInterfaces.sol

111:     uint public constant protocolSeizeShareMantissa = 2.8e16; //2.8%

118:     bool public constant isCToken = true;
File: lodestar-protocol/contracts/Comptroller.sol

95:     uint224 public constant compInitialIndex = 1e36;
File: lodestar-protocol/contracts/ComptrollerInterface.sol

6:     bool public constant isComptroller = true;
File: lodestar-protocol/contracts/ErrorReporter.sol

75:     uint public constant NO_ERROR = 0; // support legacy return codes
File: lodestar-protocol/contracts/Governance/Comp.sol

6:     string public constant name = "Lodestar";

9:     string public constant symbol = "LODE";

12:     uint8 public constant decimals = 18;

15:     uint public constant totalSupply = 20000000e18; // 20 million LODE

39:     bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");

42:     bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
File: lodestar-protocol/contracts/Governance/GovernorAlpha.sol

6:     string public constant name = "Compound Governor Alpha";

110:     bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");

113:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)");
File: lodestar-protocol/contracts/Governance/GovernorBravoDelegate.sol

9:     string public constant name = "Lodestar Governor Bravo";

12:     uint public constant MIN_PROPOSAL_THRESHOLD = 1000e18; // 1,000 Comp

15:     uint public constant MAX_PROPOSAL_THRESHOLD = 100000e18; //100,000 Comp

18:     uint public constant MIN_VOTING_PERIOD = 86400; // About 24 hours

21:     uint public constant MAX_VOTING_PERIOD = 1209600; // About 2 weeks

24:     uint public constant MIN_VOTING_DELAY = 1;

27:     uint public constant MAX_VOTING_DELAY = 604800; // About 1 week

30:     uint public constant quorumVotes = 400000e18; // 400,000 = 4% of Comp

33:     uint public constant proposalMaxOperations = 10; // 10 actions

36:     bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");

39:     bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)");
File: lodestar-protocol/contracts/InterestRateModel.sol

10:     bool public constant isInterestRateModel = true;
File: lodestar-protocol/contracts/JumpRateModel.sol

18:     uint public constant blocksPerYear = 2102400;
File: lodestar-protocol/contracts/Lens/CompoundLens.sol

67:     address public constant nullAddress = address(0);
File: lodestar-protocol/contracts/Oracle/Denominations.sol

5:     address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

6:     address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB;

9:     address public constant USD = address(840);

10:     address public constant GBP = address(826);

11:     address public constant EUR = address(978);

12:     address public constant JPY = address(392);

13:     address public constant KRW = address(410);

14:     address public constant CNY = address(156);

15:     address public constant AUD = address(36);

16:     address public constant CAD = address(124);

17:     address public constant CHF = address(756);

18:     address public constant ARS = address(32);

19:     address public constant PHP = address(608);

20:     address public constant NZD = address(554);

21:     address public constant SGD = address(702);

22:     address public constant NGN = address(566);

23:     address public constant ZAR = address(710);

24:     address public constant RUB = address(643);

25:     address public constant INR = address(356);

26:     address public constant BRL = address(986);
File: lodestar-protocol/contracts/Oracle/Interfaces/PlvGLPOracleInterface.sol

5:     bool public constant isGLPOracle = true;
File: lodestar-protocol/contracts/Oracle/Interfaces/SushiOracleInterface.sol

5:     bool public constant isSushiOracle = true;
File: lodestar-protocol/contracts/Oracle/PriceOracle.sol

6:     bool public constant isPriceOracle = true;
File: lodestar-protocol/contracts/Oracle/PriceOracleProxyETH.sol

19:     bool public constant isPriceOracle = true;
File: lodestar-protocol/contracts/Oracle/SushiOracle.sol

9:     bool public constant isSushiOracle = true;
File: lodestar-protocol/contracts/PriceOracle.sol

8:     bool public constant isPriceOracle = true;
File: lodestar-protocol/contracts/Timelock.sol

16:     uint public constant GRACE_PERIOD = 14 days;

17:     uint public constant MINIMUM_DELAY = 2 days;

18:     uint public constant MAXIMUM_DELAY = 30 days;
File: lodestar-protocol/contracts/WhitePaperInterestRateModel.sol

19:     uint public constant blocksPerYear = 2102400;
File: plvglp-oracle/contracts/plvGLPOracle.sol

31:     bool public constant isGLPOracle = true;

[GAS-11] Use shift Right/Left instead of division/multiplication if possible

Shifting left by N is like multiplying by 2^N and shifting right by N is like dividing by 2^N

Instances (5):

File: lodestar-protocol/contracts/CTokenInterfaces.sol

112: }
File: lodestar-protocol/contracts/ExponentialNoError.sol

15:     uint constant mantissaOne = expScale;

85:         return uint32(n);
File: lodestar-protocol/contracts/Governance/Comp.sol

211:             Checkpoint memory cp = checkpoints[account][center];

277:         return uint32(n);

[GAS-12] Incrementing with a smaller type than uint256 incurs overhead

Instances (1):

File: lodestar-protocol/contracts/Timelock.sol

7:     using SafeMath for uint;

[GAS-13] Splitting require() statements that use && saves gas

Instances (16):

File: lodestar-protocol/contracts/CToken.sol

36:         require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once");
File: lodestar-protocol/contracts/Comptroller.sol

1083:         require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input");

1123:         require(numMarkets != 0 && numMarkets == numSupplyCaps, "invalid input");
File: lodestar-protocol/contracts/Governance/GovernorAlpha.sol

138:         require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorAlpha::propose: proposal function information arity mismatch");

228:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
File: lodestar-protocol/contracts/Governance/GovernorBravoDelegate.sol

54:         require(votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, "GovernorBravo::initialize: invalid voting period");

55:         require(votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, "GovernorBravo::initialize: invalid voting delay");

56:         require(proposalThreshold_ >= MIN_PROPOSAL_THRESHOLD && proposalThreshold_ <= MAX_PROPOSAL_THRESHOLD, "GovernorBravo::initialize: invalid proposal threshold");

79:         require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorBravo::propose: proposal function information arity mismatch");

166:                 require((comp.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold) && msg.sender == whitelistGuardian, "GovernorBravo::cancel: whitelisted proposer");

210:         require(proposalCount >= proposalId && proposalId > initialProposalId, "GovernorBravo::state: invalid proposal id");

308:         require(newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, "GovernorBravo::_setVotingDelay: invalid voting delay");

321:         require(newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, "GovernorBravo::_setVotingPeriod: invalid voting period");

335:         require(newProposalThreshold >= MIN_PROPOSAL_THRESHOLD && newProposalThreshold <= MAX_PROPOSAL_THRESHOLD, "GovernorBravo::_setProposalThreshold: invalid proposal threshold");

404:         require(msg.sender == pendingAdmin && msg.sender != address(0), "GovernorBravo:_acceptAdmin: pending admin only");
File: lodestar-protocol/contracts/Oracle/PriceOracleProxyETH.sol

236:         require(cTokenAddresses.length == sources.length && cTokenAddresses.length == bases.length, "mismatched data");

[GAS-14] Use storage instead of memory for structs/arrays

Using memory copies the struct or array in memory. Use storage to save the location in storage and have cheaper reads:

Instances (81):

File: lodestar-protocol/contracts/CErc20Delegator.sol

95:         return abi.decode(data, (uint));

106:         return abi.decode(data, (uint));

118:             abi.encodeWithSignature("redeemBehalf(uint256, address)", redeemTokens, redeemee)

131:             abi.encodeWithSignature("redeemUnderlying(uint256)", redeemAmount)

143:         return abi.decode(data, (uint));

154:             abi.encodeWithSignature("borrowBehalf(uint256, address)", borrowAmount, borrowee)

166:         return abi.decode(data, (uint));

177:             abi.encodeWithSignature("repayBorrowBehalf(address,uint256)", borrower, repayAmount)

196:             abi.encodeWithSignature("liquidateBorrow(address,uint256,address)", borrower, repayAmount, cTokenCollateral)

209:         return abi.decode(data, (bool));

221:             abi.encodeWithSignature("transferFrom(address,address,uint256)", src, dst, amount)

236:             abi.encodeWithSignature("approve(address,uint256)", spender, amount)

249:             abi.encodeWithSignature("allowance(address,address)", owner, spender)

261:         return abi.decode(data, (uint));

272:         return abi.decode(data, (uint));

283:             abi.encodeWithSignature("getAccountSnapshot(address)", account)

294:         return abi.decode(data, (uint));

303:         return abi.decode(data, (uint));

312:         return abi.decode(data, (uint));

322:         return abi.decode(data, (uint));

332:             abi.encodeWithSignature("borrowBalanceStored(address)", account)

343:         return abi.decode(data, (uint));

353:         return abi.decode(data, (uint));

362:         return abi.decode(data, (uint));

372:         return abi.decode(data, (uint));

386:             abi.encodeWithSignature("seize(address,address,uint256)", liquidator, borrower, seizeTokens)

409:             abi.encodeWithSignature("_setPendingAdmin(address)", newPendingAdmin)

421:             abi.encodeWithSignature("_setComptroller(address)", newComptroller)

433:             abi.encodeWithSignature("_setReserveFactor(uint256)", newReserveFactorMantissa)

445:         return abi.decode(data, (uint));

455:         return abi.decode(data, (uint));

465:         return abi.decode(data, (uint));

476:             abi.encodeWithSignature("_setInterestRateModel(address)", newInterestRateModel)

490:         assembly {

517:             abi.encodeWithSignature("delegateToImplementation(bytes)", data)
File: lodestar-protocol/contracts/CToken.sol

180:         return mul_ScalarTruncate(exchangeRate, accountTokens[owner]);

358:         uint interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior);

413: 

502: 

875:         uint protocolSeizeAmount = mul_ScalarTruncate(exchangeRate, protocolSeizeTokens);
File: lodestar-protocol/contracts/Comptroller.sol

119: 

142:         for (uint i = 0; i < len; i++) {

220:         uint len = userAssetList.length;

423:         updateCompBorrowIndex(cToken, borrowIndex);

472:         updateCompBorrowIndex(cToken, borrowIndex);

794:         for (uint i = 0; i < assets.length; i++) {

959: 

962:         if (lessThanExp(highLimit, newCollateralFactorExp)) {

1248:             updateCompBorrowIndex(address(cToken), borrowIndex);

1270:             supplyState.index = safe224(

1294:             borrowState.index = safe224(

1330: 

1369: 

1413:         holders[0] = holder;

1430:                 updateCompBorrowIndex(address(cToken), borrowIndex);
File: lodestar-protocol/contracts/Exponential.sol

69:         if (err != MathError.NO_ERROR) {

81:         if (err != MathError.NO_ERROR) {

125:         if (err != MathError.NO_ERROR) {

169:         if (err != MathError.NO_ERROR) {
File: lodestar-protocol/contracts/ExponentialNoError.sol

39:         return truncate(product);

47:         return add_(truncate(product), addend);
File: lodestar-protocol/contracts/Governance/Comp.sol

212:             if (cp.fromBlock == blockNumber) {
File: lodestar-protocol/contracts/Governance/GovernorBravoDelegator.sol

54:         assembly {
File: lodestar-protocol/contracts/Lens/CompoundLens.sol

97:             abi.encodePacked(comptroller.compSupplySpeeds.selector, abi.encode(address(cToken)))

105:             abi.encodePacked(comptroller.compBorrowSpeeds.selector, abi.encode(address(cToken)))

114:                 abi.encodePacked(comptroller.compSpeeds.selector, abi.encode(address(cToken)))

129:             abi.encodePacked(comptroller.borrowCaps.selector, address(cToken))

137:             abi.encodePacked(comptroller.supplyCaps.selector, address(cToken))

191:         for (uint i = 0; i < cTokenCount; i++) {

240:         for (uint i = 0; i < cTokenCount; i++) {

262:         for (uint i = 0; i < cTokenCount; i++) {

298:         for (uint i = 0; i < proposalCount; i++) {

300:             res[i] = GovReceipt({

324:         for (uint i = 0; i < proposalCount; i++) {

326:             res[i] = GovBravoReceipt({

380:         for (uint i = 0; i < proposalIds.length; i++) {

430: 

448:         for (uint i = 0; i < proposalIds.length; i++) {

532:         for (uint i = 0; i < blockNumbers.length; i++) {
File: lodestar-protocol/contracts/Oracle/PriceOracleProxyETH.sol

98:         if (cTokenAddress == letherAddress) {
File: lodestar-protocol/contracts/Timelock.sol

101:         require(success, "Timelock::executeTransaction: Transaction execution reverted.");

[GAS-15] Increments can be unchecked in for-loops

Instances (28):

File: lodestar-protocol/contracts/Comptroller.sol

142:         for (uint i = 0; i < len; i++) {

222:         for (uint i = 0; i < len; i++) {

794:         for (uint i = 0; i < assets.length; i++) {

1037:         for (uint i = 0; i < allMarkets.length; i++) {

1085:         for (uint i = 0; i < numMarkets; i++) {

1125:         for (uint i = 0; i < numMarkets; i++) {

1425:         for (uint i = 0; i < cTokens.length; i++) {

1431:                 for (uint j = 0; j < holders.length; j++) {

1437:                 for (uint j = 0; j < holders.length; j++) {

1442:         for (uint j = 0; j < holders.length; j++) {

1494:         for (uint i = 0; i < numTokens; ++i) {
File: lodestar-protocol/contracts/Governance/GovernorAlpha.sol

181:         for (uint i = 0; i < proposal.targets.length; i++) {

197:         for (uint i = 0; i < proposal.targets.length; i++) {

211:         for (uint i = 0; i < proposal.targets.length; i++) {
File: lodestar-protocol/contracts/Governance/GovernorBravoDelegate.sol

127:         for (uint i = 0; i < proposal.targets.length; i++) {

147:         for (uint i = 0; i < proposal.targets.length; i++) {

174:         for (uint i = 0; i < proposal.targets.length; i++) {
File: lodestar-protocol/contracts/Lens/CompoundLens.sol

191:         for (uint i = 0; i < cTokenCount; i++) {

240:         for (uint i = 0; i < cTokenCount; i++) {

262:         for (uint i = 0; i < cTokenCount; i++) {

298:         for (uint i = 0; i < proposalCount; i++) {

324:         for (uint i = 0; i < proposalCount; i++) {

380:         for (uint i = 0; i < proposalIds.length; i++) {

448:         for (uint i = 0; i < proposalIds.length; i++) {

532:         for (uint i = 0; i < blockNumbers.length; i++) {
File: lodestar-protocol/contracts/Oracle/PriceOracleProxyETH.sol

237:         for (uint256 i = 0; i < cTokenAddresses.length; i++) {
File: plvglp-oracle/contracts/plvGLPOracle.sol

95:             for (uint256 i = 0; i < latestIndexing; i++) {

102:             for (uint256 i = firstIndex; i <= latestIndexing; i++) {

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

Instances (26):

File: lodestar-protocol/contracts/CToken.sol

40:         require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero.");

506:         if (redeemTokensIn > 0) {
File: lodestar-protocol/contracts/Comptroller.sol

342:         if (shortfall > 0) {

362:         if (redeemTokens == 0 && redeemAmount > 0) {

417:         if (shortfall > 0) {

1266:         if (deltaBlocks > 0 && supplySpeed > 0) {

1266:         if (deltaBlocks > 0 && supplySpeed > 0) {

1269:             Double memory ratio = supplyTokens > 0 ? fraction(compAccrued, supplyTokens) : Double({mantissa: 0});

1275:         } else if (deltaBlocks > 0) {

1290:         if (deltaBlocks > 0 && borrowSpeed > 0) {

1290:         if (deltaBlocks > 0 && borrowSpeed > 0) {

1293:             Double memory ratio = borrowAmount > 0 ? fraction(compAccrued, borrowAmount) : Double({mantissa: 0});

1299:         } else if (deltaBlocks > 0) {

1389:         if (deltaBlocks > 0 && compSpeed > 0) {

1389:         if (deltaBlocks > 0 && compSpeed > 0) {

1457:         if (amount > 0 && amount <= compRemaining) {
File: lodestar-protocol/contracts/Governance/Comp.sol

179:         return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;

245:         if (srcRep != dstRep && amount > 0) {

248:                 uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;

255:                 uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;

265:       if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
File: lodestar-protocol/contracts/Governance/GovernorAlpha.sol

228:         require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id");
File: lodestar-protocol/contracts/Oracle/PriceOracleProxyETH.sol

133:         require(price > 0, "invalid price");

145:         require(price > 0, "invalid price");
File: lodestar-protocol/contracts/SafeMath.sol

154:         require(b > 0, errorMessage);
File: plvglp-oracle/contracts/plvGLPOracle.sol

51:         require(index > 0, "First index cannot be zero.");

[GAS-17] <x> += <y> costs more gas than <x> = <x> + <y> for state variables

Instances (2):

File: lodestar-protocol/contracts/Comptroller.sol

96:         sum += HistoricalIndices[i].recordedIndex;

103:         sum += HistoricalIndices[i].recordedIndex;

[GAS-18] Using immutable on variables that are only set in the constructor and never after (2.1k gas per var)

Use immutable if you want to assign a permanent value at construction. Use constants if you already know the permanent value. Both get directly embedded in bytecode, saving SLOAD. Variables only set in the constructor and never edited afterwards should be marked as immutable, as it would avoid the expensive storage-writing operation in the constructor (around 20 000 gas per variable) and replace the expensive storage-reading operations (around 2100 gas per reading) to a less expensive value reading (3 gas)

Instances (1):

File: lodestar-protocol/contracts/BaseJumpRateModelV2.sol

19:         address public owner;
LodestarFinance commented 1 year ago

Please provide a summary of total gas savings with the proposed changes in terms of contract calls and deployment costs. Please provide relevant sources to substantiate submissions wherever possible.

rajatbeladiya commented 1 year ago

Hi @LodestarFinance, I can give you an exact number of gas savings but I am not able to setup and run tests in your repo and getting some errors. If you can help me out with tests setup, I can provide you with total gas savings numbers. Thanks.

In meantime I will provide gas savings here.

rajatbeladiya commented 1 year ago

Hi @LodestarFinance , here is the full report with gas savings. Thanks.

This report mentions

total gas savings: 278585 Deployment gas savings: 130647

Overview

I created repo at https://github.com/rajatbeladiya/gas-checker to substantiate my submission with hardhat-gas-reporter where needed.

here I created two Files called NormalCodeGas.sol and OprimizedCodeGas.sol

NormalCodeGas.sol = this file includes code before the optimization OprimizedCodeGas.sol = this file includes code after optimization

compared gas with both files.

Gas Calculations

[Gas-1]

it saves 15 gas per comparison ( checkout below image )

total gas: 5 * 11 instances => 165 gas

Source Commit: https://github.com/rajatbeladiya/gas-checker/commit/fd78ff0f75ce79bf47c9269e3bbccb6d888cf827

gas-1,7

[Gas-2]

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.

total gas: 100 * 22 instances = 2200 gas

source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27

[Gas-3]

bytes32 uses less gas because it fits in a single word of the EVM, and string is a dynamically sized-type which has current limitations in Solidity (such as can't be returned from a function to a contract).

Source: https://ethereum.stackexchange.com/a/3796/66961

[Gas-4]

for the storage array ( extra sload operation ) => 100 additional extra gas for each iteration except for the first for the memory array ( extra mload operation ) => 3 additional gas for each iteration except for the first

Note: total gas will depend on how big the array is. here contracts has 7 storage iterating arrays and 9 memory iterating arrays

Suppose every elements is atleast 5 elements, then total gas: 100 7 4 + 3 9 5 = 2935 gas

source: https://gist.github.com/grGred/9bab8b9bad0cd42fc23d4e31e7347144#for-loops-improvement

[Gas-5]

cached state variables in stack variable saves 100 gas per instance rather than re-reading them from storage

total gas: 100 * 1 instance = 100

Source: https://dev.to/jamiescript/gas-saving-techniques-in-solidity-324c

[Gas-6]

Instead of using error strings, to reduce deployment and runtime cost, you should use Custom Errors. This would save both deployment and runtime cost.

there is 169 instances => changes will reduce significant gas cost of deployment and runtime cost.

Source: https://blog.soliditylang.org/2021/04/21/custom-errors/

[Gas-7]

here initialization to default value costs extra 3959 deployment gas, checkout Deployments in below image

total gas: 3959 * 33 instances => 130647 gas

Source Commit: https://github.com/rajatbeladiya/gas-checker/commit/fd78ff0f75ce79bf47c9269e3bbccb6d888cf827

gas-1,7

[Gas-8]

using short revert string over longer revert strings will reduce deployment cost and runtime cost also.

there is 117 instances of long strings => changes will reduce significant gas cost of deployment and runtime cost.

Source: https://yos.io/2021/05/17/gas-efficient-solidity/#tip-13-shorten-require-messages-to-less-than-32-characters

[Gas-9]

pre increment saves 5 gas per iteration compared to post increment.

Note: total gas will depend on how big the iterating value is. calculation is only for 1 time iteration.

total gas: 5 * 29 instances = 145 gas

Source: https://yos.io/2021/05/17/gas-efficient-solidity/#tip-12-i-costs-less-gas-compared-to-i-or-i--1

[Gas-10]

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

total gas: 3406 * 60 instances = 204360

[Gas-11]

it saves 2 gas because DIV / MUL opcode uses 5 gas, the SHR / SHL opcode only uses 3 gas

total gas: 2 * 5 instance = 10 gas

Source: https://forum.openzeppelin.com/t/a-collection-of-gas-optimisation-tricks/19966/8

[Gas-12]

Incrementing with a smaller type than uint256 incurs overhead

Source: https://yos.io/2021/05/17/gas-efficient-solidity/#tip-15-usage-of-uint8-may-increase-gas-cost

[Gas-13]

Splitting require() statements that use && saves 3 gas.

total gas: 3 * 16 instance = 48 gas

source: https://yos.io/2021/05/17/gas-efficient-solidity/#tip-11-splitting-require-statements-that-use--saves-gas

[Gas-14]

With EIP-1884 SLOAD now costs 800 gas. Use storage to save the location in storage and have cheaper reads

total gas: 800 * 81 instance = 64800 gas

source: https://ethereum.stackexchange.com/a/66413/66961

[Gas-15]

It saves 55 gas per unchecked increment.

Note: calculation is only for 1 iteration. total gas = 55 * 28 instances = 1540 gas

Source: https://github.com/ethereum/solidity/issues/11721

[Gas-16]

saves 6 gas to Use != 0 instead of > 0 for unsigned integer comparison. The reason != 0 is better is the absence of DUP1(0X80) and GT(0X11)

total gas: 6 * 26 instance = 156 gas

Source: https://github.com/code-423n4/2022-01-yield-findings/issues/57

[Gas-17]

here below image shows it saves 13 gas per operation to use = + instead of +=

total gas : 13 * 2 instance = 26 gas

Source Commit: https://github.com/rajatbeladiya/gas-checker/commit/387d914e1ebdfde5a0cde9af1d968d479cf7a5d8

gas-17

[Gas-18]

Using immutable on variables that are only set in the constructor and never after will replace the expensive storage-reading operations ( around 2100 gas per reading )

total gas = 2100 * 1 instance = 2100

Source: https://dev.to/jamiescript/gas-saving-techniques-in-solidity-324c