You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Please, consider everything described below as a general recommendation. These notes will represent potential possibilities to optimize gas consumption. It's okay, if something is not suitable in your case. Just let me know the reason in the comments section. Enjoy!
[G-01] Mark functions as external instead of public
PROBLEM
External functions allow to read directly from calldata, meanwhile public functions need to allocate the space in the memory for arguments.
PROOF OF CONCEPT
-setAccountantContract(address) should be declared external
contracts/CNote.sol#L16-L21
-RetAccountant() should be declared external
contracts/Note.sol#L17-L19
-delegateToViewImplementation(bytes) should be declared external
contracts/Accountant/AccountantDelegator.sol#L107-L115
-initialize(address) should be declared external
contracts/Treasury/TreasuryDelegate.sol#L19-L27
-delegate(address) should be declared external
contracts/Governance/Comp.sol#L148-L150
-delegateBySig(address,uint256,uint256,uint8,bytes32,bytes32) should be declared external
contracts/Governance/Comp.sol#L161-L170
-getPriorVotes(address,uint256) should be declared external
contracts/Governance/Comp.sol#L189-L221
-getActions(uint256) should be declared external:
contracts/Governance/GovernorAlpha.sol#L218-L221GovernorAlpha.getActions(uint256)
-castVote(uint256,bool) should be declared external:
contracts/Governance/GovernorAlpha.sol#L249-L251
-castVoteBySig(uint256,bool,uint8,bytes32,bytes32) should be declared external:
contracts/Governance/GovernorAlpha.sol#L253-L260
-__queueSetTimelockPendingAdmin(address,uint256) should be declared external:
contracts/Governance/GovernorAlpha.sol#L292-L295
-cancel(uint256) should be declared external:
contracts/Governance/GovernorAlpha.sol#L203-L216
-queue(uint256) should be declared external:
contracts/Governance/GovernorAlpha.sol#L177-L186
-__abdicate() should be declared external:
contracts/Governance/GovernorAlpha.sol#L287-L290
-execute(uint256) should be declared external:
contracts/Governance/GovernorAlpha.sol#L193-L201
-propose(address[],uint256[],string[],bytes[],string) should be declared external:
contracts/Governance/GovernorAlpha.sol#L136-L175
-__executeSetTimelockPendingAdmin(address,uint256) should be declared external:
contracts/Governance/GovernorAlpha.sol#L297-L300
-getReceipt(uint256,address) should be declared external:
contracts/Governance/GovernorAlpha.sol#L223-L225
-__acceptAdmin() should be declared external
contracts/Governance/GovernorAlpha.sol#L282-L285
-delegateBySig(address,uint256,uint256,uint8,bytes32,bytes32) should be declared external:
contracts/Governance/Comp.sol#L161-L170
-setAccountantContract(address) should be declared external:
contracts/CNote.sol#L16-L21
-initialize(address) should be declared external:
contracts/Treasury/TreasuryDelegate.sol#L19-L27
-initialize(address) should be declared external:
contracts/Governance/GovernorBravoDelegate.sol#L24-L31
-drip() should be declared external:
contracts/Reservoir.sol#L46-L67
-RetAccountant() should be declared external:
contracts/Note.sol#L17-L19
-delegateToViewImplementation(bytes) should be declared external:
contracts/Accountant/AccountantDelegator.sol#L107-L115
TOOLS USED
Slither
Manual Analysis
MITIGATION
Replace public by external.
[G-02] Define Custom Errors with error-codes
PROBLEM
Using predefined custom error leads to gas optimization. Also it's great to revert predefined error-code for the specific message instead of reverting exactly the same message for multiple require statements.
PROOF OF CONCEPT
contracts/CNote.sol
-require(msg.sender== admin, "CNote::_setAccountantContract:Only admin may call this function");
-require(success, "TOKEN_TRANSFER_IN_FAILED");
-require(balanceCur ==0, "Accountant has not been correctly supplied");
-require(success, "TOKEN_TRANSFER_OUT_FAILED");
-require(token.balanceOf(address(this)) ==0, "cNote::doTransferOut: TransferOut Failed");
-require(_notEntered, "re-entered");
-require(_notEntered, "re-entered");
contracts/CToken.sol
-require(msg.sender== admin, "only admin may initialize the market");
-require(accrualBlockNumber ==0&& borrowIndex ==0, "market may only be initialized once");
-require(initialExchangeRateMantissa >0, "initial exchange rate must be greater than zero.");
-require(err == NO_ERROR, "setting comptroller failed");
-require(err == NO_ERROR, "setting interest rate model failed");
-require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high");
-require(redeemTokensIn ==0|| redeemAmountIn ==0, "one of redeemTokensIn or redeemAmountIn must be zero");
-require(amountSeizeError == NO_ERROR, "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED");
-require(cTokenCollateral.balanceOf(borrower) >= seizeTokens, "LIQUIDATE_SEIZE_TOO_MUCH");
-require(cTokenCollateral.seize(liquidator, borrower, seizeTokens) == NO_ERROR, "token seizure failed");
-require(newComptroller.isComptroller(), "marker method returned false");
contracts/NoteInterest.sol
-require(msg.sender== admin, "only the admin may set the base rate");
-require(msg.sender== admin, "only the admin may set the adjuster coefficient");
-require(msg.sender== admin, "only the admin may set the update frequency");
-require(msg.sender== admin, "AccountantDelegator::_setImplementation: admin only");
-require(implementation_ !=address(0), "AccountantDelegator::_setImplementation: invalid implementation address");
-require(msg.value==0,"AccountantDelegator:fallback: cannot send value to fallback");
contracts/Comp.sol
-require(signatory !=address(0), "Comp::delegateBySig: invalid signature");
-require(nonce == nonces[signatory]++, "Comp::delegateBySig: invalid nonce");
-require(block.timestamp<= expiry, "Comp::delegateBySig: signature expired");
-require(blockNumber <block.number, "Comp::getPriorVotes: not yet determined");
-require(src !=address(0), "Comp::_transferTokens: cannot transfer from the zero address");
-require(dst !=address(0), "Comp::_transferTokens: cannot transfer to the zero address");
Following contracts also contains optimization possibilities.
contracts/GovernorAlpha.sol
contracts/GovernorBravoDelegate.sol
contracts/GovernorBravoDelegator.sol
contracts/BaseV1-core.sol
contracts/BaceV1-periphery.sol
contracts/Token.sol
contracts/TreasuryDelegator.sol
TOOLS USED
Slither
Manual Analysis
MITIGATION
Define your own custom errors and revert them instead of directly provided revert-message. I would probably create my own library for errors, but it's up to you.
It's also great to use error-codes, like: "L1" or "P1, P2", alowing fetching this codes with actual messages off-chain.
[G-03] Cache the data into the memory
PROBLEM
Accesing the storage is pretty expensive. Therefore, it's better to cache state variables before proceding these type of operations.
PROOF OF CONCEPT
contracts/GovernorAlpha.sol
Maybe, it's better to push this proposal into the memory rather than operating with references? I undestood the motivation behind the reference to the state var, simply you want to be able to change some attributes, but if you want to read those attributes, it's better to load directly from the memory, because SLOAD >> MLOAD. It's sagnificant gas saving, especially in the loops.
Also, these contracts contains the same possibilities:
contracts/CToken.sol
contracts/Governance/GovernorBravoDelegate.sol
TOOLS USED
Slither
Manual Analysis
MITIGATION
For those cases, where you would like to change the state var, it's okay to define it as storage. But for loads define an object in the memory, then operate with it.
[G-04] Redundant code
PROBLEM
There is no need to check for overflow and underflows since solidity 0.8.0.
There is no need in all custom arithmetic operations since solidity 0.8.0.
This is ignoring the fact that in all the linked instances, each storage value is read only once, a pointer to storage is actually cheaper for all those cases.
The one exception is proposal.targets.length; however I will not count this as valid as 99% of the submissions is incorrect
Table of contents
Disclaimer
[G-01] Mark functions as external instead of public
PROBLEM
PROOF OF CONCEPT
TOOLS USED
MITIGATION
[G-02] Define Custom Errors with error-codes
PROBLEM
require
statements.PROOF OF CONCEPT
contracts/CNote.sol
contracts/CToken.sol
contracts/NoteInterest.sol
contracts/Accountant/AccountantDelegate.sol
contracts/Accountant/AccountantDelegator.sol
contracts/Comp.sol
Following contracts also contains optimization possibilities.
TOOLS USED
MITIGATION
Define your own custom errors and revert them instead of directly provided revert-message. I would probably create my own library for errors, but it's up to you.
It's also great to use error-codes, like: "L1" or "P1, P2", alowing fetching this codes with actual messages off-chain.
[G-03] Cache the data into the memory
PROBLEM
PROOF OF CONCEPT
contracts/GovernorAlpha.sol
Maybe, it's better to push this proposal into the memory rather than operating with references? I undestood the motivation behind the reference to the state var, simply you want to be able to change some attributes, but if you want to read those attributes, it's better to load directly from the memory, because SLOAD >> MLOAD. It's sagnificant gas saving, especially in the loops.
TOOLS USED
MITIGATION
[G-04] Redundant code
PROBLEM
PROOF OF CONCEPT
TOOLS USED
MITIGATION
[G-05] Minor optimizations
General Recommendations
dripped = 0
oruint i = 0
, it's == 0 by default. It consumes gas for MLOAD/SLOAD opcodesrequire(a == b && c == d)
=>require(a == b); require(c == d)
, extra gas usage forAND
opcode.if (msg.sender != admin){revert ...}
with modifiers, reduces deployment cost.uint256
=>uint96/uint32
, if it's not properly tide up in a storage. It costs extra gas for casting..balance
see EIP-2929.The text was updated successfully, but these errors were encountered: