Olympus DAO contest - oyc_109's results

Version 3 of Olympus protocol, a decentralized floating currency.

General Information

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

Olympus DAO

Findings Distribution

Researcher Performance

Rank: 62/147

Findings: 2

Award: $91.18

🌟 Selected for report: 0

🚀 Solo Findings: 0

[L-01] Unspecific Compiler Version Pragma

Avoid floating pragmas for non-library contracts.

While floating pragmas make sense for libraries to allow them to be included with multiple different versions of applications, it may be a security risk for application implementations.

A known vulnerable compiler version may accidentally be selected or security tools might fall-back to an older compiler version ending up checking a different EVM compilation that is ultimately deployed on the blockchain.

It is recommended to pin to a concrete compiler version.

2022-08-olympus/src/policies/interfaces/IHeart.sol::2 => pragma solidity >=0.8.0; 2022-08-olympus/src/policies/interfaces/IOperator.sol::2 => pragma solidity >=0.8.0;

[L-02] Use of Block.timestamp

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.

2022-08-olympus/src/modules/PRICE.sol::143 => lastObservationTime = uint48(block.timestamp); 2022-08-olympus/src/modules/PRICE.sol::146 => emit NewObservation(block.timestamp, currentPrice, _movingAverage); 2022-08-olympus/src/modules/PRICE.sol::165 => if (updatedAt < block.timestamp - 3 * uint256(observationFrequency)) 2022-08-olympus/src/modules/PRICE.sol::171 => if (updatedAt < block.timestamp - uint256(observationFrequency)) 2022-08-olympus/src/modules/PRICE.sol::215 => if (startObservations_.length != numObs || lastObservationTime_ > uint48(block.timestamp)) 2022-08-olympus/src/modules/RANGE.sol::85 => lastActive: uint48(block.timestamp), 2022-08-olympus/src/modules/RANGE.sol::92 => lastActive: uint48(block.timestamp), 2022-08-olympus/src/modules/RANGE.sol::136 => _range.high.lastActive = uint48(block.timestamp); 2022-08-olympus/src/modules/RANGE.sol::138 => emit WallDown(true, block.timestamp, capacity_); 2022-08-olympus/src/modules/RANGE.sol::148 => _range.low.lastActive = uint48(block.timestamp); 2022-08-olympus/src/modules/RANGE.sol::150 => emit WallDown(false, block.timestamp, capacity_); 2022-08-olympus/src/modules/RANGE.sol::191 => lastActive: uint48(block.timestamp), 2022-08-olympus/src/modules/RANGE.sol::200 => lastActive: uint48(block.timestamp), 2022-08-olympus/src/modules/RANGE.sol::207 => emit WallUp(high_, block.timestamp, capacity_); 2022-08-olympus/src/modules/RANGE.sol::231 => emit CushionDown(high_, block.timestamp); 2022-08-olympus/src/modules/RANGE.sol::233 => emit CushionUp(high_, block.timestamp, marketCapacity_); 2022-08-olympus/src/policies/Governance.sol::171 => block.timestamp, 2022-08-olympus/src/policies/Governance.sol::212 => if (block.timestamp > proposal.submissionTimestamp + ACTIVATION_DEADLINE) { 2022-08-olympus/src/policies/Governance.sol::227 => if (block.timestamp < activeProposal.activationTimestamp + GRACE_PERIOD) { 2022-08-olympus/src/policies/Governance.sol::231 => activeProposal = ActivatedProposal(proposalId_, block.timestamp); 2022-08-olympus/src/policies/Governance.sol::235 => emit ProposalActivated(proposalId_, block.timestamp); 2022-08-olympus/src/policies/Governance.sol::272 => if (block.timestamp < activeProposal.activationTimestamp + EXECUTION_TIMELOCK) { 2022-08-olympus/src/policies/Heart.sol::63 => lastBeat = block.timestamp; 2022-08-olympus/src/policies/Heart.sol::94 => if (block.timestamp < lastBeat + frequency()) revert Heart_OutOfCycle(); 2022-08-olympus/src/policies/Heart.sol::108 => emit Beat(block.timestamp); 2022-08-olympus/src/policies/Heart.sol::131 => lastBeat = block.timestamp - frequency(); 2022-08-olympus/src/policies/Operator.sol::128 => lastRegen: uint48(block.timestamp), 2022-08-olympus/src/policies/Operator.sol::210 => uint48(block.timestamp) >= RANGE.lastActive(true) + uint48(config_.regenWait) && 2022-08-olympus/src/policies/Operator.sol::216 => uint48(block.timestamp) >= RANGE.lastActive(false) + uint48(config_.regenWait) && 2022-08-olympus/src/policies/Operator.sol::404 => conclusion: uint48(block.timestamp + config_.cushionDuration), 2022-08-olympus/src/policies/Operator.sol::456 => conclusion: uint48(block.timestamp + config_.cushionDuration), 2022-08-olympus/src/policies/Operator.sol::708 => _status.high.lastRegen = uint48(block.timestamp); 2022-08-olympus/src/policies/Operator.sol::720 => _status.low.lastRegen = uint48(block.timestamp);

[L-03] safeApprove() is deprecated

safeApprove() is deprecated in favor of safeIncreaseAllowance() and safeDecreaseAllowance(). If only setting the initial allowance to the value that means infinite, safeIncreaseAllowance() can be used instead

2022-08-olympus/src/policies/BondCallback.sol::57 => ohm.safeApprove(address(MINTR), type(uint256).max); 2022-08-olympus/src/policies/Operator.sol::167 => ohm.safeApprove(address(MINTR), type(uint256).max);

[L-04] Open TODOs

Code architecture, incentives, and error handling/reporting questions/issues should be resolved before deployment

2022-08-olympus/src/policies/Operator.sol::657 => /// TODO determine if this should use the last price from the MA or recalculate the current price, ideally last price is ok since it should have been just updated and should include check against secondary? 2022-08-olympus/src/policies/TreasuryCustodian.sol::51 => // TODO Currently allows anyone to revoke any approval EXCEPT activated policies. 2022-08-olympus/src/policies/TreasuryCustodian.sol::52 => // TODO must reorg policy storage to be able to check for deactivated policies. 2022-08-olympus/src/policies/TreasuryCustodian.sol::56 => // TODO Make sure `policy_` is an actual policy and not a random address.

[L-05] decimals() not part of ERC20 standard

decimals() is not part of the official ERC20 standard and might fail for tokens that do not implement it. While in practice it is very unlikely, as usually most of the tokens implement it, this should still be considered as a potential issue.

2022-08-olympus/src/modules/PRICE.sol::84 => uint8 ohmEthDecimals = _ohmEthPriceFeed.decimals(); 2022-08-olympus/src/modules/PRICE.sol::87 => uint8 reserveEthDecimals = _reserveEthPriceFeed.decimals(); 2022-08-olympus/src/policies/Operator.sol::122 => ohmDecimals = tokens_[0].decimals(); 2022-08-olympus/src/policies/Operator.sol::124 => reserveDecimals = tokens_[1].decimals(); 2022-08-olympus/src/policies/Operator.sol::375 => uint256 oracleScale = 10**uint8(int8(PRICE.decimals()) - priceDecimals); 2022-08-olympus/src/policies/Operator.sol::418 => uint8 oracleDecimals = PRICE.decimals(); 2022-08-olympus/src/policies/Operator.sol::493 => return decimals - int8(PRICE.decimals()); 2022-08-olympus/src/policies/Operator.sol::754 => 10**ohmDecimals * 10**PRICE.decimals() 2022-08-olympus/src/policies/Operator.sol::764 => 10**ohmDecimals * 10**PRICE.decimals(), 2022-08-olympus/src/policies/Operator.sol::784 => 10**ohmDecimals * 10**PRICE.decimals(),

[L-06] Unsafe use of transfer()/transferFrom() with IERC20

Some tokens do not implement the ERC20 standard properly but are still accepted by most code that accepts ERC20 tokens. For example Tether (USDT)'s transfer() and transferFrom() functions do not return booleans as the specification requires, and instead have no return value. When these sorts of tokens are cast to IERC20, their function signatures do not match and therefore the calls made, revert. Use OpenZeppelin’s SafeERC20's safeTransfer()/safeTransferFrom() instead

2022-08-olympus/src/policies/Governance.sol::259 => VOTES.transferFrom(msg.sender, address(this), userVotes); 2022-08-olympus/src/policies/Governance.sol::312 => VOTES.transferFrom(address(this), msg.sender, userVotes);

[L-07] Events not emitted for important state changes

When changing state variables events are not emitted. Emitting events allows monitoring activities with off-chain monitoring tools.

2022-08-olympus/src/Kernel.sol::126 => function setActiveStatus(bool activate_) external onlyKernel { 2022-08-olympus/src/policies/BondCallback.sol::190 => function setOperator(Operator operator_) external onlyRole("callback_admin") { 2022-08-olympus/src/policies/Operator.sol::498 => function setSpreads(uint256 cushionSpread_, uint256 wallSpread_) 2022-08-olympus/src/policies/Operator.sol::510 => function setThresholdFactor(uint256 thresholdFactor_) external onlyRole("operator_policy") { 2022-08-olympus/src/policies/Operator.sol::586 => function setBondContracts(IBondAuctioneer auctioneer_, IBondCallback callback_) 2022-08-olympus/src/policies/interfaces/IHeart.sol::34 => function setRewardTokenAndAmount(ERC20 token_, uint256 reward_) external; 2022-08-olympus/src/policies/interfaces/IOperator.sol::74 => function setSpreads(uint256 cushionSpread_, uint256 wallSpread_) external; 2022-08-olympus/src/policies/interfaces/IOperator.sol::80 => function setThresholdFactor(uint256 thresholdFactor_) external; 2022-08-olympus/src/policies/interfaces/IOperator.sol::85 => function setCushionFactor(uint32 cushionFactor_) external; 2022-08-olympus/src/policies/interfaces/IOperator.sol::92 => function setCushionParams( 2022-08-olympus/src/policies/interfaces/IOperator.sol::101 => function setReserveFactor(uint32 reserveFactor_) external; 2022-08-olympus/src/policies/interfaces/IOperator.sol::109 => function setRegenParams( 2022-08-olympus/src/policies/interfaces/IOperator.sol::119 => function setBondContracts(IBondAuctioneer auctioneer_, IBondCallback callback_) external;

[L-08] _safeMint() should be used rather than _mint() wherever possible

Some tokens do not implement the ERC20 standard properly but are still accepted by most code that accepts ERC20 tokens. For example Tether (USDT)'s transfer() and transferFrom() functions do not return booleans as the specification requires, and instead have no return value. When these sorts of tokens are cast to IERC20, their function signatures do not match and therefore the calls made, revert. Use OpenZeppelin’s SafeERC20's safeTransfer()/safeTransferFrom() instead

2022-08-olympus/src/modules/VOTES.sol::36 => _mint(wallet_, amount_);

[L-09] Missing check for 0 transfer

Some ERC20 tokens will revert when transferring zero. A require statement should be added to check that amount_ is not 0

https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/modules/TRSRY.sol#L82 https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/modules/TRSRY.sol#L99 https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/modules/TRSRY.sol#L110

[L-10] Lack of zero address check

https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/modules/MINTR.sol#L16 https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/modules/RANGE.sol#L102-L103 https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/modules/PRICE.sol#L83 https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/modules/PRICE.sol#L86 https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/policies/Operator.sol#L119-L121 https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/policies/BondCallback.sol#L43-L44

[L-11] Unsafe cast

Precision may be lost when casting uint48 to uint32

https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/modules/PRICE.sol#L97

[L-12] RoundID not validatated

When getting prices using latestRoundData(), roundID should also be validated against answeredInRound

eg

(uint80 roundID, int256 _price, , uint256 updatedAt, uint80 answeredInRound) = feed.latestRoundData(); require(answeredInRound >= roundID, "ChainLink: Stale price"); require(updatedAt != 0, "ChainLink: Round not complete");

https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/modules/PRICE.sol#L161 https://github.com/code-423n4/2022-08-olympus/blob/2a0b515012b4a40076f6eac487f7816aafb8724a/src/modules/PRICE.sol#L170

[N-01] Use a more recent version of solidity

Use a solidity version of at least 0.8.4 to get bytes.concat() instead of abi.encodePacked(<bytes>,<bytes>) Use a solidity version of at least 0.8.12 to get string.concat() instead of abi.encodePacked(<str>,<str>) Use a solidity version of at least 0.8.13 to get the ability to use using for with a list of free functions

2022-08-olympus/src/policies/interfaces/IHeart.sol::2 => pragma solidity >=0.8.0; 2022-08-olympus/src/policies/interfaces/IOperator.sol::2 => pragma solidity >=0.8.0;

[N-02] Large multiples of ten should use scientific notation

Use (e.g. 1e6) rather than decimal literals (e.g. 1000000), for better code readability

2022-08-olympus/src/modules/RANGE.sol::245 => wallSpread_ > 10000 || 2022-08-olympus/src/modules/RANGE.sol::247 => cushionSpread_ > 10000 || 2022-08-olympus/src/modules/RANGE.sol::264 => if (thresholdFactor_ > 10000 || thresholdFactor_ < 100) revert RANGE_InvalidParams(); 2022-08-olympus/src/policies/Governance.sol::164 => if (VOTES.balanceOf(msg.sender) * 10000 < VOTES.totalSupply() * SUBMISSION_REQUIREMENT) 2022-08-olympus/src/policies/Operator.sol::111 => if (configParams[4] > 10000 || configParams[4] < 100) revert Operator_InvalidParams(); 2022-08-olympus/src/policies/Operator.sol::518 => if (cushionFactor_ > 10000 || cushionFactor_ < 100) revert Operator_InvalidParams(); 2022-08-olympus/src/policies/Operator.sol::550 => if (reserveFactor_ > 10000 || reserveFactor_ < 100) revert Operator_InvalidParams();

[N-03] Event is missing indexed fields

Each event should use three indexed fields if there are three or more fields

2022-08-olympus/src/modules/INSTR.sol::11 => event InstructionsStored(uint256 instructionsId); 2022-08-olympus/src/modules/PRICE.sol::26 => event NewObservation(uint256 timestamp_, uint256 price_, uint256 movingAverage_); 2022-08-olympus/src/modules/PRICE.sol::27 => event MovingAverageDurationChanged(uint48 movingAverageDuration_); 2022-08-olympus/src/modules/PRICE.sol::28 => event ObservationFrequencyChanged(uint48 observationFrequency_); 2022-08-olympus/src/modules/RANGE.sol::20 => event WallUp(bool high_, uint256 timestamp_, uint256 capacity_); 2022-08-olympus/src/modules/RANGE.sol::21 => event WallDown(bool high_, uint256 timestamp_, uint256 capacity_); 2022-08-olympus/src/modules/RANGE.sol::22 => event CushionUp(bool high_, uint256 timestamp_, uint256 capacity_); 2022-08-olympus/src/modules/RANGE.sol::23 => event CushionDown(bool high_, uint256 timestamp_); 2022-08-olympus/src/modules/RANGE.sol::30 => event SpreadsChanged(uint256 cushionSpread_, uint256 wallSpread_); 2022-08-olympus/src/modules/RANGE.sol::31 => event ThresholdFactorChanged(uint256 thresholdFactor_); 2022-08-olympus/src/policies/Governance.sol::86 => event ProposalSubmitted(uint256 proposalId); 2022-08-olympus/src/policies/Governance.sol::87 => event ProposalEndorsed(uint256 proposalId, address voter, uint256 amount); 2022-08-olympus/src/policies/Governance.sol::88 => event ProposalActivated(uint256 proposalId, uint256 timestamp); 2022-08-olympus/src/policies/Governance.sol::89 => event WalletVoted(uint256 proposalId, address voter, bool for_, uint256 userVotes); 2022-08-olympus/src/policies/Governance.sol::90 => event ProposalExecuted(uint256 proposalId); 2022-08-olympus/src/policies/Heart.sol::28 => event Beat(uint256 timestamp_); 2022-08-olympus/src/policies/Heart.sol::29 => event RewardIssued(address to_, uint256 rewardAmount_); 2022-08-olympus/src/policies/Heart.sol::30 => event RewardUpdated(ERC20 token_, uint256 rewardAmount_); 2022-08-olympus/src/policies/Operator.sol::51 => event CushionFactorChanged(uint32 cushionFactor_); 2022-08-olympus/src/policies/Operator.sol::52 => event CushionParamsChanged(uint32 duration_, uint32 debtBuffer_, uint32 depositInterval_); 2022-08-olympus/src/policies/Operator.sol::53 => event ReserveFactorChanged(uint32 reserveFactor_); 2022-08-olympus/src/policies/Operator.sol::54 => event RegenParamsChanged(uint32 wait_, uint32 threshold_, uint32 observe_);

[N-04] Missing NatSpec

Code should include NatSpec

2022-08-olympus/src/policies/TreasuryCustodian.sol::1 => // SPDX-License-Identifier: AGPL-3.0 2022-08-olympus/src/utils/KernelUtils.sol::1 => // SPDX-License-Identifier: AGPL-3.0-only

[N-05] Constants should be defined rather than using magic numbers

It is bad practice to use numbers directly in code without explanation

2022-08-olympus/src/modules/PRICE.sol::90 => if (exponent > 38) revert Price_InvalidParams(); 2022-08-olympus/src/modules/RANGE.sol::245 => wallSpread_ > 10000 || 2022-08-olympus/src/modules/RANGE.sol::246 => wallSpread_ < 100 || 2022-08-olympus/src/modules/RANGE.sol::247 => cushionSpread_ > 10000 || 2022-08-olympus/src/modules/RANGE.sol::248 => cushionSpread_ < 100 || 2022-08-olympus/src/modules/RANGE.sol::264 => if (thresholdFactor_ > 10000 || thresholdFactor_ < 100) revert RANGE_InvalidParams(); 2022-08-olympus/src/policies/Governance.sol::164 => if (VOTES.balanceOf(msg.sender) * 10000 < VOTES.totalSupply() * SUBMISSION_REQUIREMENT) 2022-08-olympus/src/policies/Governance.sol::217 => (totalEndorsementsForProposal[proposalId_] * 100) < 2022-08-olympus/src/policies/Governance.sol::268 => if (netVotes * 100 < VOTES.totalSupply() * EXECUTION_THRESHOLD) { 2022-08-olympus/src/policies/Operator.sol::111 => if (configParams[4] > 10000 || configParams[4] < 100) revert Operator_InvalidParams(); 2022-08-olympus/src/policies/Operator.sol::378 => 36 + scaleAdjustment + int8(reserveDecimals) - int8(ohmDecimals) - priceDecimals 2022-08-olympus/src/policies/Operator.sol::433 => 36 + scaleAdjustment + int8(ohmDecimals) - int8(reserveDecimals) - priceDecimals 2022-08-olympus/src/policies/Operator.sol::518 => if (cushionFactor_ > 10000 || cushionFactor_ < 100) revert Operator_InvalidParams(); 2022-08-olympus/src/policies/Operator.sol::550 => if (reserveFactor_ > 10000 || reserveFactor_ < 100) revert Operator_InvalidParams(); 2022-08-olympus/src/utils/KernelUtils.sol::58 => for (uint256 i = 0; i < 32; ) {

[N-06] Public functions not called by the contract should be declared external instead

Contracts are allowed to override their parents' functions and change the visibility from external to public.

2022-08-olympus/src/Kernel.sol::439 => function grantRole(Role role_, address addr_) public onlyAdmin { 2022-08-olympus/src/Kernel.sol::451 => function revokeRole(Role role_, address addr_) public onlyAdmin { 2022-08-olympus/src/modules/INSTR.sol::28 => function VERSION() public pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/modules/INSTR.sol::37 => function getInstructions(uint256 instructionsId_) public view returns (Instruction[] memory) { 2022-08-olympus/src/modules/MINTR.sol::20 => function KEYCODE() public pure override returns (Keycode) { 2022-08-olympus/src/modules/MINTR.sol::33 => function mintOhm(address to_, uint256 amount_) public permissioned { 2022-08-olympus/src/modules/MINTR.sol::37 => function burnOhm(address from_, uint256 amount_) public permissioned { 2022-08-olympus/src/modules/PRICE.sol::108 => function KEYCODE() public pure override returns (Keycode) { 2022-08-olympus/src/modules/RANGE.sol::110 => function KEYCODE() public pure override returns (Keycode) { 2022-08-olympus/src/modules/TRSRY.sol::47 => function KEYCODE() public pure override returns (Keycode) { 2022-08-olympus/src/modules/VOTES.sol::22 => function KEYCODE() public pure override returns (Keycode) { 2022-08-olympus/src/modules/VOTES.sol::45 => function transfer(address to_, uint256 amount_) public pure override returns (bool) { 2022-08-olympus/src/policies/Governance.sol::145 => function getMetadata(uint256 proposalId_) public view returns (ProposalMetadata memory) { 2022-08-olympus/src/policies/Governance.sol::151 => function getActiveProposal() public view returns (ActivatedProposal memory) {

[N-07] Adding a return statement when the function defines a named return variable, is redundant

It is not necessary to have both a named return and a return statement.

2022-08-olympus/src/modules/INSTR.sol::28 => function VERSION() public pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/modules/MINTR.sol::25 => function VERSION() external pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/modules/PRICE.sol::113 => function VERSION() external pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/modules/RANGE.sol::115 => function VERSION() external pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/modules/TRSRY.sol::51 => function VERSION() external pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/modules/VOTES.sol::27 => function VERSION() external pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/policies/BondCallback.sol::177 => returns (uint256 in_, uint256 out_)

[N-08] Missing event for critical parameter change

Emitting events after sensitive changes take place, to facilitate tracking and notify off-chain clients following changes to the contract.

2022-08-olympus/src/Kernel.sol::126 => function setActiveStatus(bool activate_) external onlyKernel { 2022-08-olympus/src/policies/BondCallback.sol::190 => function setOperator(Operator operator_) external onlyRole("callback_admin") { 2022-08-olympus/src/policies/Operator.sol::498 => function setSpreads(uint256 cushionSpread_, uint256 wallSpread_) 2022-08-olympus/src/policies/Operator.sol::510 => function setThresholdFactor(uint256 thresholdFactor_) external onlyRole("operator_policy") { 2022-08-olympus/src/policies/Operator.sol::586 => function setBondContracts(IBondAuctioneer auctioneer_, IBondCallback callback_) 2022-08-olympus/src/policies/interfaces/IHeart.sol::34 => function setRewardTokenAndAmount(ERC20 token_, uint256 reward_) external; 2022-08-olympus/src/policies/interfaces/IOperator.sol::74 => function setSpreads(uint256 cushionSpread_, uint256 wallSpread_) external; 2022-08-olympus/src/policies/interfaces/IOperator.sol::80 => function setThresholdFactor(uint256 thresholdFactor_) external; 2022-08-olympus/src/policies/interfaces/IOperator.sol::85 => function setCushionFactor(uint32 cushionFactor_) external; 2022-08-olympus/src/policies/interfaces/IOperator.sol::92 => function setCushionParams( 2022-08-olympus/src/policies/interfaces/IOperator.sol::101 => function setReserveFactor(uint32 reserveFactor_) external; 2022-08-olympus/src/policies/interfaces/IOperator.sol::109 => function setRegenParams( 2022-08-olympus/src/policies/interfaces/IOperator.sol::119 => function setBondContracts(IBondAuctioneer auctioneer_, IBondCallback callback_) external;

[N-09] Lines are too long

Usually lines in source code are limited to 80 characters. Today's screens are much larger so it's reasonable to stretch this in some cases. Since the files will most likely reside in GitHub, and GitHub starts using a scroll bar in all cases when the length is over 164 characters, the lines below should be split when they reach that length

2022-08-olympus/src/modules/PRICE.sol::31 => /// @dev Price feeds. Chainlink typically provides price feeds for an asset in ETH. Therefore, we use two price feeds against ETH, one for OHM and one for the Reserve asset, to calculate the relative price of OHM in the Reserve asset. 2022-08-olympus/src/modules/RANGE.sol::40 => uint256 spread; // Spread of the band (increase/decrease from the moving average to set the band prices), percent with 2 decimal places (i.e. 1000 = 10% spread) 2022-08-olympus/src/modules/RANGE.sol::46 => uint256 capacity; // Amount of tokens that can be used to defend the side of the range. Specified in OHM tokens on the high side and Reserve tokens on the low side. 2022-08-olympus/src/modules/RANGE.sol::61 => /// @notice Threshold factor for the change, a percent in 2 decimals (i.e. 1000 = 10%). Determines how much of the capacity must be spent before the side is taken down. 2022-08-olympus/src/policies/Operator.sol::97 => uint32[8] memory configParams // [cushionFactor, cushionDuration, cushionDebtBuffer, cushionDepositInterval, reserveFactor, regenWait, regenThreshold, regenObserve] 2022-08-olympus/src/policies/Operator.sol::657 => /// TODO determine if this should use the last price from the MA or recalculate the current price, ideally last price is ok since it should have been just updated and should include check against secondary? 2022-08-olympus/src/policies/interfaces/IOperator.sol::15 => uint32 cushionDebtBuffer; // Percentage over the initial debt to allow the market to accumulate at any one time. Percent with 3 decimals, e.g. 1_000 = 1 %. See IBondAuctioneer for more info. 2022-08-olympus/src/policies/interfaces/IOperator.sol::90 => /// @param debtBuffer_ - Percentage over the initial debt to allow the market to accumulate at any one time. Percent with 3 decimals, e.g. 1_000 = 1 %. See IBondAuctioneer for more info.

#0 - ind-igo

2022-09-09T04:04:17Z

Great report, thank you.

[G-01] Don't Initialize Variables with Default Value

Uninitialized variables are assigned with the types default value. Explicitly initializing a variable with it's default value costs unnecesary gas.

2022-08-olympus/src/Kernel.sol::397 => for (uint256 i = 0; i < reqLength; ) { 2022-08-olympus/src/utils/KernelUtils.sol::43 => for (uint256 i = 0; i < 5; ) { 2022-08-olympus/src/utils/KernelUtils.sol::58 => for (uint256 i = 0; i < 32; ) {

[G-02] Cache Array Length Outside of Loop

Caching the array length outside a loop saves reading it on each iteration, as long as the array's length is not changed during the loop.

2022-08-olympus/src/policies/Governance.sol::278 => for (uint256 step; step < instructions.length; ) {

[G-03] Use Shift Right/Left instead of Division/Multiplication if possible

A division/multiplication by any number x being a power of 2 can be calculated by shifting log2(x) to the right/left.

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.

2022-08-olympus/src/policies/Operator.sol::372 => int8 scaleAdjustment = int8(ohmDecimals) - int8(reserveDecimals) + (priceDecimals / 2); 2022-08-olympus/src/policies/Operator.sol::419 => uint256 invCushionPrice = 10**(oracleDecimals * 2) / range.cushion.low.price; 2022-08-olympus/src/policies/Operator.sol::420 => uint256 invWallPrice = 10**(oracleDecimals * 2) / range.wall.low.price; 2022-08-olympus/src/policies/Operator.sol::427 => int8 scaleAdjustment = int8(reserveDecimals) - int8(ohmDecimals) + (priceDecimals / 2);

[G-04] Using private rather than public for constants, saves gas

If needed, the value can be read from the verified contract source code. Savings are due to the compiler not having to create non-payable getter functions for deployment calldata, and not adding another entry to the method ID table

2022-08-olympus/src/modules/MINTR.sol::9 => OHM public immutable ohm; 2022-08-olympus/src/modules/RANGE.sol::68 => ERC20 public immutable ohm; 2022-08-olympus/src/modules/RANGE.sol::71 => ERC20 public immutable reserve; 2022-08-olympus/src/policies/Operator.sol::82 => ERC20 public immutable ohm; 2022-08-olympus/src/policies/Operator.sol::83 => uint8 public immutable ohmDecimals; 2022-08-olympus/src/policies/Operator.sol::85 => ERC20 public immutable reserve; 2022-08-olympus/src/policies/Operator.sol::86 => uint8 public immutable reserveDecimals;

[G-05] Functions guaranteed to revert when called by normal users can be marked payable

If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. 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 21 gas per call to the function, in addition to the extra deployment cost

2022-08-olympus/src/Kernel.sol::76 => function changeKernel(Kernel newKernel_) external onlyKernel { 2022-08-olympus/src/Kernel.sol::105 => function INIT() external virtual onlyKernel {} 2022-08-olympus/src/Kernel.sol::126 => function setActiveStatus(bool activate_) external onlyKernel { 2022-08-olympus/src/Kernel.sol::235 => function executeAction(Actions action_, address target_) external onlyExecutor { 2022-08-olympus/src/Kernel.sol::439 => function grantRole(Role role_, address addr_) public onlyAdmin { 2022-08-olympus/src/Kernel.sol::451 => function revokeRole(Role role_, address addr_) public onlyAdmin { 2022-08-olympus/src/policies/BondCallback.sol::152 => function batchToTreasury(ERC20[] memory tokens_) external onlyRole("callback_admin") { 2022-08-olympus/src/policies/BondCallback.sol::190 => function setOperator(Operator operator_) external onlyRole("callback_admin") { 2022-08-olympus/src/policies/Heart.sol::130 => function resetBeat() external onlyRole("heart_admin") { 2022-08-olympus/src/policies/Heart.sol::135 => function toggleBeat() external onlyRole("heart_admin") { 2022-08-olympus/src/policies/Heart.sol::150 => function withdrawUnspentRewards(ERC20 token_) external onlyRole("heart_admin") { 2022-08-olympus/src/policies/Operator.sol::195 => function operate() external override onlyWhileActive onlyRole("operator_operate") { 2022-08-olympus/src/policies/Operator.sol::510 => function setThresholdFactor(uint256 thresholdFactor_) external onlyRole("operator_policy") { 2022-08-olympus/src/policies/Operator.sol::516 => function setCushionFactor(uint32 cushionFactor_) external onlyRole("operator_policy") { 2022-08-olympus/src/policies/Operator.sol::548 => function setReserveFactor(uint32 reserveFactor_) external onlyRole("operator_policy") { 2022-08-olympus/src/policies/Operator.sol::618 => function regenerate(bool high_) external onlyRole("operator_admin") { 2022-08-olympus/src/policies/Operator.sol::624 => function toggleActive() external onlyRole("operator_admin") { 2022-08-olympus/src/policies/VoterRegistration.sol::45 => function issueVotesTo(address wallet_, uint256 amount_) external onlyRole("voter_admin") { 2022-08-olympus/src/policies/VoterRegistration.sol::53 => function revokeVotesFrom(address wallet_, uint256 amount_) external onlyRole("voter_admin") {

[G-06] Empty blocks should be removed or emit something

The code should be refactored such that they no longer exist, or the block should do something useful, such as emitting an event or reverting.

2022-08-olympus/src/Kernel.sol::85 => constructor(Kernel kernel_) KernelAdapter(kernel_) {} 2022-08-olympus/src/Kernel.sol::95 => function KEYCODE() public pure virtual returns (Keycode) {} 2022-08-olympus/src/Kernel.sol::100 => function VERSION() external pure virtual returns (uint8 major, uint8 minor) {} 2022-08-olympus/src/Kernel.sol::105 => function INIT() external virtual onlyKernel {} 2022-08-olympus/src/Kernel.sol::115 => constructor(Kernel kernel_) KernelAdapter(kernel_) {} 2022-08-olympus/src/Kernel.sol::139 => function configureDependencies() external virtual returns (Keycode[] memory dependencies) {} 2022-08-olympus/src/Kernel.sol::143 => function requestPermissions() external view virtual returns (Permissions[] memory requests) {} 2022-08-olympus/src/modules/INSTR.sol::20 => constructor(Kernel kernel_) Module(kernel_) {} 2022-08-olympus/src/modules/TRSRY.sol::45 => constructor(Kernel kernel_) Module(kernel_) {} 2022-08-olympus/src/modules/VOTES.sol::19 => {} 2022-08-olympus/src/policies/Governance.sol::59 => constructor(Kernel kernel_) Policy(kernel_) {} 2022-08-olympus/src/policies/PriceConfig.sol::15 => constructor(Kernel kernel_) Policy(kernel_) {} 2022-08-olympus/src/policies/TreasuryCustodian.sol::24 => constructor(Kernel kernel_) Policy(kernel_) {} 2022-08-olympus/src/policies/VoterRegistration.sol::16 => constructor(Kernel kernel_) Policy(kernel_) {}

[G-07] Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead

When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.

2022-08-olympus/src/modules/PRICE.sol::44 => uint32 public nextObsIndex; 2022-08-olympus/src/modules/PRICE.sol::47 => uint32 public numObservations; 2022-08-olympus/src/modules/PRICE.sol::59 => uint8 public constant decimals = 18; 2022-08-olympus/src/modules/PRICE.sol::84 => uint8 ohmEthDecimals = _ohmEthPriceFeed.decimals(); 2022-08-olympus/src/modules/PRICE.sol::87 => uint8 reserveEthDecimals = _reserveEthPriceFeed.decimals(); 2022-08-olympus/src/modules/PRICE.sol::127 => uint32 numObs = numObservations; 2022-08-olympus/src/modules/PRICE.sol::185 => uint32 lastIndex = nextObsIndex == 0 ? numObservations - 1 : nextObsIndex - 1; 2022-08-olympus/src/policies/Operator.sol::83 => uint8 public immutable ohmDecimals; 2022-08-olympus/src/policies/Operator.sol::86 => uint8 public immutable reserveDecimals; 2022-08-olympus/src/policies/Operator.sol::89 => uint32 public constant FACTOR_SCALE = 1e4; 2022-08-olympus/src/policies/Operator.sol::485 => int8 decimals; 2022-08-olympus/src/policies/Operator.sol::665 => uint32 observe = _config.regenObserve; 2022-08-olympus/src/policies/interfaces/IOperator.sol::13 => uint32 cushionFactor; // percent of capacity to be used for a single cushion deployment, assumes 2 decimals (i.e. 1000 = 10%) 2022-08-olympus/src/policies/interfaces/IOperator.sol::14 => uint32 cushionDuration; // duration of a single cushion deployment in seconds 2022-08-olympus/src/policies/interfaces/IOperator.sol::15 => uint32 cushionDebtBuffer; // Percentage over the initial debt to allow the market to accumulate at any one time. Percent with 3 decimals, e.g. 1_000 = 1 %. See IBondAuctioneer for more info. 2022-08-olympus/src/policies/interfaces/IOperator.sol::16 => uint32 cushionDepositInterval; // Target frequency of deposits. Determines max payout of the bond market. See IBondAuctioneer for more info. 2022-08-olympus/src/policies/interfaces/IOperator.sol::17 => uint32 reserveFactor; // percent of reserves in treasury to be used for a single wall, assumes 2 decimals (i.e. 1000 = 10%) 2022-08-olympus/src/policies/interfaces/IOperator.sol::18 => uint32 regenWait; // minimum duration to wait to reinstate a wall in seconds 2022-08-olympus/src/policies/interfaces/IOperator.sol::19 => uint32 regenThreshold; // number of price points on other side of moving average to reinstate a wall 2022-08-olympus/src/policies/interfaces/IOperator.sol::20 => uint32 regenObserve; // number of price points to observe to determine regeneration 2022-08-olympus/src/policies/interfaces/IOperator.sol::31 => uint32 count; // current number of price points that count towards regeneration 2022-08-olympus/src/policies/interfaces/IOperator.sol::33 => uint32 nextObservation; // index of the next observation in the observations array

[G-08] Using bools for storage incurs overhead

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. Use uint256(1) and uint256(2) for true/false instead

2022-08-olympus/src/Kernel.sol::113 => bool public isActive; 2022-08-olympus/src/Kernel.sol::181 => mapping(Keycode => mapping(Policy => mapping(bytes4 => bool))) public modulePermissions; 2022-08-olympus/src/Kernel.sol::194 => mapping(address => mapping(Role => bool)) public hasRole; 2022-08-olympus/src/Kernel.sol::197 => mapping(Role => bool) public isRole; 2022-08-olympus/src/modules/PRICE.sol::62 => bool public initialized; 2022-08-olympus/src/policies/BondCallback.sol::24 => mapping(address => mapping(uint256 => bool)) public approvedMarkets; 2022-08-olympus/src/policies/Governance.sol::105 => mapping(uint256 => bool) public proposalHasBeenActivated; 2022-08-olympus/src/policies/Governance.sol::117 => mapping(uint256 => mapping(address => bool)) public tokenClaimsForProposal; 2022-08-olympus/src/policies/Heart.sol::33 => bool public active; 2022-08-olympus/src/policies/Operator.sol::63 => bool public initialized; 2022-08-olympus/src/policies/Operator.sol::66 => bool public active;

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

use <x> = <x> + <y> or <x> = <x> - <y> instead to save gas

2022-08-olympus/src/modules/PRICE.sol::136 => _movingAverage += (currentPrice - earliestPrice) / numObs; 2022-08-olympus/src/modules/PRICE.sol::138 => _movingAverage -= (earliestPrice - currentPrice) / numObs; 2022-08-olympus/src/modules/PRICE.sol::222 => total += startObservations_[i]; 2022-08-olympus/src/modules/TRSRY.sol::96 => reserveDebt[token_][msg.sender] += amount_; 2022-08-olympus/src/modules/TRSRY.sol::97 => totalDebt[token_] += amount_; 2022-08-olympus/src/modules/TRSRY.sol::115 => reserveDebt[token_][msg.sender] -= received; 2022-08-olympus/src/modules/TRSRY.sol::116 => totalDebt[token_] -= received; 2022-08-olympus/src/modules/TRSRY.sol::131 => if (oldDebt < amount_) totalDebt[token_] += amount_ - oldDebt; 2022-08-olympus/src/modules/TRSRY.sol::132 => else totalDebt[token_] -= oldDebt - amount_; 2022-08-olympus/src/modules/VOTES.sol::56 => balanceOf[from_] -= amount_; 2022-08-olympus/src/modules/VOTES.sol::58 => balanceOf[to_] += amount_; 2022-08-olympus/src/policies/BondCallback.sol::143 => _amountsPerMarket[id_][0] += inputAmount_; 2022-08-olympus/src/policies/BondCallback.sol::144 => _amountsPerMarket[id_][1] += outputAmount_; 2022-08-olympus/src/policies/Governance.sol::194 => totalEndorsementsForProposal[proposalId_] -= previousEndorsement; 2022-08-olympus/src/policies/Governance.sol::198 => totalEndorsementsForProposal[proposalId_] += userVotes; 2022-08-olympus/src/policies/Governance.sol::252 => yesVotesForProposal[activeProposal.proposalId] += userVotes; 2022-08-olympus/src/policies/Governance.sol::254 => noVotesForProposal[activeProposal.proposalId] += userVotes; 2022-08-olympus/src/policies/Heart.sol::103 => lastBeat += frequency();

[G-11] Use a more recent version of solidity

Use a solidity version of at least 0.8.2 to get compiler automatic inlining Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require() strings Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value

2022-08-olympus/src/policies/interfaces/IHeart.sol::2 => pragma solidity >=0.8.0; 2022-08-olympus/src/policies/interfaces/IOperator.sol::2 => pragma solidity >=0.8.0;

[G-12] Prefix increments cheaper than Postfix increments

++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too) Saves 5 gas PER LOOP

2022-08-olympus/src/utils/KernelUtils.sol::49 => i++; 2022-08-olympus/src/utils/KernelUtils.sol::64 => i++;

[G-13] Don't compare boolean expressions to boolean literals

The extran comparison wastes gas if (<x> == true) => if (<x>), if (<x> == false) => if (!<x>)

2022-08-olympus/src/policies/Governance.sol::223 => if (proposalHasBeenActivated[proposalId_] == true) { 2022-08-olympus/src/policies/Governance.sol::306 => if (tokenClaimsForProposal[proposalId_][msg.sender] == true) {

[G-14] Public functions not called by the contract should be declared external instead

Contracts are allowed to override their parents' functions and change the visibility from external to public and can save gas by doing so.

2022-08-olympus/src/Kernel.sol::439 => function grantRole(Role role_, address addr_) public onlyAdmin { 2022-08-olympus/src/Kernel.sol::451 => function revokeRole(Role role_, address addr_) public onlyAdmin { 2022-08-olympus/src/modules/INSTR.sol::28 => function VERSION() public pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/modules/INSTR.sol::37 => function getInstructions(uint256 instructionsId_) public view returns (Instruction[] memory) { 2022-08-olympus/src/modules/MINTR.sol::20 => function KEYCODE() public pure override returns (Keycode) { 2022-08-olympus/src/modules/MINTR.sol::33 => function mintOhm(address to_, uint256 amount_) public permissioned { 2022-08-olympus/src/modules/MINTR.sol::37 => function burnOhm(address from_, uint256 amount_) public permissioned { 2022-08-olympus/src/modules/PRICE.sol::108 => function KEYCODE() public pure override returns (Keycode) { 2022-08-olympus/src/modules/RANGE.sol::110 => function KEYCODE() public pure override returns (Keycode) { 2022-08-olympus/src/modules/TRSRY.sol::47 => function KEYCODE() public pure override returns (Keycode) { 2022-08-olympus/src/modules/VOTES.sol::22 => function KEYCODE() public pure override returns (Keycode) { 2022-08-olympus/src/modules/VOTES.sol::45 => function transfer(address to_, uint256 amount_) public pure override returns (bool) { 2022-08-olympus/src/policies/Governance.sol::145 => function getMetadata(uint256 proposalId_) public view returns (ProposalMetadata memory) { 2022-08-olympus/src/policies/Governance.sol::151 => function getActiveProposal() public view returns (ActivatedProposal memory) {

[G-15] Not using the named return variables when a function returns, wastes deployment gas

It is not necessary to have both a named return and a return statement.

2022-08-olympus/src/modules/INSTR.sol::28 => function VERSION() public pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/modules/MINTR.sol::25 => function VERSION() external pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/modules/PRICE.sol::113 => function VERSION() external pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/modules/RANGE.sol::115 => function VERSION() external pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/modules/TRSRY.sol::51 => function VERSION() external pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/modules/VOTES.sol::27 => function VERSION() external pure override returns (uint8 major, uint8 minor) { 2022-08-olympus/src/policies/BondCallback.sol::177 => returns (uint256 in_, uint256 out_)

[G-16] Multiple address mappings can be combined into a single mapping of an address to a struct, where appropriate

Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.

2022-08-olympus/src/modules/TRSRY.sol::33 => mapping(address => mapping(ERC20 => uint256)) public withdrawApproval; 2022-08-olympus/src/modules/TRSRY.sol::39 => mapping(ERC20 => mapping(address => uint256)) public reserveDebt;
2022-08-olympus/src/policies/Governance.sol::102 => mapping(uint256 => mapping(address => uint256)) public userEndorsementsForProposal; 2022-08-olympus/src/policies/Governance.sol::114 => mapping(uint256 => mapping(address => uint256)) public userVotesForProposal; 2022-08-olympus/src/policies/Governance.sol::117 => mapping(uint256 => mapping(address => bool)) public tokenClaimsForProposal;

[G-17] Use assembly to check for address(0)

Saves 6 gas per instance if using assembly to check for address(0)

e.g.

assembly { if iszero(_addr) { mstore(0x00, "zero address") revert(0x00, 0x20) } }

instances:

2022-08-olympus/src/Kernel.sol::269 => if (address(getModuleForKeycode[keycode]) != address(0))

[G-18] Using storage instead of memory for structs/arrays saves gas

When fetching data from a storage location, assigning the data to a memory variable causes all fields of the struct/array to be read from storage, which incurs a Gcoldsload (2100 gas) for each field of the struct/array. If the fields are read from the new memory variable, they incur an additional MLOAD rather than a cheap stack read.

Instead of declearing the variable with the memory keyword, declaring the variable with the storage keyword and caching any fields that need to be re-read in stack variables, will be much cheaper, only incuring the Gcoldsload for the fields actually read. The only time it makes sense to read the whole struct/array into a memory variable, is if the full struct/array is being returned by the function, is being passed to a function that requires memory, or if the array/struct is being read from another memory array/struct

2022-08-olympus/src/Kernel.sol::379 => Policy[] memory dependents = moduleDependents[keycode_];

[G-19] internal functions only called once can be inlined to save gas

Not inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls.

2022-08-olympus/src/Kernel.sol::131 => function getModuleAddress(Keycode keycode_) internal view returns (address) { 2022-08-olympus/src/Kernel.sol::266 => function _installModule(Module newModule_) internal { 2022-08-olympus/src/Kernel.sol::279 => function _upgradeModule(Module newModule_) internal { 2022-08-olympus/src/Kernel.sol::295 => function _activatePolicy(Policy policy_) internal { 2022-08-olympus/src/Kernel.sol::325 => function _deactivatePolicy(Policy policy_) internal { 2022-08-olympus/src/Kernel.sol::351 => function _migrateKernel(Kernel newKernel_) internal { 2022-08-olympus/src/Kernel.sol::378 => function _reconfigurePolicies(Keycode keycode_) internal { 2022-08-olympus/src/Kernel.sol::409 => function _pruneFromDependents(Policy policy_) internal { 2022-08-olympus/src/policies/Heart.sol::111 => function _issueReward(address to_) internal { 2022-08-olympus/src/policies/Operator.sol::652 => function _addObservation() internal {

[G-20] State variables only set in the constructor should be declared immutable

Avoids a Gsset (20000 gas) in the constructor, and replaces each Gwarmacces (100 gas) with a PUSH32 (3 gas).

2022-08-olympus/src/policies/BondCallback.sol::43 => aggregator = aggregator_; 2022-08-olympus/src/policies/BondCallback.sol::44 => ohm = ohm_; 2022-08-olympus/src/policies/Heart.sol::60 => _operator = operator_; 2022-08-olympus/src/policies/Operator.sol::144 => _status = Status({low: regen, high: regen});

[G-21] internal functions not called by the contract should be removed to save deployment gas

If the functions are required by an interface, the contract should inherit from that interface and use the override keyword

2022-08-olympus/src/Kernel.sol::131 => function getModuleAddress(Keycode keycode_) internal view returns (address) {
AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter