Olympus DAO contest - gogo'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: 58/147

Findings: 2

Award: $97.62

๐ŸŒŸ Selected for report: 0

๐Ÿš€ Solo Findings: 0

2022-08-OLYMPUS

Low Risk and Non-Critical Issues

Events not emmited on important state changes

Emmiting events is recommended each time when a state variable's value is being changed or just some critical event for the contract has occurred. It also helps off-chain monitoring of the contract's state.

There are 13 instances of this issue:

File: src/Kernel.sol

139:  function configureDependencies() external virtual returns (Keycode[] memory dependencies) {}

143:  function requestPermissions() external view virtual returns (Permissions[] memory requests) {}

https://github.com/code-423n4/2022-08-olympus/tree/main/src/Kernel.sol

File: src/modules/PRICE.sol

205:  function initialize(uint256[] memory startObservations_, uint48 lastObservationTime_)

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/PRICE.sol

File: src/policies/BondCallback.sol

190:  function setOperator(Operator operator_) external onlyRole("callback_admin") {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/BondCallback.sol

File: src/policies/Heart.sol

69:   function configureDependencies() external override returns (Keycode[] memory dependencies) {

130:  function resetBeat() external onlyRole("heart_admin") {

135:  function toggleBeat() external onlyRole("heart_admin") {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Heart.sol

File: src/policies/Operator.sol

154:  function configureDependencies() external override returns (Keycode[] memory dependencies) {

586:  function setBondContracts(IBondAuctioneer auctioneer_, IBondCallback callback_)

598:  function initialize() external onlyRole("operator_admin") {

624:  function toggleActive() external onlyRole("operator_admin") {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Operator.sol

File: src/policies/PriceConfig.sol

18:   function configureDependencies() external override returns (Keycode[] memory dependencies) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/PriceConfig.sol

File: src/policies/TreasuryCustodian.sol

27:   function configureDependencies() external override returns (Keycode[] memory dependencies) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/TreasuryCustodian.sol

public functions not called by the contract should be declared external instead

There are 18 instances of this issue:

File: src/Kernel.sol

439:  function grantRole(Role role_, address addr_) public onlyAdmin {

451:  function revokeRole(Role role_, address addr_) public onlyAdmin {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/Kernel.sol

File: src/modules/INSTR.sol

28:   function VERSION() public pure override returns (uint8 major, uint8 minor) {

37:   function getInstructions(uint256 instructionsId_) public view returns (Instruction[] memory) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/INSTR.sol

File: src/modules/MINTR.sol

20:   function KEYCODE() public pure override returns (Keycode) {

33:   function mintOhm(address to_, uint256 amount_) public permissioned {

37:   function burnOhm(address from_, uint256 amount_) public permissioned {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/MINTR.sol

File: src/modules/PRICE.sol

108:  function KEYCODE() public pure override returns (Keycode) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/PRICE.sol

File: src/modules/RANGE.sol

110:  function KEYCODE() public pure override returns (Keycode) {

215:  function updateMarket(

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/RANGE.sol

File: src/modules/TRSRY.sol

47:   function KEYCODE() public pure override returns (Keycode) {

75:   function withdrawReserves(

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/TRSRY.sol

File: src/modules/VOTES.sol

22:   function KEYCODE() public pure override returns (Keycode) {

45:   function transfer(address to_, uint256 amount_) public pure override returns (bool) {

51:   function transferFrom(

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/VOTES.sol

File: src/policies/Governance.sol

145:  function getMetadata(uint256 proposalId_) public view returns (ProposalMetadata memory) {

151:  function getActiveProposal() public view returns (ActivatedProposal memory) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Governance.sol

File: src/policies/Heart.sol

18:     /// @dev    The Olympus Heart contract provides keeper rewards to call the heart beat function which fuels

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Heart.sol

Left ToDos

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

There are 1 instances of this issue:

File: 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?

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Operator.sol

Use 1e18 instead of 10**18 or 1000000000000000000

There are 13 instances of this issue:

File: src/modules/PRICE.sol

91:   _scaleFactor = 10**exponent;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/PRICE.sol

File: src/policies/Operator.sol

375:  uint256 oracleScale = 10**uint8(int8(PRICE.decimals()) - priceDecimals);

376:  uint256 bondScale = 10 **

419:  uint256 invCushionPrice = 10**(oracleDecimals * 2) / range.cushion.low.price;

420:  uint256 invWallPrice = 10**(oracleDecimals * 2) / range.wall.low.price;

430:  uint256 oracleScale = 10**uint8(int8(oracleDecimals) - priceDecimals);

431:  uint256 bondScale = 10 **

753:  10**reserveDecimals * RANGE.price(true, false),

754:  10**ohmDecimals * 10**PRICE.decimals()

764:  10**ohmDecimals * 10**PRICE.decimals(),

765:  10**reserveDecimals * RANGE.price(true, true)

784:  10**ohmDecimals * 10**PRICE.decimals(),

785:  10**reserveDecimals * RANGE.price(true, true)

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Operator.sol

Non-library/interface files should use fixed compiler versions, not floating ones

There are 1 instances of this issue:

File: src/interfaces/IBondCallback.sol

2:    pragma solidity >=0.8.0;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/interfaces/IBondCallback.sol

Natspec is missing

There are 2 instances of this issue:

File: src/policies/TreasuryCustodian.sol

      /// @audit

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/TreasuryCustodian.sol

File: src/utils/KernelUtils.sol

      /// @audit

https://github.com/code-423n4/2022-08-olympus/tree/main/src/utils/KernelUtils.sol

Event is missing indexed fields

There are 12 instances of this issue:

File: src/modules/PRICE.sol

event      NewObservation(uint256 timestamp_, uint256 price_, uint256 movingAverage_)

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/PRICE.sol

File: src/modules/RANGE.sol

event      WallUp(bool high_, uint256 timestamp_, uint256 capacity_)

event      WallDown(bool high_, uint256 timestamp_, uint256 capacity_)

event      CushionUp(bool high_, uint256 timestamp_, uint256 capacity_)

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/RANGE.sol

File: src/modules/TRSRY.sol

event      ApprovedForWithdrawal(address indexed policy_, ERC20 indexed token_, uint256 amount_)

event      DebtIncurred(ERC20 indexed token_, address indexed policy_, uint256 amount_)

event      DebtRepaid(ERC20 indexed token_, address indexed policy_, uint256 amount_)

event      DebtSet(ERC20 indexed token_, address indexed policy_, uint256 amount_)

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/TRSRY.sol

File: src/policies/Governance.sol

event      ProposalEndorsed(uint256 proposalId, address voter, uint256 amount)

event      WalletVoted(uint256 proposalId, address voter, bool for_, uint256 userVotes)

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Governance.sol

File: src/policies/Operator.sol

event      CushionParamsChanged(uint32 duration_, uint32 debtBuffer_, uint32 depositInterval_)

event      RegenParamsChanged(uint32 wait_, uint32 threshold_, uint32 observe_)

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Operator.sol

Not used import

There are 2 instances of this issue:

File: src/Kernel.sol

4:    import "src/utils/KernelUtils.sol";

https://github.com/code-423n4/2022-08-olympus/tree/main/src/Kernel.sol

File: src/utils/KernelUtils.sol

4:    import {Keycode, Role} from "../Kernel.sol";

https://github.com/code-423n4/2022-08-olympus/tree/main/src/utils/KernelUtils.sol

2022-08-OLYMPUS

Gas Optimizations Report

The usage of ++i will cost less gas than i++. The same change can be applied to i-- as well.

This change would save up to 6 gas per instance/loop.

There are 7 instances of this issue:

File: src/policies/Operator.sol

488:  decimals++;

670:  _status.low.count++;

675:  _status.low.count--;

686:  _status.high.count++;

691:  _status.high.count--;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Operator.sol

File: src/utils/KernelUtils.sol

49:   i++;

64:   i++;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/utils/KernelUtils.sol

State variables should be cached in stack variables rather than re-reading them.

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

There are 20 instances of this issue:

File: src/modules/RANGE.sol

      /// @audit Cache `_range`. Used 6 times in `updatePrices`
160:  uint256 wallSpread = _range.wall.spread;
161:  uint256 cushionSpread = _range.cushion.spread;
173:  _range.wall.low.price,
174:  _range.cushion.low.price,
175:  _range.cushion.high.price,
176:  _range.wall.high.price

      /// @audit Cache `_range`. Used 4 times in `price`
305:  return _range.wall.high.price;
307:  return _range.wall.low.price;
311:  return _range.cushion.high.price;
313:  return _range.cushion.low.price;

      /// @audit Cache `FACTOR_SCALE`. Used 5 times in `updatePrices`
164:  _range.wall.low.price = (movingAverage_ * (FACTOR_SCALE - wallSpread)) / FACTOR_SCALE;
165:  _range.wall.high.price = (movingAverage_ * (FACTOR_SCALE + wallSpread)) / FACTOR_SCALE;
167:  _range.cushion.low.price = (movingAverage_ * (FACTOR_SCALE - cushionSpread)) / FACTOR_SCALE;
169:  (movingAverage_ * (FACTOR_SCALE + cushionSpread)) /
170:  FACTOR_SCALE;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/RANGE.sol

File: src/policies/BondCallback.sol

      /// @audit Cache `ohm`. Used 3 times in `callback`
118:  if (quoteToken == payoutToken && quoteToken == ohm) {
125:  } else if (quoteToken == ohm) {
131:  } else if (payoutToken == ohm) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/BondCallback.sol

File: src/policies/Operator.sol

      /// @audit Cache `_status`. Used 6 times in `_addObservation`
666:  Regen memory regen = _status.low;
670:  _status.low.count++;
675:  _status.low.count--;
682:  regen = _status.high;
686:  _status.high.count++;
691:  _status.high.count--;

      /// @audit Cache `RANGE`. Used 7 times in `requestPermissions`
172:  Keycode RANGE_KEYCODE = RANGE.KEYCODE();
177:  requests[0] = Permissions(RANGE_KEYCODE, RANGE.updateCapacity.selector);
178:  requests[1] = Permissions(RANGE_KEYCODE, RANGE.updateMarket.selector);
179:  requests[2] = Permissions(RANGE_KEYCODE, RANGE.updatePrices.selector);
180:  requests[3] = Permissions(RANGE_KEYCODE, RANGE.regenerate.selector);
181:  requests[4] = Permissions(RANGE_KEYCODE, RANGE.setSpreads.selector);
182:  requests[5] = Permissions(RANGE_KEYCODE, RANGE.setThresholdFactor.selector);

      /// @audit Cache `RANGE`. Used 3 times in `operate`
210:  uint48(block.timestamp) >= RANGE.lastActive(true) + uint48(config_.regenWait) &&
216:  uint48(block.timestamp) >= RANGE.lastActive(false) + uint48(config_.regenWait) &&
223:  OlympusRange.Range memory range = RANGE.range();

      /// @audit Cache `RANGE`. Used 3 times in `_activate`
364:  OlympusRange.Range memory range = RANGE.range();
415:  RANGE.updateMarket(true, market, marketCapacity);
467:  RANGE.updateMarket(false, market, marketCapacity);

      /// @audit Cache `RANGE`. Used 3 times in `_checkCushion`
735:  bool sideActive = RANGE.active(high_);
736:  uint256 market = RANGE.market(high_);
741:  RANGE.capacity(high_) < auctioneer.currentCapacity(market))

      /// @audit Cache `RANGE`. Used 4 times in `getAmountOut`
753:  10**reserveDecimals * RANGE.price(true, false),
758:  if (amountOut > RANGE.capacity(false)) revert Operator_InsufficientCapacity();
765:  10**reserveDecimals * RANGE.price(true, true)
769:  if (amountOut > RANGE.capacity(true)) revert Operator_InsufficientCapacity();

      /// @audit Cache `MINTR`. Used 3 times in `requestPermissions`
174:  Keycode MINTR_KEYCODE = MINTR.KEYCODE();
184:  requests[7] = Permissions(MINTR_KEYCODE, MINTR.mintOhm.selector);
185:  requests[8] = Permissions(MINTR_KEYCODE, MINTR.burnOhm.selector);

      /// @audit Cache `auctioneer`. Used 4 times in `_activate`
409:  uint256 market = auctioneer.createMarket(params);
412:  callback.whitelist(address(auctioneer.getTeller()), market);
461:  uint256 market = auctioneer.createMarket(params);
464:  callback.whitelist(address(auctioneer.getTeller()), market);

      /// @audit Cache `callback`. Used 4 times in `_activate`
397:  callbackAddr: address(callback),
412:  callback.whitelist(address(auctioneer.getTeller()), market);
449:  callbackAddr: address(callback),
464:  callback.whitelist(address(auctioneer.getTeller()), market);

      /// @audit Cache `ohm`. Used 4 times in `swap`
277:  if (tokenIn_ == ohm) {
299:  ohm.safeTransferFrom(msg.sender, address(this), amountIn_);
307:  emit Swap(ohm, reserve, amountIn_, amountOut);
335:  emit Swap(reserve, ohm, amountIn_, amountOut);

      /// @audit Cache `ohmDecimals`. Used 4 times in `_activate`
372:  int8 scaleAdjustment = int8(ohmDecimals) - int8(reserveDecimals) + (priceDecimals / 2);
378:  36 + scaleAdjustment + int8(reserveDecimals) - int8(ohmDecimals) - priceDecimals
427:  int8 scaleAdjustment = int8(reserveDecimals) - int8(ohmDecimals) + (priceDecimals / 2);
433:  36 + scaleAdjustment + int8(ohmDecimals) - int8(reserveDecimals) - priceDecimals

      /// @audit Cache `reserve`. Used 5 times in `swap`
305:  TRSRY.withdrawReserves(msg.sender, reserve, amountOut);
307:  emit Swap(ohm, reserve, amountIn_, amountOut);
308:  } else if (tokenIn_ == reserve) {
330:  reserve.safeTransferFrom(msg.sender, address(TRSRY), amountIn_);
335:  emit Swap(reserve, ohm, amountIn_, amountOut);

      /// @audit Cache `reserveDecimals`. Used 4 times in `_activate`
372:  int8 scaleAdjustment = int8(ohmDecimals) - int8(reserveDecimals) + (priceDecimals / 2);
378:  36 + scaleAdjustment + int8(reserveDecimals) - int8(ohmDecimals) - priceDecimals
427:  int8 scaleAdjustment = int8(reserveDecimals) - int8(ohmDecimals) + (priceDecimals / 2);
433:  36 + scaleAdjustment + int8(ohmDecimals) - int8(reserveDecimals) - priceDecimals

      /// @audit Cache `FACTOR_SCALE`. Used 3 times in `fullCapacity`
780:  uint256 capacity = (reservesInTreasury * _config.reserveFactor) / FACTOR_SCALE;
786:  ) * (FACTOR_SCALE + RANGE.spread(true) * 2)) /
787:  FACTOR_SCALE;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Operator.sol

File: src/policies/PriceConfig.sol

      /// @audit Cache `PRICE`. Used 3 times in `requestPermissions`
32:   permissions[0] = Permissions(PRICE.KEYCODE(), PRICE.initialize.selector);
33:   permissions[1] = Permissions(PRICE.KEYCODE(), PRICE.changeMovingAverageDuration.selector);
34:   permissions[2] = Permissions(PRICE.KEYCODE(), PRICE.changeObservationFrequency.selector);

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/PriceConfig.sol

File: src/policies/TreasuryCustodian.sol

      /// @audit Cache `TRSRY`. Used 3 times in `requestPermissions`
35:   Keycode TRSRY_KEYCODE = TRSRY.KEYCODE();
38:   requests[0] = Permissions(TRSRY_KEYCODE, TRSRY.setApprovalFor.selector);
39:   requests[1] = Permissions(TRSRY_KEYCODE, TRSRY.setDebt.selector);

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/TreasuryCustodian.sol

It costs more gas to initialize non-constant/non-immutable variables to zero than to let the default of zero be applied

Not overwriting the default for stack variables saves 8 gas. Storage and memory variables have larger savings

There are 3 instances of this issue:

File: src/Kernel.sol

397:  for (uint256 i = 0; i < reqLength; ) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/Kernel.sol

File: src/utils/KernelUtils.sol

43:   for (uint256 i = 0; i < 5; ) {

58:   for (uint256 i = 0; i < 32; ) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/utils/KernelUtils.sol

Using private rather than public for constants, saves gas

If needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table

There are 9 instances of this issue:

File: src/modules/PRICE.sol

59:   uint8 public constant decimals = 18;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/PRICE.sol

File: src/modules/RANGE.sol

65:   uint256 public constant FACTOR_SCALE = 1e4;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/RANGE.sol

File: src/policies/Governance.sol

121:  uint256 public constant SUBMISSION_REQUIREMENT = 100;

124:  uint256 public constant ACTIVATION_DEADLINE = 2 weeks;

127:  uint256 public constant GRACE_PERIOD = 1 weeks;

130:  uint256 public constant ENDORSEMENT_THRESHOLD = 20;

133:  uint256 public constant EXECUTION_THRESHOLD = 33;

137:  uint256 public constant EXECUTION_TIMELOCK = 3 days;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Governance.sol

File: src/policies/Operator.sol

89:   uint32 public constant FACTOR_SCALE = 1e4;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Operator.sol

Functions that are access-restricted from most users may be marked as payable

Marking a function as payable reduces gas cost since the compiler does not have to check whether a payment was provided or not. This change will save around 21 gas per function call.

There are 2 instances of this issue:

File: src/Kernel.sol

439:  function grantRole(Role role_, address addr_) public onlyAdmin {

451:  function revokeRole(Role role_, address addr_) public onlyAdmin {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/Kernel.sol

++i/i++ should be unchecked{++I}/unchecked{I++} in for-loops

When an increment or any arithmetic operation is not possible to overflow it should be placed in unchecked{} block. \This is because of the default compiler overflow and underflow safety checks since Solidity version 0.8.0. \In for-loops it saves around 30-40 gas per loop

There are 2 instances of this issue:

File: src/policies/Governance.sol

281:  ++step;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Governance.sol

File: src/policies/TreasuryCustodian.sol

62:   ++j;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/TreasuryCustodian.sol

<x> += <y> costs more gas than <x> = <x> + <y> for state variables

There are 11 instances of this issue:

File: src/modules/PRICE.sol

136:  _movingAverage += (currentPrice - earliestPrice) / numObs;

138:  _movingAverage -= (earliestPrice - currentPrice) / numObs;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/PRICE.sol

File: src/modules/TRSRY.sol

97:   totalDebt[token_] += amount_;

116:  totalDebt[token_] -= received;

131:  if (oldDebt < amount_) totalDebt[token_] += amount_ - oldDebt;

132:  else totalDebt[token_] -= oldDebt - amount_;

96:   reserveDebt[token_][msg.sender] += amount_;

115:  reserveDebt[token_][msg.sender] -= received;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/TRSRY.sol

File: src/policies/BondCallback.sol

143:  _amountsPerMarket[id_][0] += inputAmount_;

144:  _amountsPerMarket[id_][1] += outputAmount_;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/BondCallback.sol

File: src/policies/Heart.sol

103:  lastBeat += frequency();

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Heart.sol

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.' \ https://docs.soliditylang.org/en/v0.8.15/internals/layout_in_storage.html \ Use a larger size then downcast where needed

There are 54 instances of this issue:

File: src/Kernel.sol

100:  function VERSION() external pure virtual returns (uint8 major, uint8 minor) {}

https://github.com/code-423n4/2022-08-olympus/tree/main/src/Kernel.sol

File: src/modules/INSTR.sol

28:   function VERSION() public pure override returns (uint8 major, uint8 minor) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/INSTR.sol

File: src/modules/MINTR.sol

25:   function VERSION() external pure override returns (uint8 major, uint8 minor) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/MINTR.sol

File: src/modules/PRICE.sol

44:   uint32 public nextObsIndex;

47:   uint32 public numObservations;

59:   uint8 public constant decimals = 18;

84:   uint8 ohmEthDecimals = _ohmEthPriceFeed.decimals();

87:   uint8 reserveEthDecimals = _reserveEthPriceFeed.decimals();

113:  function VERSION() external pure override returns (uint8 major, uint8 minor) {

127:  uint32 numObs = numObservations;

185:  uint32 lastIndex = nextObsIndex == 0 ? numObservations - 1 : nextObsIndex - 1;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/PRICE.sol

File: src/modules/RANGE.sol

115:  function VERSION() external pure override returns (uint8 major, uint8 minor) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/RANGE.sol

File: src/modules/TRSRY.sol

51:   function VERSION() external pure override returns (uint8 major, uint8 minor) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/TRSRY.sol

File: src/modules/VOTES.sol

27:   function VERSION() external pure override returns (uint8 major, uint8 minor) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/VOTES.sol

File: src/policies/interfaces/IOperator.sol

13:   uint32 cushionFactor;

14:   uint32 cushionDuration;

15:   uint32 cushionDebtBuffer;

16:   uint32 cushionDepositInterval;

17:   uint32 reserveFactor;

18:   uint32 regenWait;

19:   uint32 regenThreshold;

20:   uint32 regenObserve;

31:   uint32 count;

33:   uint32 nextObservation;

85:   function setCushionFactor(uint32 cushionFactor_) external;

93:   uint32 duration_,

94:   uint32 debtBuffer_,

95:   uint32 depositInterval_

101:  function setReserveFactor(uint32 reserveFactor_) external;

110:  uint32 wait_,

111:  uint32 threshold_,

112:  uint32 observe_

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/interfaces/IOperator.sol

File: src/policies/Operator.sol

51:   event CushionFactorChanged(uint32 cushionFactor_);

52:   event CushionParamsChanged(uint32 duration_, uint32 debtBuffer_, uint32 depositInterval_);

53:   event ReserveFactorChanged(uint32 reserveFactor_);

54:   event RegenParamsChanged(uint32 wait_, uint32 threshold_, uint32 observe_);

83:   uint8 public immutable ohmDecimals;

86:   uint8 public immutable reserveDecimals;

89:   uint32 public constant FACTOR_SCALE = 1e4;

371:  int8 priceDecimals = _getPriceDecimals(range.cushion.high.price);

372:  int8 scaleAdjustment = int8(ohmDecimals) - int8(reserveDecimals) + (priceDecimals / 2);

418:  uint8 oracleDecimals = PRICE.decimals();

426:  int8 priceDecimals = _getPriceDecimals(invCushionPrice);

427:  int8 scaleAdjustment = int8(reserveDecimals) - int8(ohmDecimals) + (priceDecimals / 2);

485:  int8 decimals;

516:  function setCushionFactor(uint32 cushionFactor_) external onlyRole("operator_policy") {

528:  uint32 duration_,

529:  uint32 debtBuffer_,

530:  uint32 depositInterval_

548:  function setReserveFactor(uint32 reserveFactor_) external onlyRole("operator_policy") {

560:  uint32 wait_,

561:  uint32 threshold_,

562:  uint32 observe_

665:  uint32 observe = _config.regenObserve;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Operator.sol

Don't compare boolean expressions to boolean literals

Use if(x)/if(!x) instead of if(x == true)/if(x == false).

There are 2 instances of this issue:

File: src/policies/Governance.sol

223:  if (proposalHasBeenActivated[proposalId_] == true) {

306:  if (tokenClaimsForProposal[proposalId_][msg.sender] == true) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Governance.sol

Use calldata instead of memory for function parameters

If a reference type function parameter is read-only, it is cheaper in gas to use calldata instead of memory. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory. Try to use calldata as a data location because it will avoid copies and also makes sure that the data cannot be modified.

There are 6 instances of this issue:

File: src/Kernel.sol

393:  Permissions[] memory requests_,

https://github.com/code-423n4/2022-08-olympus/tree/main/src/Kernel.sol

File: src/modules/PRICE.sol

205:  function initialize(uint256[] memory startObservations_, uint48 lastObservationTime_)

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/PRICE.sol

File: src/policies/BondCallback.sol

152:  function batchToTreasury(ERC20[] memory tokens_) external onlyRole("callback_admin") {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/BondCallback.sol

File: src/policies/Governance.sol

162:  string memory proposalURI_

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Governance.sol

File: src/policies/PriceConfig.sol

45:   function initialize(uint256[] memory startObservations_, uint48 lastObservationTime_)

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/PriceConfig.sol

File: src/policies/TreasuryCustodian.sol

53:   function revokePolicyApprovals(address policy_, ERC20[] memory tokens_) external {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/TreasuryCustodian.sol

Replace x <= y with x < y + 1, and x >= y with x > y - 1

In the EVM, there is no opcode for >= or <=. When using greater than or equal, two operations are performed: > and =. Using strict comparison operators hence saves gas

There are 7 instances of this issue:

File: src/policies/Operator.sol

210:  uint48(block.timestamp) >= RANGE.lastActive(true) + uint48(config_.regenWait) &&

211:  _status.high.count >= config_.regenThreshold

216:  uint48(block.timestamp) >= RANGE.lastActive(false) + uint48(config_.regenWait) &&

217:  _status.low.count >= config_.regenThreshold

486:  while (price_ >= 10) {

667:  if (currentPrice >= movingAverage) {

683:  if (currentPrice <= movingAverage) {

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Operator.sol

Use immutable & constant for state variables that do not change their value

There are 9 instances of this issue:

File: src/modules/RANGE.sol

59:   Range internal _range;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/RANGE.sol

File: src/policies/BondCallback.sol

28:   IBondAggregator public aggregator;

32:   ERC20 public ohm;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/BondCallback.sol

File: src/policies/Heart.sol

33:   bool public active;

48:   IOperator internal _operator;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Heart.sol

File: src/policies/Operator.sol

59:   Status internal _status;

60:   Config internal _config;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Operator.sol

File: src/policies/PriceConfig.sol

11:   OlympusPrice internal PRICE;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/PriceConfig.sol

File: src/policies/TreasuryCustodian.sol

20:   OlympusTreasury internal TRSRY;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/TreasuryCustodian.sol

Using bools for storage variables incurs overhead

Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from 'false' to 'true', after having been 'true' in the past

There are 4 instances of this issue:

File: src/modules/PRICE.sol

62:   bool public initialized;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/modules/PRICE.sol

File: src/policies/Heart.sol

33:   bool public active;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Heart.sol

File: src/policies/Operator.sol

63:   bool public initialized;

66:   bool public active;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/Operator.sol

Use a more recent version of solidity

Use a solidity version of at least 0.8.2 to get simple 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

There are 3 instances of this issue:

File: src/interfaces/IBondCallback.sol

2:    pragma solidity >=0.8.0;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/interfaces/IBondCallback.sol

File: src/policies/interfaces/IHeart.sol

2:    pragma solidity >=0.8.0;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/interfaces/IHeart.sol

File: src/policies/interfaces/IOperator.sol

2:    pragma solidity >=0.8.0;

https://github.com/code-423n4/2022-08-olympus/tree/main/src/policies/interfaces/IOperator.sol

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