Platform: Code4rena
Start Date: 25/08/2022
Pot Size: $75,000 USDC
Total HM: 35
Participants: 147
Period: 7 days
Judge: 0xean
Total Solo HM: 15
Id: 156
League: ETH
Rank: 48/147
Findings: 2
Award: $248.18
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: zzzitron
Also found by: 0x040, 0x1f8b, 0x52, 0x85102, 0xDjango, 0xNazgul, 0xNineDec, 0xSky, 0xSmartContract, 0xkatana, 8olidity, Aymen0909, Bahurum, BipinSah, Bnke0x0, CRYP70, CertoraInc, Ch_301, Chandr, Chom, CodingNameKiki, Deivitto, DimSon, Diraco, ElKu, EthLedger, Funen, GalloDaSballo, Guardian, IllIllI, JansenC, Jeiwan, Lambda, LeoS, Margaret, MasterCookie, PPrieditis, PaludoX0, Picodes, PwnPatrol, RaymondFam, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, StevenL, The_GUILD, TomJ, Tomo, Trust, Waze, __141345__, ajtra, ak1, apostle0x01, aviggiano, bin2chen, bobirichman, brgltd, c3phas, cRat1st0s, carlitox477, cccz, ch13fd357r0y3r, cloudjunky, cryptphi, csanuragjain, d3e4, datapunk, delfin454000, devtooligan, dipp, djxploit, durianSausage, eierina, enckrish, erictee, fatherOfBlocks, gogo, grGred, hansfriese, hyh, ignacio, indijanc, itsmeSTYJ, ladboy233, lukris02, martin, medikko, mics, natzuu, ne0n, nxrblsrpr, okkothejawa, oyc_109, p_crypt0, pfapostol, prasantgupta52, rajatbeladiya, rbserver, reassor, ret2basic, robee, rokinot, rvierdiiev, shenwilly, sikorico, sorrynotsorry, tnevler, tonisives, w0Lfrum, yixxas
56.8212 DAI - $56.82
Context: All Contracts
Description: It is recommended that Solidity contracts are fully annotated using NatSpec for all public interfaces (everything in the ABI). It is clearly stated in the Solidity official documentation. In complex projects such as Defi, the interpretation of all functions and their arguments and returns is important for code readability and auditability. https://docs.soliditylang.org/en/v0.8.15/natspec-format.html
Recommendation: NatSpec comments should be increased in Contracts
There are many missing NatSpecs throughout the project, if we sample 1 of them Current Code: TRSRY.sol#L63-L72
/// @notice Sets approval for specific withdrawer addresses function setApprovalFor( address withdrawer_, ERC20 token_, uint256 amount_ ) external permissioned { withdrawApproval[withdrawer_][token_] = amount_; emit ApprovedForWithdrawal(withdrawer_, token_, amount_); }
Recommendation Code:
/** * @notice Sets approval for specific withdrawer addresses * @dev This function updates the amount of the withdrawApproval mapping value based on the given 3 argument values * @param withdrawer_ Withdrawer address from state variable * @param token_ instance of ERC20 * @param amount_ User amount * @param permissioned Control of admin for access */ function setApprovalFor( address withdrawer_, ERC20 token_, uint256 amount_ ) external permissioned { withdrawApproval[withdrawer_][token_] = amount_; emit ApprovedForWithdrawal(withdrawer_, token_, amount_); }
Description:
Cryptocurrency mixing service, Tornado Cash, has been blacklisted in the OFAC.
A lot of blockchain companies, token projects, NFT Projects have blacklisted
all Ethereum addresses owned by Tornado Cash listed in the US Treasury Department's sanction against the protocol.
(https://home.treasury.gov/policy-issues/financial-sanctions/recent-actions/20220808)
In addition, these platforms even ban accounts that have received ETH on their account with Tornadocash
Some of these Projects;
Details on the subject; https://twitter.com/bantg/status/1556712790894706688?s=20&t=HUTDTeLikUr6Dv9JdMF7AA https://cryptopotato.com/defi-protocol-aave-bans-justin-sun-after-he-randomly-received-0-1-eth-from-tornado-cash/
For this reason, every project in the Ethereum network must have a blacklist function, this is a good method to avoid legal problems in the future, apart from the current need.
Transactions from the project by an account funded by Tonadocash or banned by OFAC can lead to legal problems.Especially American Citizens may want to get addresses to the blacklist legally, this is not an obligation
The ban on Tornado Cash makes little sense, because in the end, no one can prevent people from using other mixer smart contracts, or forking the existing ones. It neither hinders cybercrime, nor privacy.
Here is the most beautiful and close to the project example; Manifold
Manifold Contract https://etherscan.io/address/0xe4e4003afe3765aca8149a82fc064c0b125b9e5a#code
modifier nonBlacklistRequired(address extension) { require(!_blacklistedExtensions.contains(extension), "Extension blacklisted"); _; }
Recommended Mitigation Steps add to Blacklist function and modifier
Context: VoterRegistration.sol#L45-L48 VoterRegistration.sol#L53-L56 TreasuryCustodian.sol#L71-L78 TreasuryCustodian.sol#L80-L87 PriceConfig.sol#L69-L74 PriceConfig.sol#L58-L63 Operator.sol#L624-L62 Governance.sol#L295-L313
Description: Events help non-contract tools to track changes, and events prevent users from being surprised by changes
Recommendation: Add Event-Emit
Context: Heart.sol#L111-L113
Recommendation: Add to indexed parameter for countable Events
Context: Operator.sol#L100 Operator.sol#L590
Description: There is no need to cast a variable that is already an address, such as address(auctioneer_), auctioneer also address.
Recommendation: Use directly variable
Context: Operator.sol#L657 TreasuryCustodian.sol#L51-L56
Recommendation: Use temporary TODOs as you work on a feature, but make sure to treat them before merging. Either add a link to a proper issue in your TODO, or remove it from the code.
Context: Kernel.sol#L180
Description: Mapping and its Natspec comments show different Key Value positions
Recommendation:
Kernel.sol#L180
/// @dev Policy -> Keycode -> Function Selector -> bool for permission mapping(Keycode => mapping(Policy => mapping(bytes4 => bool))) public modulePermissions;
Recomendation code:
/// @dev Keycode -> Policy -> Function Selector -> bool for permission mapping(Keycode => mapping(Policy => mapping(bytes4 => bool))) public modulePermissions;
Context: Kernel.sol#L326
Description: Custom Error name and function behavior is different
Recommendation:
Kernel.sol#L326
function _deactivatePolicy(Policy policy_) internal { if (!policy_.isActive()) revert Kernel_PolicyNotActivated(address(policy_)); // … }
Recomendation code:
Contract hasnt this custom Error, so it should be added in Kernel.sol
function _deactivatePolicy(Policy policy_) internal { if (!policy_.isActive()) revert Kernel_PolicyAlreadyDeactive(address(policy_));
block.number
instead block.timestamp
Context: Governance.sol#L171 Governance.sol#L212 Governance.sol#L231-L235 Governance.sol#L272
Description: Block timestamps have historically been used for a variety of applications, such as entropy for random numbers (see the Entropy Illusion for further details), locking funds for periods of time, and various state-changing conditional statements that are time-dependent. Miners have the ability to adjust timestamps slightly, which can prove to be dangerous if block timestamps are used incorrectly in smart contracts.
Recommendation: Block timestamps should not be used for entropy or generating random numbers—i.e., they should not be the deciding factor (either directly or through some derivation) for winning a game or changing an important state.
Time-sensitive logic is sometimes required; e.g., for unlocking contracts (time-locking) , It is sometimes recommended to use block.number and an average block time to estimate times; with a 10 second block time, 1 week equates to approximately, 60480 blocks. Thus, specifying a block number at which to change a contract state can be more secure, as miners are unable to easily manipulate the block number.
🌟 Selected for report: pfapostol
Also found by: 0x040, 0x1f8b, 0x85102, 0xDjango, 0xNazgul, 0xNineDec, 0xSmartContract, 0xkatana, Amithuddar, Aymen0909, Bnke0x0, CertoraInc, Chandr, CodingNameKiki, Deivitto, Dionysus, Diraco, ElKu, Fitraldys, Funen, GalloDaSballo, Guardian, IllIllI, JC, JansenC, Jeiwan, LeoS, Metatron, Noah3o6, RaymondFam, ReyAdmirado, Rohan16, RoiEvenHaim, Rolezn, Ruhum, Saintcode_, Shishigami, Sm4rty, SooYa, StevenL, Tagir2003, The_GUILD, TomJ, Tomo, Waze, __141345__, ajtra, apostle0x01, aviggiano, bobirichman, brgltd, c3phas, cRat1st0s, carlitox477, cccz, ch0bu, chrisdior4, d3e4, delfin454000, djxploit, durianSausage, erictee, exolorkistis, fatherOfBlocks, gogo, grGred, hyh, ignacio, jag, karanctf, kris, ladboy233, lukris02, m_Rassska, martin, medikko, natzuu, ne0n, newfork01, oyc_109, peiw, rbserver, ret2basic, robee, rokinot, rvierdiiev, sikorico, simon135, tnevler, zishansami
191.3636 DAI - $191.36
Number | Optimization Details | Per gas saved | Context |
---|---|---|---|
1 | Sort Solidity operations using short-circuit mode | 2111 | 2 |
2 | Update on repayLoan function for gas earnings | ~1000 | 1 |
3 | Gas improvement on returning instructionsId value. | ~200 | 1 |
4 | Shifting cheaper than division | 146 | 2 |
5 | Using uint256 instead of bool in mappings is more gas efficient | 146 | 6 |
6 | Converting toggleBeat architecture from bool to uint256 saves gas | 130 | 2 |
7 | Gas can be saved by changing the way the constructor defines its arguments. | 94 | 6 |
8 | Unchecked decrement can be used in -= received [85 gas per use + ~15000 gas saved during deployment cost ] | 85 | 1 |
9 | Use assembly to write address storage values | 33 | 5 |
10 | Functions guaranteed to revert when called by normal users can be marked payable | 24 | 9 |
11 | Function ordering via method ID | 22 | All contracts |
12 | <x> += <y> costs more gas than <x> = <x> + <y> for state variables | 16 | 9 |
13 | Setting the constructor to payable | 13 | 14 |
14 | Catching the array length prior to loop [13 gas per 6 arrays instance] | 13 | 1 |
15 | Direct definition of boolean literals consumes more gas When we use if | 9 | 2 |
16 | Using private rather than public for constans , saves gas | 7 | 8 |
17 | Use assembly to check for address(0) | 6 | 1 |
18 | Using > instead of >= is more gas efficient | 3 | 1 |
Context: RANGE.sol#L221 Operator.sol#L739-L741
Description:
Short-circuiting is a solidity contract development model that uses OR/AND
logic to sequence different cost operations. It puts low gas cost operations in the front and high gas cost operations in the back, so that if the front is low If the cost operation is feasible, you can skip (short-circuit) the subsequent high-cost Ethereum virtual machine operation.
//f(x) is a low gas cost operation //g(y) is a high gas cost operation //Sort operations with different gas costs as follows f(x) || g(y) f(x) && g(y)
Proof Of Concept: The optimizer was turned on and set to 10000 runs
Operator.sol#L739-L741 : [ Min 2000 Gas Saved ] RANGE.sol#L221 : [ 2111 Gas Saved (Poc is given below) ]
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.updateMarket(); c1.updateMarket(); } } contract Contract0 { uint256 marketCapacity_ = 0; uint256 market_ = type(uint256).max; error RANGE_InvalidParams(); function updateMarket() public { if (market_ == type(uint256).max && marketCapacity_ != 0) revert RANGE_InvalidParams(); } } contract Contract1 { uint256 marketCapacity_ = 0; uint256 market_ = type(uint256).max; error RANGE_InvalidParams(); function updateMarket() public { if (marketCapacity_ != 0 && market_ == type(uint256).max) revert RANGE_InvalidParams(); } }
Gas Report:
╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 66808 ┆ 253 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ updateMarket ┆ 4379 ┆ 4379 ┆ 4379 ┆ 4379 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 66808 ┆ 253 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ updateMarket ┆ 2268 ┆ 2268 ┆ 2268 ┆ 2268 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
repayLoan
function for gas earnings [~1000 gas]Context: TRSRY.sol#L105-L119
Description:
In the repayLoan
function, gas is saved if transactions that consume gas but are not necessary are removed safeTransferFrom. Since the safeTransferFrom
function does all balance and return success checks, there is no need for an additional check. So if we removed to balanceOf
and other math. totaly ~1000 gas saved (Get the balance of the account it will cost 400 units of gas)
Current Code
TRSRY.sol#L105-L119 function repayLoan(ERC20 token_, uint256 amount_) external nonReentrant { if (reserveDebt[token_][msg.sender] == 0) revert TRSRY_NoDebtOutstanding(); uint256 prevBalance = token_.balanceOf(address(this)); token_.safeTransferFrom(msg.sender, address(this), amount_); uint256 received = token_.balanceOf(address(this)) - prevBalance; reserveDebt[token_][msg.sender] -= received; totalDebt[token_] -= received; emit DebtRepaid(token_, msg.sender, received); }
Recomendation code:
function repayLoan(ERC20 token_, uint256 amount_) external nonReentrant { if (reserveDebt[token_][msg.sender] == 0) revert TRSRY_NoDebtOutstanding(); // uint256 prevBalance = token_.balanceOf(address(this)); (This line not necessary) token_.safeTransferFrom(msg.sender, address(this), amount_); // uint256 received = token_.balanceOf(address(this)) - prevBalance; (This line not necessary) reserveDebt[token_][msg.sender] -= amount; // (directly read to ‘amount’ ) totalDebt[token_] -= amount; // (directly read to ‘amount’ ) emit DebtRepaid(token_, msg.sender, amount); // (directly read to ‘amount’ ) }
instructionsId
value [~200 gas saved]Context: INSTR.sol#L42
Recommendation:
instructionsId
in function returns and delete INSTR.sol#L78 can save gas
Proof Of Concept: The optimizer was turned on and set to 10000 runs INSTR.sol#L78
function store(Instruction[] calldata instructions_) external permissioned returns (uint256) { //codes….. return instructionsId;
Recomendation code:
function store(Instruction[] calldata instructions_) external permissioned returns (uint256 instructionsId) { //codes…..
Context: Operator.sol#L372 Operator.sol#L427
Description:
A division by 2 can be calculated by shifting one to the right. While the DIV
opcode uses 5 gas
, the SHR
opcode only uses 3 gas
Furthermore, Solidity's division operation also includes a division-by-0 prevention which is bypassed using shifting.
Recommendation: Change the functions visibility to external to save gas.
Proof Of Concept: The optimizer was turned on and set to 10000 runs
contract GasTest is DSTest { Contract1 c1; Contract2 c2; function setUp() public { c1 = new Contract1(); c2 = new Contract2(); } function testGas() public { c1.checkPoint(); c2.checkPoint(); } } contract Contract1 { uint32 a = 10; uint32 b = 2; function checkPoint() public view returns (uint32) { uint32 center = a/b; return(center); } } contract Contract2 { uint32 a = 10; uint32 b = 2; function checkPoint() public view returns (uint32) { uint32 center = a >> 1; return(center); } }
Gas Report
╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 70035 ┆ 293 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ checkPoint ┆ 2413 ┆ 2413 ┆ 2413 ┆ 2413 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract2 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 49417 ┆ 189 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ checkPoint ┆ 2267 ┆ 2267 ┆ 2267 ┆ 2267 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
uint256
instead of bool
in mappings is more gas efficient [146 gas per instance]Context: Kernel.sol#L181 Kernel.sol#L194 Kernel.sol#L197 BondCallback.sol#L24 Governance.sol#L105 Governance.sol#L117
Description: OpenZeppelin uint256 and bool comparison : Booleans are more expensive than uint256 or any type that takes up a full word because each write operation emits an extra SLOAD to first read the slot's contents, replace the bits taken up by the boolean, and then write back. This is the compiler's defense against contract upgrades and pointer aliasing, and it cannot be disabled.The values being non-zero value makes deployment a bit more expensive.
Recommendation: Use uint256(1) and uint256(2) instead of bool
Proof Of Concept: The optimizer was turned on and set to 10000 runs
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0._setPolicyPermissions(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 79, true); c1._setPolicyPermissions(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 79, 2); } } contract Contract0 { mapping(address => mapping(uint256 => bool)) public modulePermissions; error boolcheck(); function _setPolicyPermissions(address addr_, uint256 id, bool log) external { modulePermissions[addr_][id] = log; if(modulePermissions[addr_][id] = false) revert boolcheck(); } } contract Contract1 { mapping(address => mapping(uint256 => uint256)) public modulePermissions; error boolcheck(); // Use uint256 instead of bool (1 = false , 2 = true) function _setPolicyPermissions(address addr_, uint256 id, uint256 log) external { modulePermissions[addr_][id] == log; if(modulePermissions[addr_][id] == 1) revert boolcheck(); } }
Gas Report
╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 88335 ┆ 473 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ _setPolicyPermissions ┆ 2750 ┆ 2750 ┆ 2750 ┆ 2750 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 87135 ┆ 467 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ _setPolicyPermissions ┆ 2604 ┆ 2604 ┆ 2604 ┆ 2604 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
toggleBeat
architecture from bool
to uint256
saves gas [130 gas per instance]Context: Heart.sol#L135-L137 Operator.sol#L624-L627
Current Code
Heart.sol#L135-L137 bool public active; function toggleBeat() external onlyRole("heart_admin") { active = !active; }
Recomendation code:
uint256 check; // 1= active, 2= inactive function use() external { check = 2; }
Proof Of Concept:
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.set(); c1.set(); c0.use(); c1.use(); } } contract Contract0 { uint256 check; // 1= active, 2= inactive error Heart_BeatStopped(); function use() external { check = 2; } function set() external { if(check == 1) revert Heart_BeatStopped(); } } contract Contract1 { bool active; error Heart_BeatStopped(); function use() external { active = !active; } function set() external { if (active) revert Heart_BeatStopped(); } }
Proof of Concept Gas Report
╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 36687 ┆ 214 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ set ┆ 2255 ┆ 2255 ┆ 2255 ┆ 2255 ┆ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ use ┆ 20116 ┆ 20116 ┆ 20116 ┆ 20116 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 45499 ┆ 258 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ set ┆ 2258 ┆ 2258 ┆ 2258 ┆ 2258 ┆ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ use ┆ 20246 ┆ 20246 ┆ 20246 ┆ 20246 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯
Context: TRSRY.sol#L17 MINTR.sol#L8 RANGE.sol#L16 PRICE.sol#L22 VOTES.sol#L11 INSTR.sol#L10
Description: When the constructor is inherited, the constructor arguments in the contract we inherit can be updated in two ways.
1 - Either specify directly in the inheritance list. (New use with Gas Optimization) 2- Or via a "modifier" of the derived constructor. (standard use)
In the contexts mentioned above, we get gas saved with this usage
Proof Of Concept: The optimizer was turned on and set to 10000 runs
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(7); c1 = new Contract1(); } function testGas() public { c0.set(); c1.set(); } } contract Base { uint public x; constructor(uint _x) { x = _x; } } contract Contract0 is Base { constructor(uint _y) Base(7) {} function set() public { } } contract Contract1 is Base(7) { constructor() {} function set() public { } }
Gas Report
Deployment cost difference can be checked ╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 49723 ┆ 266 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ set ┆ 120 ┆ 120 ┆ 120 ┆ 120 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 49587 ┆ 172 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ set ┆ 120 ┆ 120 ┆ 120 ┆ 120 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯
-= received
[85 gas per use + ~15000 gas saved during deployment cost]Context: TRSRY.sol#L116
Description:
Newer versions of the Solidity compiler will check for integer overflows and underflows automatically. This provides safety but increases gas costs. When an unsigned integer is guaranteed to never overflow, the unchecked feature of Solidity can be used to save gas costs. There is no need for -= received
safemath check in this function, there is no possibility of underflow, the first operations of the function do these checks
You can see exactly this example in transmission11 in solmate ERC20 contract: https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol#L99-L105
Current Code
TRSRY.sol#L105-L119 function repayLoan(ERC20 token_, uint256 amount_) external nonReentrant { if (reserveDebt[token_][msg.sender] == 0) revert TRSRY_NoDebtOutstanding(); uint256 prevBalance = token_.balanceOf(address(this)); token_.safeTransferFrom(msg.sender, address(this), amount_); uint256 received = token_.balanceOf(address(this)) - prevBalance; reserveDebt[token_][msg.sender] -= received; totalDebt[token_] -= received; emit DebtRepaid(token_, msg.sender, received); }
Recomendation code:
function repayLoan(ERC20 token_, uint256 amount_) external nonReentrant { if (reserveDebt[token_][msg.sender] == 0) revert TRSRY_NoDebtOutstanding(); uint256 prevBalance = token_.balanceOf(address(this)); token_.safeTransferFrom(msg.sender, address(this), amount_); uint256 received = token_.balanceOf(address(this)) - prevBalance; reserveDebt[token_][msg.sender] -= received; unchecked { totalDebt[token_] -= received; } emit DebtRepaid(token_, msg.sender, received); }
Proof Of Concept:
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.repay(5); c1.repay(5); } } contract Contract0 { uint256 balanceOfme = 100; function repay( uint256 amount ) public { balanceOfme -= amount; } } contract Contract1 { uint256 balanceOfme = 100; function repay( uint256 amount ) public { unchecked { balanceOfme -= amount; } } }
Proof of Concept Gas Report
╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 66999 ┆ 260 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ repay ┆ 5293 ┆ 5293 ┆ 5293 ┆ 5293 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 51787 ┆ 183 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ repay ┆ 5208 ┆ 5208 ┆ 5208 ┆ 5208 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
Context: Heart.sol#L144-L145 Kernel.sol#L76-L77 Operator.sol#L119-L120 Operator.sol#L593-L594 BondCallback.sol#L43-L44
Proof Of Concept: The optimizer was turned on and set to 10000 runs
contract GasTestFoundry is DSTest { Contract1 c1; Contract2 c2; function setUp() public { c1 = new Contract1(); c2 = new Contract2(); } function testGas() public { c1.setRewardTokenAndAmount(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 356); c2.setRewardTokenAndAmount(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 356); } } contract Contract1 { address rewardToken ; uint256 reward; function setRewardTokenAndAmount(address token_, uint256 reward_) external { rewardToken = token_; reward = reward_; } } contract Contract2 { address rewardToken ; uint256 reward; function setRewardTokenAndAmount(address token_, uint256 reward_) external { assembly { sstore(rewardToken.slot, token_) sstore(reward.slot, reward_) } } }
Gas Report
╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 50899 ┆ 285 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ setRewardTokenAndAmount ┆ 44490 ┆ 44490 ┆ 44490 ┆ 44490 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮ │ src/test/GasTest.t.sol:Contract2 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 38087 ┆ 221 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ setRewardTokenAndAmount ┆ 44457 ┆ 44457 ┆ 44457 ┆ 44457 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯
Context: Kernel.sol#L235 Kernel.sol#L439 Kernel.sol#L451 BondCallback.sol#L190 BondCallback.sol#L152 Heart.sol#L130 Heart.sol#L135 Heart.sol#L140 Heart.sol#L150
Description: If a function modifier or require such as onlyOwner-admin is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2), DUP1(3), ISZERO(3), PUSH2(3), JUMPI(10), PUSH1(3), DUP1(3), REVERT(0), JUMPDEST(1), POP(2), which costs an average of about 24 gas per call to the function, in addition to the extra deployment cost
Recommendation:
Functions guaranteed to revert when called by normal users can be marked payable (for only onlyowner
or admin
functions)
Proof Of Concept: The optimizer was turned on and set to 10000 runs
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.resetBeat(); c1.resetBeat(); } } contract Contract0 { uint256 versionNFTDropCollection; function resetBeat() external { versionNFTDropCollection++; } } contract Contract1 { uint256 versionNFTDropCollection; function resetBeat() external payable { versionNFTDropCollection++; } }
Gas Report
╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 44293 ┆ 252 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ resetBeat ┆ 22308 ┆ 22308 ┆ 22308 ┆ 22308 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 41893 ┆ 240 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ resetBeat ┆ 22284 ┆ 22284 ┆ 22284 ┆ 22284 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯
Context: All Contracts
Description: Contracts most called functions could simply save gas by function ordering via Method ID. Calling a function at runtime will be cheaper if the function is positioned earlier in the order (has a relatively lower Method ID) because 22 gas are added to the cost of a function for every position that came before it. The caller can save on gas if you prioritize most called functions.
Recommendation:
Find a lower method ID
name for the most called functions for example Call() vs. Call1() is cheaper by 22 gas
For example, the function IDs in the Operator.sol
contract will be the most used; A lower method ID may be given
Proof of Consept: https://coinsbench.com/advanced-gas-optimizations-tips-for-solidity-85c47f413dc5
Operator.sol function names can be named and sorted according to METHOD ID
Sighash | Function Signature ======================== 9459b875 => configureDependencies() 5924be70 => requestPermissions() 7159a618 => operate() dc7fd16c => swap(ERC20,uint256,uint256) ab104771 => bondPurchase(uint256,uint256) b75a14c8 => _activate(bool) d3170ab7 => _deactivate(bool) ae159e98 => _getPriceDecimals(uint256) 41f19a46 => setSpreads(uint256,uint256) 33bd33b4 => setThresholdFactor(uint256) aa9760a1 => setCushionFactor(uint32) f2fa4737 => setCushionParams(uint32,uint32,uint32) 01de9ba8 => setReserveFactor(uint32) 3a3f54bc => setRegenParams(uint32,uint32,uint32) 945beb0a => setBondContracts(IBondAuctioneer,IBondCallback) 8129fc1c => initialize() 2ecd1f20 => regenerate(bool) 29c68dc1 => toggleActive() d5711035 => _updateCapacity(bool,uint256) 1d952c94 => _updateRangePrices() 4c3a66d4 => _addObservation() 68c42258 => _regenerate(bool) 6d96c12c => _checkCushion(bool) 770a996e => getAmountOut(ERC20,uint256) 9e897711 => fullCapacity(bool) 200d2ed2 => status() 79502c55 => config()
x += y
costs more gas than x = x + y
for state variables [16 gas per instance]Context: PRICE.sol#L136 PRICE.sol#L138 TRSRY.sol#L96-L97 TRSRY.sol#L131 VOTES.sol#L58 BondCallback.sol#L143-L144 Governance.sol#L198 Governance.sol#L252-L254 Heart.sol#L103
Description:
x += y
costs more gas than x = x + y
for state variables
Proof Of Concept: The optimizer was turned on and set to 10000 runs
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.foo(33); c1.foo(33); } } contract Contract0 { uint256 _totalBorrow = 60; function foo(uint256 _interestEarned) public { _totalBorrow += interestEarned; } } contract Contract1 { uint256 _totalBorrow = 60; function foo(uint256 _interestEarned) public { _totalBorrow = _totalBorrow + interestEarned; } }
Gas Report
╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 70805 ┆ 279 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ foo ┆ 5302 ┆ 5302 ┆ 5302 ┆ 5302 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 69805 ┆ 274 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ foo ┆ 5286 ┆ 5286 ┆ 5286 ┆ 5286 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
Context: Kernel.sol#L217, TRSRY.sol#L45, MINTR.sol#L15 RANGE.sol#L77, PRICE.sol#L22, VOTES.sol#L16 INSTR.sol#L20, TreasuryCustodian.sol#L24 Operator.sol#L92, BondCallback.sol#L38, Heart.sol#L54 PriceConfig.sol#L15, Governance.sol#L59, VoterRegistration.sol#L16
Description:
You can cut out 10 opcodes in the creation-time EVM bytecode if you declare a constructor payable. Making the constructor payable eliminates the need for an initial check of msg.value == 0
and saves 13 gas
on deployment with no security risks.
Recommendation:
Set the constructor to payable
Proof Of Concept: https://forum.openzeppelin.com/t/a-collection-of-gas-optimisation-tricks/19966/5?u=pcaversaccio
The optimizer was turned on and set to 10000 runs
contract GasTestFoundry is DSTest { Contract1 c1; Contract2 c2; function setUp() public { c1 = new Contract1(); c2 = new Contract2(); } function testGas() public { c1.x(); c2.x(); } } contract Contract1 { uint256 public dummy; constructor() payable { dummy = 1; } function x() public { } } contract Contract2 { uint256 public dummy; constructor() { dummy = 1; } function x() public { } }
Gas Report
╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 49563 ┆ 159 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ ╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract2 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 49587 ┆ 172 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
Context: Governance.sol#L278
Description: One can save gas by caching the array length (in stack) and using that set variable in the loop. Replace state variable reads and writes within loops with local variable reads and writes. This is done by assigning state variable values to new local variables, reading and/or writing the local variables in a loop, then after the loop assigning any changed local variables to their equivalent state variables.
Recommendation:
Simply do something like so before the for loop: uint length = variable.length
Then add length in place of variable.length
in the for loop.
Proof Of Concept: The optimizer was turned on and set to 10000 runs
contract GasTest is DSTest { Contract1 c1; Contract2 c2; function setUp() public { c1 = new Contract1(); c2 = new Contract2(); } function testGas() public { address[] memory Inputs = new address[](6); Inputs[0] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; Inputs[1] = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2; Inputs[2] = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db; Inputs[3] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB; Inputs[4] = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2; Inputs[5] = 0x17F6AD8Ef982297579C203069C1DbfFE4348c372; c1.executeProposal(Inputs); c2.executeProposal(Inputs); } } contract Contract1 { function executeProposal(address[] memory test) public pure { uint256 a = test.length; for (uint256 step; step < a; ) { unchecked { ++step; } } } } contract Contract2 { function executeProposal(address[] memory test) public pure { for (uint256 step; step < test.length; ) { unchecked { ++step; } } } }
Gas Report
╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 92941 ┆ 496 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ executeProposal ┆ 1653 ┆ 1653 ┆ 1653 ┆ 1653 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract2 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 92541 ┆ 494 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ executeProposal ┆ 1666 ┆ 1666 ┆ 1666 ┆ 1666 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
if
[9 gas per instance]Context: Governance.sol#L223-L224 Governance.sol#L306
Description:
Direct definition of boolean literals consumes more gas When we use if
Proof Of Concept The optimizer was turned on and set to 10000 runs
use for GasTest : Governance.sol#L223-L224
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.activateProposal(2); c1.activateProposal(2); } } contract Contract0 { mapping(uint256 => bool) public proposalHasBeenActivated; error ProposalAlreadyActivated(); function activateProposal(uint256 proposalId_) public { if (proposalHasBeenActivated[proposalId_] == true) { revert ProposalAlreadyActivated(); } } } contract Contract1 { mapping(uint256 => bool) public proposalHasBeenActivated; error ProposalAlreadyActivated(); function activateProposal(uint256 proposalId_) public { if (proposalHasBeenActivated[proposalId_]) { revert ProposalAlreadyActivated(); } } }
Gas Report
╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 56305 ┆ 313 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ activateProposal ┆ 2427 ┆ 2427 ┆ 2427 ┆ 2427 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 55505 ┆ 309 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ activateProposal ┆ 2418 ┆ 2418 ┆ 2418 ┆ 2418 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
private
rather than public
for constans
, saves gas [7 gas per instance]Context: Governance.sol#L121 Governance.sol#L124 Governance.sol#L127 Governance.sol#L130 Governance.sol#L133 Governance.sol#L137 RANGE.sol#L65 Operator.sol#L89
Proof Of Concept The optimizer was turned on and set to 10000 runs
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0._getPriceDecimals(); c1._getPriceDecimals(); } } contract Contract0 { uint32 public constant FACTOR_SCALE = 1e4; function _getPriceDecimals() public returns(uint32) { return FACTOR_SCALE; } } contract Contract1 { uint32 private constant FACTOR_SCALE = 1e4; function _getPriceDecimals() public returns(uint32) { return FACTOR_SCALE; } }
Gas Report
╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 29281 ┆ 176 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ _getPriceDecimals ┆ 156 ┆ 156 ┆ 156 ┆ 156 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 24075 ┆ 149 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ _getPriceDecimals ┆ 149 ┆ 149 ┆ 149 ┆ 149 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯
address(0)
[6 gas per instance]Context: BondCallback.sol#L191
Proof Of Concept: The optimizer was turned on and set to 10000 runs
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public view { c0.setOperator(address(this)); c1.setOperator(address(this)); } } contract Contract0 { error Callback_InvalidParams(); function setOperator(address operator_) public pure { if (address(operator_) == address(0)) revert Callback_InvalidParams(); } } contract Contract1 { function setOperator(address operator_) public pure { assembly { if iszero(operator_) { mstore(0x00, "Callback_InvalidParams") revert(0x00, 0x20) } } } }
Gas Report:
╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 50899 ┆ 285 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ setOperator ┆ 258 ┆ 258 ┆ 258 ┆ 258 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 44893 ┆ 255 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ setOperator ┆ 252 ┆ 252 ┆ 252 ┆ 252 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯
>
instead of >=
is more gas efficient [3 gas per instance]Context: Operator.sol#L486
Proof Of Concept The optimizer was turned on and set to 10000 runs use for GasTest : Operator.sol#L486
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0._getPriceDecimals(2); c1._getPriceDecimals(2); } } contract Contract0 { function _getPriceDecimals(uint256 price_) public view returns (int8) { int8 decimals; while (price_ >= 10) { price_ = price_ / 10; decimals++; } } } contract Contract1 { function _getPriceDecimals(uint256 price_) public view returns (int8) { int8 decimals; while (price_ > 9) { price_ = price_ / 10; decimals++; } } }
Gas Report
╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 69517 ┆ 379 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ _getPriceDecimals ┆ 298 ┆ 298 ┆ 298 ┆ 298 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 69717 ┆ 380 ... See the rest this report [here](https://github.com/code-423n4/2022-08-olympus-findings/blob/main/data/0xSmartContract-G.md)