Olympus DAO contest - IllIllI'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: 8/147

Findings: 5

Award: $2,358.52

🌟 Selected for report: 1

šŸš€ Solo Findings: 1

Findings Information

🌟 Selected for report: IllIllI

Labels

bug
2 (Med Risk)
disagree with severity
old-submission-method

Awards

1905.4132 DAI - $1,905.41

External Links

Lines of code

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L134-L144

Vulnerability details

Impact

While users are incentivized to call the heartbeat, the incentive may be removed later, or it may be more profitable to use old prices, so users may not call the heartbeat during unfavorable prices, leading to the TWAP price being incorrect, and users getting the wrong price for their assets.

A similar case of an incomplete TWAP algorithm was found to be of Medium risk

Proof of Concept

A TWAP is a Time-Weighted average price, but the algorithm below does not take into account the time between observations:

File: /src/modules/PRICE.sol   #1

134          // Calculate new moving average
135          if (currentPrice > earliestPrice) {
136              _movingAverage += (currentPrice - earliestPrice) / numObs;
137          } else {
138              _movingAverage -= (earliestPrice - currentPrice) / numObs;
139          }
140  
141          // Push new observation into storage and store timestamp taken at
142          observations[nextObsIndex] = currentPrice;
143          lastObservationTime = uint48(block.timestamp);
144:         nextObsIndex = (nextObsIndex + 1) % numObs;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L134-L144

While the Heart policy enforces an upper bound on how frequently updates are added to the average, there is no guarantee that users call beat() in a timely manner:

File: /src/policies/Heart.sol   #2

92       function beat() external nonReentrant {
93           if (!active) revert Heart_BeatStopped();
94:          if (block.timestamp < lastBeat + frequency()) revert Heart_OutOfCycle();

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L92-L94

The incentive may be set to too low an amount:

File: /src/policies/Heart.sol   #3

140      function setRewardTokenAndAmount(ERC20 token_, uint256 reward_)
141          external
142          onlyRole("heart_admin")
143      {
144          rewardToken = token_;
145          reward = reward_;
146          emit RewardUpdated(token_, reward_);
147:     }

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L140-L147

Or users may find it more profitable to skip a particular update, or front-run an unfavorable update, with a transaction that trades assets at the old price

Tools Used

Code inspection

Always call an internal version of beat() that doesn't revert, in functions that swap user assets. The code should also track the timestamps of when each beat() is called, and include the amount of time that has passed since the last beat, in the TWAP calculation

#0 - Oighty

2022-09-08T17:52:27Z

The referenced issue is a bit different than our use case since we will be using a much longer duration moving average. The goal is to get an approximate moving average over a certain period of time (e.g. 120 days) vs. an exact number since, as you say, the time of each observation cannot be guaranteed to be at a specific time. We believe that using a long duration with a sufficient number of observations will make this value close enough to the true value it is approximating, and prevents actors from manipulating the value by waiting to provide a specific value (1 out of ~360 obs doesn't move the needle). The use of the "TWAP" term may be semantically inaccurate.

As for not guaranteeing that the update will be called or issues with several observations close to each other, see comments on #405 and #79.

The mitigations suggested do not seem to provide a solution that improves the system. Calling beat() on user actions would not have the observations roughly evenly spaced. Tracking timestamps is possible, but I don't see how it improves the data.

#1 - 0xean

2022-09-19T13:47:12Z

@Oighty - I think the warden is suggesting that the call to beat() in the user actions would do more to ensure that the "TWAP" stays up to date. If the call isn't past the correct period, it would just return and make no change (costing some amount of gas, ofc).

I do think it may be worth considering, that way no user action can take place without the TWAP being as up to date as possible and no additional calls to the contract may be necessary if users are interacting with the contract frequently enough.

While this is related to #79 - I think the points raised here and the mitigation is sufficiently different to warrant this issue to stand alone.

#2 - Oighty

2022-09-22T16:00:39Z

That's a fair point. One issue with calling beat on user actions, e.g. Operator.swap, is that it would update the wall price that the user is swapping at. Therefore, the call could fail due to the slippage check. This could be confusing behavior and may have unintended consequences of DOS'ing the system. Additionally, the gas cost of beat is highly variable (sometimes up to 600k gas when opening a bond market) and would cause some users to unexpectedly pay a lot more gas for a swap.

I'll discuss with the team, but I don't think the pros exceed the cons.

Findings Information

🌟 Selected for report: GalloDaSballo

Also found by: 0xNazgul, IllIllI, rbserver

Labels

bug
duplicate
2 (Med Risk)
sponsor disputed
old-submission-method

Awards

347.2615 DAI - $347.26

External Links

Lines of code

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/VoterRegistration.sol#L45-L56 https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/PriceConfig.sol#L45-L74 https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/TreasuryCustodian.sol#L71-L87 https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/TreasuryCustodian.sol#L42-L48 https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L140-L147 https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L149-L152 https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L586-L589

Vulnerability details

Impact

The introduction of roles drastically expands the attack surface of accounts that may be compromised in order to attack the protocol.

Even if the admin or role members are benevolent the fact that there is a rug vector available may negatively impact the protocol's reputation.

Proof of Concept

There are many different ways to rug users, depending on which role is assigned.

voter_admins can give themselves votes, or take away votes from other users, making them able to win any vote:

File: /src/policies/VoterRegistration.sol   #1

45       function issueVotesTo(address wallet_, uint256 amount_) external onlyRole("voter_admin") {
46           // Issue the votes in the VOTES module
47           VOTES.mintTo(wallet_, amount_);
48       }
49   
...
53       function revokeVotesFrom(address wallet_, uint256 amount_) external onlyRole("voter_admin") {
54           // Revoke the votes in the VOTES module
55           VOTES.burnFrom(wallet_, amount_);
56:      }

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/VoterRegistration.sol#L45-L56

price_admins can repeatedly re-initialize the moving averages, which will cause the calls to fetch the price to always revert, not allowing users to redeem their assets:

File: /src/policies/PriceConfig.sol   #2

45       function initialize(uint256[] memory startObservations_, uint48 lastObservationTime_)
46           external
47           onlyRole("price_admin")
48       {
49           PRICE.initialize(startObservations_, lastObservationTime_);
50       }
51   
...
58       function changeMovingAverageDuration(uint48 movingAverageDuration_)
59           external
60           onlyRole("price_admin")
61       {
62           PRICE.changeMovingAverageDuration(movingAverageDuration_);
63       }
64   
...
69       function changeObservationFrequency(uint48 observationFrequency_)
70           external
71           onlyRole("price_admin")
72       {
73           PRICE.changeObservationFrequency(observationFrequency_);
74:      }

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/PriceConfig.sol#L45-L74

custodians can change the debt arbitrarily, changing the price for which users can redeem their assets:

File: /src/policies/TreasuryCustodian.sol   #3

71       function increaseDebt(
72           ERC20 token_,
73           address debtor_,
74           uint256 amount_
75       ) external onlyRole("custodian") {
76           uint256 debt = TRSRY.reserveDebt(token_, debtor_);
77           TRSRY.setDebt(token_, debtor_, debt + amount_);
78       }
79   
80       function decreaseDebt(
81           ERC20 token_,
82           address debtor_,
83           uint256 amount_
84       ) external onlyRole("custodian") {
85           uint256 debt = TRSRY.reserveDebt(token_, debtor_);
86           TRSRY.setDebt(token_, debtor_, debt - amount_);
87:      }

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/TreasuryCustodian.sol#L71-L87

heart_admins can change the reward to zero, causing there to be no incentive to call the heartbeat, so the TWAP becomes stale and reverts, or can set a reward token that always reverts, causing heartbeats to always revert:

File: /src/policies/Heart.sol   #4

140      function setRewardTokenAndAmount(ERC20 token_, uint256 reward_)
141          external
142          onlyRole("heart_admin")
143      {
144          rewardToken = token_;
145          reward = reward_;
146          emit RewardUpdated(token_, reward_);
147:     }

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L140-L147

operator_admin can influence prices by setting a malicious contract as the auctioneer, or can assign a non-zero invalid address, so function calls always revert:

File: /src/policies/Operator.sol   #5

586      function setBondContracts(IBondAuctioneer auctioneer_, IBondCallback callback_)
587          external
588          onlyRole("operator_admin")
589      {
590          if (address(auctioneer_) == address(0) || address(callback_) == address(0))
591              revert Operator_InvalidParams();
592          /// Set contracts
593          auctioneer = auctioneer_;
594          callback = callback_;
595:     }

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L586-L595

Tools Used

Code inspection

Only have one admin user which can only be assigned to a DAO, and only give the DAO permissions to do things via proposals and votes

#0 - 0xLienid

2022-09-08T17:35:57Z

true but comes with the territory of being a permissioned system. Roles will be granted to DAO associated multisigs or policies.

#1 - 0xean

2022-09-20T23:43:16Z

Some of these are intentionally designed by the system, the one that may or may not be is the ability to manipulate total supply of votes in a way that then completely controls the passing of proposals. I am going to mark this as a duplicate of #380 for that reason. The rest is QA.

Findings Information

Awards

11.0311 DAI - $11.03

Labels

bug
duplicate
2 (Med Risk)
sponsor confirmed
old-submission-method

External Links

Lines of code

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

Vulnerability details

Impact

If the oracle price feeds are insufficiently validated, there will be pricing errors leading to the miss-pricing of assets/risk

Proof of Concept

If either price becomes negative, either because of a Chainlink bug, or due to the actual price being negative (as has been seen recently for the price of oil, and with European interest rates), the unchecked cast to uint256 will result in the value being used overflowing, causing a very large value to be used:

File: /src/modules/PRICE.sol   #1

161              (, int256 ohmEthPriceInt, , uint256 updatedAt, ) = _ohmEthPriceFeed.latestRoundData();
162              // Use a multiple of observation frequency to determine what is too old to use.
163              // Price feeds will not provide an updated answer if the data doesn't change much.
164              // This would be similar to if the feed just stopped updating; therefore, we need a cutoff.
165              if (updatedAt < block.timestamp - 3 * uint256(observationFrequency))
166                  revert Price_BadFeed(address(_ohmEthPriceFeed));
167              ohmEthPrice = uint256(ohmEthPriceInt);
168  
169              int256 reserveEthPriceInt;
170              (, reserveEthPriceInt, , updatedAt, ) = _reserveEthPriceFeed.latestRoundData();
171              if (updatedAt < block.timestamp - uint256(observationFrequency))
172                  revert Price_BadFeed(address(_reserveEthPriceFeed));
173:             reserveEthPrice = uint256(reserveEthPriceInt);

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

Tools Used

Code inspection

require() that the price is non-negative, or use int256 as the price type, rather than uint256

#0 - Oighty

2022-09-06T18:54:46Z

Duplicate. See comment on #441.

Summary

Low Risk Issues

IssueInstances
[L‑01]Function should revert if no valid action is found1
[L‑02]safeApprove() is deprecated2
[L‑03]Missing checks for address(0x0) when assigning values to address state variables2
[L‑04]Open TODOs4

Total: 9 instances over 4 issues

Non-critical Issues

IssueInstances
[N‑01]Contracts will stop working when block.timestamp overflows uint482
[N‑02]The nonReentrant modifier should occur before all other modifiers1
[N‑03]override function arguments that are unused should have the variable name removed or commented out to avoid compiler warnings2
[N‑04]public functions not called by the contract should be declared external instead9
[N‑05]Non-assembly method available1
[N‑06]constants should be defined rather than using magic numbers65
[N‑07]Missing event and or timelock for critical parameter change3
[N‑08]Constant redefined elsewhere4
[N‑09]Lines are too long8
[N‑10]Variable names that consist of all capital letters should be reserved for constant/immutable variables18
[N‑11]Typos4
[N‑12]File is missing NatSpec2
[N‑13]NatSpec is incomplete7
[N‑14]Event is missing indexed fields30
[N‑15]Not using the named return variables anywhere in the function is confusing14
[N‑16]Avoid the use of sensitive terms1

Total: 171 instances over 16 issues

Low Risk Issues

[L‑01] Function should revert if no valid action is found

There is no final else, so ActionExecuted is incorrectly emitted if the action is unknown (incorrect state handling)

There is 1 instance of this issue:

File: /src/Kernel.sol

254          } else if (action_ == Actions.MigrateKernel) {
255              ensureContract(target_);
256              _migrateKernel(Kernel(target_));
257          }
258  
259:         emit ActionExecuted(action_, target_);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/Kernel.sol#L254-L259

[L‑02] safeApprove() is deprecated

Deprecated in favor of safeIncreaseAllowance() and safeDecreaseAllowance(). If only setting the initial allowance to the value that means infinite, safeIncreaseAllowance() can be used instead. The function may currently work, but if a bug is found in this version of OpenZeppelin, and the version that you're forced to upgrade to no longer has this function, you'll encounter unnecessary delays in porting and testing replacement contracts.

There are 2 instances of this issue:

File: src/policies/BondCallback.sol

57:           ohm.safeApprove(address(MINTR), type(uint256).max);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/BondCallback.sol#L57

File: src/policies/Operator.sol

167:          ohm.safeApprove(address(MINTR), type(uint256).max);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L167

[L‑03] Missing checks for address(0x0) when assigning values to address state variables

There are 2 instances of this issue:

File: src/Kernel.sol

251:              executor = target_;

253:              admin = target_;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/Kernel.sol#L251

[L‑04] Open TODOs

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

There are 4 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/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L657

File: src/policies/TreasuryCustodian.sol

51:       // TODO Currently allows anyone to revoke any approval EXCEPT activated policies.

52:       // TODO must reorg policy storage to be able to check for deactivated policies.

56:           // TODO Make sure `policy_` is an actual policy and not a random address.

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/TreasuryCustodian.sol#L51

Non-critical Issues

[N‑01] Contracts will stop working when block.timestamp overflows uint48

This will be in ~9 million years. The DAO is supposed to live forever, right? RIGHT?!

There are 2 instances of this issue:

File: /src/modules/PRICE.sol

143:         lastObservationTime = uint48(block.timestamp);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L143

File: /src/policies/Operator.sol

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

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L210

[N‑02] The nonReentrant modifier should occur before all other modifiers

This is a best-practice to protect against reentrancy in other modifiers

There is 1 instance of this issue:

File: src/policies/Operator.sol

276:      ) external override onlyWhileActive nonReentrant returns (uint256 amountOut) {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L276

[N‑03] override function arguments that are unused should have the variable name removed or commented out to avoid compiler warnings

There are 2 instances of this issue:

File: src/modules/VOTES.sol

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

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/VOTES.sol#L45

[N‑04] 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.

There are 9 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/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/Kernel.sol#L439

File: src/modules/INSTR.sol

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

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/INSTR.sol#L37

File: src/modules/MINTR.sol

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/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/MINTR.sol#L33

File: src/modules/RANGE.sol

215       function updateMarket(
216           bool high_,
217           uint256 market_,
218           uint256 marketCapacity_
219:      ) public permissioned {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/RANGE.sol#L215-L219

File: src/modules/TRSRY.sol

75        function withdrawReserves(
76            address to_,
77            ERC20 token_,
78:           uint256 amount_

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/TRSRY.sol#L75-L78

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/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Governance.sol#L145

[N‑05] Non-assembly method available

assembly{ id := chainid() } => uint256 id = block.chainid, assembly { size := extcodesize() } => uint256 size = address().code.length There are some automated tools that will flag a project as having higher complexity if there is inline-assembly, so it's best to avoid using it where it's not necessary

There is 1 instance of this issue:

File: src/utils/KernelUtils.sol

34:           size := extcodesize(target_)

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/utils/KernelUtils.sol#L34

[N‑06] constants should be defined rather than using magic numbers

Even assembly can benefit from using readable constants instead of hex/numeric literals

There are 65 instances of this issue:

File: src/modules/PRICE.sol

/// @audit 38
90:           if (exponent > 38) revert Price_InvalidParams();

/// @audit 3
165:              if (updatedAt < block.timestamp - 3 * uint256(observationFrequency))

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L90

File: src/modules/RANGE.sol

/// @audit 3
80:           uint256[3] memory rangeParams_ // [thresholdFactor, cushionSpread, wallSpread]

/// @audit 10000
245:              wallSpread_ > 10000 ||

/// @audit 100
246:              wallSpread_ < 100 ||

/// @audit 10000
247:              cushionSpread_ > 10000 ||

/// @audit 100
248:              cushionSpread_ < 100 ||

/// @audit 10000
/// @audit 100
264:          if (thresholdFactor_ > 10000 || thresholdFactor_ < 100) revert RANGE_InvalidParams();

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/RANGE.sol#L80

File: src/policies/BondCallback.sol

/// @audit 4
71:           requests = new Permissions[](4);

/// @audit 3
75:           requests[3] = Permissions(MINTR_KEYCODE, MINTR.burnOhm.selector);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/BondCallback.sol#L71

File: src/policies/Governance.sol

/// @audit 10000
164:          if (VOTES.balanceOf(msg.sender) * 10000 < VOTES.totalSupply() * SUBMISSION_REQUIREMENT)

/// @audit 100
217:              (totalEndorsementsForProposal[proposalId_] * 100) <

/// @audit 100
268:          if (netVotes * 100 < VOTES.totalSupply() * EXECUTION_THRESHOLD) {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Governance.sol#L164

File: src/policies/Operator.sol

/// @audit 8
97:           uint32[8] memory configParams // [cushionFactor, cushionDuration, cushionDebtBuffer, cushionDepositInterval, reserveFactor, regenWait, regenThreshold, regenObserve]

/// @audit 7
103:          if (configParams[1] > uint256(7 days) || configParams[1] < uint256(1 days))

/// @audit 10_000
106:          if (configParams[2] < uint32(10_000)) revert Operator_InvalidParams();

/// @audit 3
/// @audit 3
108:          if (configParams[3] < uint32(1 hours) || configParams[3] > configParams[1])

/// @audit 4
/// @audit 10000
/// @audit 4
/// @audit 100
111:          if (configParams[4] > 10000 || configParams[4] < 100) revert Operator_InvalidParams();

/// @audit 5
114:              configParams[5] < 1 hours ||

/// @audit 6
/// @audit 7
115:              configParams[6] > configParams[7] ||

/// @audit 7
116:              configParams[7] == uint32(0)

/// @audit 7
130:              observations: new bool[](configParams[7])

/// @audit 3
137:              cushionDepositInterval: configParams[3],

/// @audit 4
138:              reserveFactor: configParams[4],

/// @audit 5
139:              regenWait: configParams[5],

/// @audit 6
140:              regenThreshold: configParams[6],

/// @audit 7
141:              regenObserve: configParams[7]

/// @audit 3
147:          emit CushionParamsChanged(configParams[1], configParams[2], configParams[3]);

/// @audit 4
148:          emit ReserveFactorChanged(configParams[4]);

/// @audit 5
/// @audit 6
/// @audit 7
149:          emit RegenParamsChanged(configParams[5], configParams[6], configParams[7]);

/// @audit 4
155:          dependencies = new Keycode[](4);

/// @audit 3
159:          dependencies[3] = toKeycode("MINTR");

/// @audit 3
164:          MINTR = OlympusMinter(getModuleAddress(dependencies[3]));

/// @audit 9
176:          requests = new Permissions[](9);

/// @audit 3
180:          requests[3] = Permissions(RANGE_KEYCODE, RANGE.regenerate.selector);

/// @audit 4
181:          requests[4] = Permissions(RANGE_KEYCODE, RANGE.setSpreads.selector);

/// @audit 5
182:          requests[5] = Permissions(RANGE_KEYCODE, RANGE.setThresholdFactor.selector);

/// @audit 6
183:          requests[6] = Permissions(TRSRY_KEYCODE, TRSRY.setApprovalFor.selector);

/// @audit 7
184:          requests[7] = Permissions(MINTR_KEYCODE, MINTR.mintOhm.selector);

/// @audit 8
185:          requests[8] = Permissions(MINTR_KEYCODE, MINTR.burnOhm.selector);

/// @audit 36
378:                      36 + scaleAdjustment + int8(reserveDecimals) - int8(ohmDecimals) - priceDecimals

/// @audit 36
433:                      36 + scaleAdjustment + int8(ohmDecimals) - int8(reserveDecimals) - priceDecimals

/// @audit 10000
/// @audit 100
518:          if (cushionFactor_ > 10000 || cushionFactor_ < 100) revert Operator_InvalidParams();

/// @audit 7
533:          if (duration_ > uint256(7 days) || duration_ < uint256(1 days))

/// @audit 10_000
535:          if (debtBuffer_ < uint32(10_000)) revert Operator_InvalidParams();

/// @audit 10000
/// @audit 100
550:          if (reserveFactor_ > 10000 || reserveFactor_ < 100) revert Operator_InvalidParams();

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L97

File: src/policies/PriceConfig.sol

/// @audit 3
31:           permissions = new Permissions[](3);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/PriceConfig.sol#L31

File: src/utils/KernelUtils.sol

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

/// @audit 0x41
/// @audit 0x5A
46:           if (char < 0x41 || char > 0x5A) revert InvalidKeycode(keycode_); // A-Z only

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

/// @audit 0x61
/// @audit 0x7A
/// @audit 0x5f
/// @audit 0x00
60:           if ((char < 0x61 || char > 0x7A) && char != 0x5f && char != 0x00) {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/utils/KernelUtils.sol#L43

[N‑07] Missing event and or timelock for critical parameter change

Events help non-contract tools to track changes, and events prevent users from being surprised by changes

There are 3 instances of this issue:

File: src/Kernel.sol

126       function setActiveStatus(bool activate_) external onlyKernel {
127           isActive = activate_;
128:      }

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/Kernel.sol#L126-L128

File: src/policies/BondCallback.sol

190       function setOperator(Operator operator_) external onlyRole("callback_admin") {
191           if (address(operator_) == address(0)) revert Callback_InvalidParams();
192           operator = operator_;
193:      }

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/BondCallback.sol#L190-L193

File: src/policies/Operator.sol

586       function setBondContracts(IBondAuctioneer auctioneer_, IBondCallback callback_)
587           external
588           onlyRole("operator_admin")
589       {
590           if (address(auctioneer_) == address(0) || address(callback_) == address(0))
591               revert Operator_InvalidParams();
592           /// Set contracts
593           auctioneer = auctioneer_;
594           callback = callback_;
595:      }

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L586-L595

[N‑08] Constant redefined elsewhere

Consider defining in only one contract so that values cannot become out of sync when only one location is updated. A cheap way to store constants in a single location is to create an internal constant in a library. If the variable is a local cache of another contract's value, consider making the cache variable internal or private, which will require external users to query the contract with the source of truth, so that callers don't get out of sync.

There are 4 instances of this issue:

File: src/modules/RANGE.sol

/// @audit seen in src/modules/MINTR.sol 
68:       ERC20 public immutable ohm;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/RANGE.sol#L68

File: src/policies/Operator.sol

/// @audit seen in src/modules/RANGE.sol 
82:       ERC20 public immutable ohm;

/// @audit seen in src/modules/RANGE.sol 
85:       ERC20 public immutable reserve;

/// @audit seen in src/modules/RANGE.sol 
89:       uint32 public constant FACTOR_SCALE = 1e4;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L82

[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

There are 8 instances of this issue:

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

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L31

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

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.

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.

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/RANGE.sol#L40

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

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.

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/interfaces/IOperator.sol#L15

File: src/policies/Operator.sol

97:           uint32[8] memory configParams // [cushionFactor, cushionDuration, cushionDebtBuffer, cushionDepositInterval, reserveFactor, regenWait, regenThreshold, regenObserve]

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/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L97

[N‑10] Variable names that consist of all capital letters should be reserved for constant/immutable variables

If the variable needs to be different based on which class it comes from, a view/pure function should be used instead (e.g. like this).

There are 18 instances of this issue:

File: src/policies/BondCallback.sol

29:       OlympusTreasury public TRSRY;

30:       OlympusMinter public MINTR;

68:           Keycode TRSRY_KEYCODE = TRSRY.KEYCODE();

69:           Keycode MINTR_KEYCODE = MINTR.KEYCODE();

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/BondCallback.sol#L29

File: src/policies/Governance.sol

56:       OlympusInstructions public INSTR;

57:       OlympusVotes public VOTES;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Governance.sol#L56

File: src/policies/Heart.sol

45:       OlympusPrice internal PRICE;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L45

File: src/policies/Operator.sol

69:       OlympusPrice internal PRICE;

70:       OlympusRange internal RANGE;

71:       OlympusTreasury internal TRSRY;

72:       OlympusMinter internal MINTR;

172:          Keycode RANGE_KEYCODE = RANGE.KEYCODE();

173:          Keycode TRSRY_KEYCODE = TRSRY.KEYCODE();

174:          Keycode MINTR_KEYCODE = MINTR.KEYCODE();

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L69

File: src/policies/PriceConfig.sol

11:       OlympusPrice internal PRICE;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/PriceConfig.sol#L11

File: src/policies/TreasuryCustodian.sol

20:       OlympusTreasury internal TRSRY;

35:           Keycode TRSRY_KEYCODE = TRSRY.KEYCODE();

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/TreasuryCustodian.sol#L20

File: src/policies/VoterRegistration.sol

10:       OlympusVotes public VOTES;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/VoterRegistration.sol#L10

[N‑11] Typos

There are 4 instances of this issue:

File: src/interfaces/IBondCallback.sol

/// @audit _callback
13:       /// @dev Should check that the quote tokens have been transferred to the contract in the _callback function

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/interfaces/IBondCallback.sol#L13

File: src/modules/PRICE.sol

/// @audit numbe
126:          // Cache numbe of observations to save gas.

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L126

File: src/policies/Operator.sol

/// @audit deactive
295:              /// If wall is down after swap, deactive the cushion as well

/// @audit deactive
326:              /// If wall is down after swap, deactive the cushion as well

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L295

[N‑12] File is missing NatSpec

There are 2 instances of this issue:

File: src/policies/TreasuryCustodian.sol

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

File: src/utils/KernelUtils.sol

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

[N‑13] NatSpec is incomplete

There are 7 instances of this issue:

File: src/modules/RANGE.sol

/// @audit Missing: '@return'
279       /// @notice Get the capacity for a side of the range.
280       /// @param  high_ - Specifies the side of the range to get capacity for (true = high side, false = low side).
281:      function capacity(bool high_) external view returns (uint256) {

/// @audit Missing: '@return'
289       /// @notice Get the status of a side of the range (whether it is active or not).
290       /// @param  high_ - Specifies the side of the range to get status for (true = high side, false = low side).
291:      function active(bool high_) external view returns (bool) {

/// @audit Missing: '@return'
300       /// @param  wall_ - Specifies the band to get the price for (true = wall, false = cushion).
301       /// @param  high_ - Specifies the side of the range to get the price for (true = high side, false = low side).
302:      function price(bool wall_, bool high_) external view returns (uint256) {

/// @audit Missing: '@return'
318       /// @notice Get the spread for the wall or cushion band.
319       /// @param  wall_ - Specifies the band to get the spread for (true = wall, false = cushion).
320:      function spread(bool wall_) external view returns (uint256) {

/// @audit Missing: '@return'
328       /// @notice Get the market ID for a side of the range.
329       /// @param  high_ - Specifies the side of the range to get market for (true = high side, false = low side).
330:      function market(bool high_) external view returns (uint256) {

/// @audit Missing: '@return'
338       /// @notice Get the timestamp when the range was last active.
339       /// @param  high_ - Specifies the side of the range to get timestamp for (true = high side, false = low side).
340:      function lastActive(bool high_) external view returns (uint256) {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/RANGE.sol#L279-L281

File: src/policies/interfaces/IOperator.sol

/// @audit Missing: '@return'
141       /// @dev    Calculates the capacity to deploy for a wall based on the amount of reserves owned by the treasury and the reserve factor.
142       /// @param  high_ - Whether to return the full capacity for the high or low wall
143:      function fullCapacity(bool high_) external view returns (uint256);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/interfaces/IOperator.sol#L141-L143

[N‑14] Event is missing indexed fields

Index event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Each event should use three indexed fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed.

There are 30 instances of this issue:

File: src/Kernel.sol

203       event PermissionsUpdated(
204           Keycode indexed keycode_,
205           Policy indexed policy_,
206           bytes4 funcSelector_,
207           bool granted_
208:      );

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/Kernel.sol#L203-L208

File: src/modules/INSTR.sol

11:       event InstructionsStored(uint256 instructionsId);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/INSTR.sol#L11

File: src/modules/PRICE.sol

26:       event NewObservation(uint256 timestamp_, uint256 price_, uint256 movingAverage_);

27:       event MovingAverageDurationChanged(uint48 movingAverageDuration_);

28:       event ObservationFrequencyChanged(uint48 observationFrequency_);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L26

File: src/modules/RANGE.sol

20:       event WallUp(bool high_, uint256 timestamp_, uint256 capacity_);

21:       event WallDown(bool high_, uint256 timestamp_, uint256 capacity_);

22:       event CushionUp(bool high_, uint256 timestamp_, uint256 capacity_);

23:       event CushionDown(bool high_, uint256 timestamp_);

24        event PricesChanged(
25            uint256 wallLowPrice_,
26            uint256 cushionLowPrice_,
27            uint256 cushionHighPrice_,
28            uint256 wallHighPrice_
29:       );

30:       event SpreadsChanged(uint256 cushionSpread_, uint256 wallSpread_);

31:       event ThresholdFactorChanged(uint256 thresholdFactor_);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/RANGE.sol#L20

File: src/modules/TRSRY.sol

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

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

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

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

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/TRSRY.sol#L20

File: src/policies/Governance.sol

86:       event ProposalSubmitted(uint256 proposalId);

87:       event ProposalEndorsed(uint256 proposalId, address voter, uint256 amount);

88:       event ProposalActivated(uint256 proposalId, uint256 timestamp);

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

90:       event ProposalExecuted(uint256 proposalId);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Governance.sol#L86

File: src/policies/Heart.sol

28:       event Beat(uint256 timestamp_);

29:       event RewardIssued(address to_, uint256 rewardAmount_);

30:       event RewardUpdated(ERC20 token_, uint256 rewardAmount_);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L28

File: src/policies/Operator.sol

45        event Swap(
46            ERC20 indexed tokenIn_,
47            ERC20 indexed tokenOut_,
48            uint256 amountIn_,
49            uint256 amountOut_
50:       );

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_);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L45-L50

File: src/policies/TreasuryCustodian.sol

17:       event ApprovalRevoked(address indexed policy_, ERC20[] tokens_);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/TreasuryCustodian.sol#L17

[N‑15] Not using the named return variables anywhere in the function is confusing

Consider changing the variable to be an unnamed one

There are 14 instances of this issue:

File: src/modules/INSTR.sol

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

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/INSTR.sol#L28

File: src/modules/MINTR.sol

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

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/MINTR.sol#L25

File: src/modules/PRICE.sol

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

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L113

File: src/modules/RANGE.sol

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

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/RANGE.sol#L115

File: src/modules/TRSRY.sol

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

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/TRSRY.sol#L51

File: src/modules/VOTES.sol

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

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/VOTES.sol#L27

File: src/policies/BondCallback.sol

/// @audit in_
/// @audit out_
173       function amountsForMarket(uint256 id_)
174           external
175           view
176           override
177:          returns (uint256 in_, uint256 out_)

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/BondCallback.sol#L173-L177

[N‑16] Avoid the use of sensitive terms

Use alternative variants, e.g. allowlist/denylist instead of whitelist/blacklist

There is 1 instance of this issue:

File: /src/policies/BondCallback.sol

83:      function whitelist(address teller_, uint256 id_)

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/BondCallback.sol#L83

Summary

Gas Optimizations

IssueInstances
[G‑01]State variables should only be updated once in a function1
[G‑02]Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct, where appropriate1
[G‑03]State variables only set in the constructor should be declared immutable11
[G‑04]State variables can be packed into fewer storage slots1
[G‑05]Using calldata instead of memory for read-only arguments in external functions saves gas5
[G‑06]Using storage instead of memory for structs/arrays saves gas4
[G‑07]State variables should be cached in stack variables rather than re-reading them from storage7
[G‑08]Multiple accesses of a mapping/array should use a local variable cache1
[G‑09]The result of function calls should be cached rather than re-calling the function3
[G‑10]<x> += <y> costs more gas than <x> = <x> + <y> for state variables3
[G‑11]internal functions only called once can be inlined to save gas9
[G‑12]Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if-statement1
[G‑13]<array>.length should not be looked up in every loop of a for-loop1
[G‑14]Optimize names to save gas20
[G‑15]Using bools for storage incurs overhead11
[G‑16]Use a more recent version of solidity3
[G‑17]++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too)7
[G‑18]Using private rather than public for constants, saves gas11
[G‑19]Don't compare boolean expressions to boolean literals2
[G‑20]Division by two should use bit shifting2
[G‑21]Superfluous event fields7
[G‑22]Functions guaranteed to revert when called by normal users can be marked payable36

Total: 147 instances over 22 issues

The source diffs can be directly applied to the code by putting the diff block into a file then doing git apply <file>

Gas Optimizations

[G‑01] State variables should only be updated once in a function

totalEndorsementsForProposal is updated twice in this function, but it could be optimized to only update the difference between previousEndorsement and userVotes instead. Futher optimizations would be to use a storage variable rather than looking up the hash of totalEndorsementsForProposal[proposalId_] each time, and to use x = x + a rather than x += a

There is 1 instance of this issue:

File: /src/policies/Governance.sol

192          // undo any previous endorsement the user made on these instructions
193          uint256 previousEndorsement = userEndorsementsForProposal[proposalId_][msg.sender];
194          totalEndorsementsForProposal[proposalId_] -= previousEndorsement;
195  
196          // reapply user endorsements with most up-to-date votes
197          userEndorsementsForProposal[proposalId_][msg.sender] = userVotes;
198:         totalEndorsementsForProposal[proposalId_] += userVotes;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Governance.sol#L192-L198

diff --git a/src/policies/Governance.sol b/src/policies/Governance.sol
index 8829e3b..c0e783f 100644
--- a/src/policies/Governance.sol
+++ b/src/policies/Governance.sol
@@ -191,11 +191,14 @@ contract OlympusGovernance is Policy {
 
         // undo any previous endorsement the user made on these instructions
         uint256 previousEndorsement = userEndorsementsForProposal[proposalId_][msg.sender];
-        totalEndorsementsForProposal[proposalId_] -= previousEndorsement;
 
         // reapply user endorsements with most up-to-date votes
         userEndorsementsForProposal[proposalId_][msg.sender] = userVotes;
-        totalEndorsementsForProposal[proposalId_] += userVotes;
+        if (previousEndorsement > userVotes) {
+            totalEndorsementsForProposal[proposalId_] -= (previousEndorsement - userVotes);
+        } else {
+            totalEndorsementsForProposal[proposalId_] += (userVotes - previousEndorsement);
+        }
 
         emit ProposalEndorsed(proposalId_, msg.sender, userVotes);
     }

Note that the numbers below are an underreporting of the gas changes due to this forge issue

diff --git a/tmp/gas_before b/tmp/gas_after
index c793374..828386f 100644
--- a/tmp/gas_before
+++ b/tmp/gas_after
@@ -289,7 +289,7 @@
 ā•žā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•”
 │ Deployment Cost                                        ┆ Deployment Size ┆        ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ 1638243                                                ┆ 8250            ┆        ┆        ┆        ┆         │
+│ 1642043                                                ┆ 8269            ┆        ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ Function Name                                          ┆ min             ┆ avg    ┆ median ┆ max    ┆ # calls │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -297,7 +297,7 @@
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ configureDependencies                                  ┆ 47868           ┆ 48513  ┆ 47868  ┆ 51868  ┆ 31      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ endorseProposal                                        ┆ 6874            ┆ 39015  ┆ 30774  ┆ 52674  ┆ 43      │
+│ endorseProposal                                        ┆ 6476            ┆ 38636  ┆ 30376  ┆ 52276  ┆ 43      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ executeProposal                                        ┆ 1850            ┆ 171376 ┆ 238748 ┆ 238748 ┆ 7       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤

[G‑02] Multiple address/ID mappings can be combined into a single mapping of an address/ID 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.

There is 1 instance of this issue:

File: src/policies/Governance.sol

96        mapping(uint256 => ProposalMetadata) public getProposalMetadata;
97    
98        /// @notice Return the total endorsements for a proposal id.
99        mapping(uint256 => uint256) public totalEndorsementsForProposal;
100   
101       /// @notice Return the number of endorsements a user has given a proposal id.
102       mapping(uint256 => mapping(address => uint256)) public userEndorsementsForProposal;
103   
104       /// @notice Return whether a proposal id has been activated. Once this is true, it should never be flipped false.
105       mapping(uint256 => bool) public proposalHasBeenActivated;
106   
107       /// @notice Return the total yes votes for a proposal id used in calculating net votes.
108       mapping(uint256 => uint256) public yesVotesForProposal;
109   
110       /// @notice Return the total no votes for a proposal id used in calculating net votes.
111       mapping(uint256 => uint256) public noVotesForProposal;
112   
113       /// @notice Return the amount of votes a user has applied to a proposal id. This does not record how the user voted.
114       mapping(uint256 => mapping(address => uint256)) public userVotesForProposal;
115   
116       /// @notice Return the amount of tokens reclaimed by a user after voting on a proposal id.
117:      mapping(uint256 => mapping(address => bool)) public tokenClaimsForProposal;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Governance.sol#L96-L117

[G‑03] State variables only set in the constructor should be declared immutable

Avoids a Gsset (20000 gas) in the constructor, and replaces the first access in each transaction (Gcoldsload - 2100 gas) and each access thereafter (Gwarmacces - 100 gas) with a PUSH32 (3 gas).

There are 11 instances of this issue:

File: src/policies/BondCallback.sol

/// @audit aggregator (constructor)
43:           aggregator = aggregator_;

/// @audit aggregator (access)
91:           (, , ERC20 payoutToken, , , ) = aggregator.getAuctioneer(id_).getMarketInfoForPurchase(id_);

/// @audit aggregator (access)
109:          (, , ERC20 payoutToken, ERC20 quoteToken, , ) = aggregator

/// @audit ohm (constructor)
44:           ohm = ohm_;

/// @audit ohm (access)
57:           ohm.safeApprove(address(MINTR), type(uint256).max);

/// @audit ohm (access)
94:           if (address(payoutToken) != address(ohm)) {

/// @audit ohm (access)
118:          if (quoteToken == payoutToken && quoteToken == ohm) {

/// @audit ohm (access)
125:          } else if (quoteToken == ohm) {

/// @audit ohm (access)
131:          } else if (payoutToken == ohm) {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/BondCallback.sol#L43

File: src/policies/Heart.sol

/// @audit _operator (constructor)
60:           _operator = operator_;

/// @audit _operator (access)
100:          _operator.operate();

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L60

diff --git a/src/policies/BondCallback.sol b/src/policies/BondCallback.sol
index 4da1a3a..4383f7e 100644
--- a/src/policies/BondCallback.sol
+++ b/src/policies/BondCallback.sol
@@ -25,11 +25,11 @@ contract BondCallback is Policy, ReentrancyGuard, IBondCallback {
     mapping(uint256 => uint256[2]) internal _amountsPerMarket;
     mapping(ERC20 => uint256) public priorBalances;
 
-    IBondAggregator public aggregator;
+    IBondAggregator immutable public aggregator;
     OlympusTreasury public TRSRY;
     OlympusMinter public MINTR;
     Operator public operator;
-    ERC20 public ohm;
+    ERC20 immutable public ohm;
 
     /*//////////////////////////////////////////////////////////////
                             POLICY INTERFACE
diff --git a/src/policies/Heart.sol b/src/policies/Heart.sol
index 7693dba..b0a46f2 100644
--- a/src/policies/Heart.sol
+++ b/src/policies/Heart.sol
@@ -45,7 +45,7 @@ contract OlympusHeart is IHeart, Policy, ReentrancyGuard {
     OlympusPrice internal PRICE;
 
     // Policies
-    IOperator internal _operator;
+    IOperator immutable internal _operator;
 
     /*//////////////////////////////////////////////////////////////
                             POLICY INTERFACE

Note that the numbers below are an underreporting of the gas changes due to this forge issue

diff --git a/tmp/gas_before b/tmp/gas_after
index c793374..1cea2f8 100644
--- a/tmp/gas_before
+++ b/tmp/gas_after
@@ -24,7 +24,7 @@
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ allKeycodes                    ┆ 706             ┆ 706    ┆ 706    ┆ 706    ┆ 2       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ executeAction                  ┆ 649             ┆ 156798 ┆ 94565  ┆ 595110 ┆ 767     │
+│ executeAction                  ┆ 649             ┆ 156789 ┆ 94565  ┆ 595110 ┆ 767     │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ executor                       ┆ 2393            ┆ 2393   ┆ 2393   ┆ 2393   ┆ 1       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -258,7 +258,7 @@
 ā•žā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•”
 │ Deployment Cost                                     ┆ Deployment Size ┆       ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ 1408325                                             ┆ 6934            ┆       ┆        ┆        ┆         │
+│ 1417471                                             ┆ 7248            ┆       ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ Function Name                                       ┆ min             ┆ avg   ┆ median ┆ max    ┆ # calls │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -268,9 +268,9 @@
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ batchToTreasury                                     ┆ 4111            ┆ 12729 ┆ 12068  ┆ 22668  ┆ 4       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ callback                                            ┆ 7678            ┆ 98980 ┆ 85627  ┆ 187927 ┆ 17      │
+│ callback                                            ┆ 7678            ┆ 97563 ┆ 85333  ┆ 183633 ┆ 17      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ configureDependencies                               ┆ 73564           ┆ 73564 ┆ 73564  ┆ 73564  ┆ 63      │
+│ configureDependencies                               ┆ 73464           ┆ 73464 ┆ 73464  ┆ 73464  ┆ 63      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ isActive                                            ┆ 395             ┆ 395   ┆ 395    ┆ 395    ┆ 63      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -282,7 +282,7 @@
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ setOperator                                         ┆ 8227            ┆ 23461 ┆ 23865  ┆ 23865  ┆ 65      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ whitelist                                           ┆ 8221            ┆ 39608 ┆ 34675  ┆ 67005  ┆ 46      │
+│ whitelist                                           ┆ 8221            ┆ 38018 ┆ 32143  ┆ 62802  ┆ 46      │
 ╰─────────────────────────────────────────────────────┓─────────────────┓───────┓────────┓────────┓─────────╯
 ╭────────────────────────────────────────────────────────┬─────────────────┬────────┬────────┬────────┬─────────╮
 │ src/policies/Governance.sol:OlympusGovernance contract ┆                 ┆        ┆        ┆        ┆         │
@@ -336,13 +336,13 @@
 ā•žā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•”
 │ Deployment Cost                              ┆ Deployment Size ┆       ┆        ┆       ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ 934119                                       ┆ 4277            ┆       ┆        ┆       ┆         │
+│ 914213                                       ┆ 4290            ┆       ┆        ┆       ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ Function Name                                ┆ min             ┆ avg   ┆ median ┆ max   ┆ # calls │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ active                                       ┆ 323             ┆ 989   ┆ 323    ┆ 2323  ┆ 3       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ beat                                         ┆ 5429            ┆ 29228 ┆ 18552  ┆ 61386 ┆ 8       │
+│ beat                                         ┆ 5429            ┆ 28154 ┆ 17478  ┆ 59238 ┆ 8       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ configureDependencies                        ┆ 24123           ┆ 24123 ┆ 24123  ┆ 24123 ┆ 11      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -397,7 +397,7 @@
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ isActive                                    ┆ 439             ┆ 439    ┆ 439    ┆ 439    ┆ 63      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ operate                                     ┆ 387             ┆ 122263 ┆ 37958  ┆ 640609 ┆ 104     │
+│ operate                                     ┆ 387             ┆ 121697 ┆ 37958  ┆ 636406 ┆ 104     │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ regenerate                                  ┆ 3772            ┆ 17622  ┆ 21791  ┆ 29292  ┆ 6       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -577,7 +577,7 @@
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ getFee                                                                  ┆ 872             ┆ 3538   ┆ 4872   ┆ 4872   ┆ 6       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ purchase                                                                ┆ 228899          ┆ 239488 ┆ 239488 ┆ 250077 ┆ 2       │
+│ purchase                                                                ┆ 228739          ┆ 239261 ┆ 239261 ┆ 249783 ┆ 2       │
 ╰─────────────────────────────────────────────────────────────────────────┓─────────────────┓────────┓────────┓────────┓─────────╯
 ╭────────────────────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮
 │ src/test/mocks/KernelTestMocks.sol:MockModule contract ┆                 ┆       ┆        ┆       ┆         │

[G‑04] State variables can be packed into fewer storage slots

If variables occupying the same slot are both written the same function or by the constructor, avoids a separate Gsset (20000 gas). Reads of the variables can also be cheaper

There is 1 instance of this issue:

File: src/policies/Heart.sol

/// @audit Variable ordering with 5 slots instead of the current 6:
///           uint256(32):lastBeat, uint256(32):reward, user-defined(20):rewardToken, bool(1):active, address(20):PRICE, address(20):_operator
33:       bool public active;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L33

diff --git a/src/policies/Heart.sol b/src/policies/Heart.sol
index 7693dba..ced0bcb 100644
--- a/src/policies/Heart.sol
+++ b/src/policies/Heart.sol
@@ -29,9 +29,6 @@ contract OlympusHeart is IHeart, Policy, ReentrancyGuard {
     event RewardIssued(address to_, uint256 rewardAmount_);
     event RewardUpdated(ERC20 token_, uint256 rewardAmount_);
 
-    /// @notice Status of the Heart, false = stopped, true = beating
-    bool public active;
-
     /// @notice Timestamp of the last beat (UTC, in seconds)
     uint256 public lastBeat;
 
@@ -41,6 +38,9 @@ contract OlympusHeart is IHeart, Policy, ReentrancyGuard {
     /// @notice Reward token address that users are sent for beating the Heart
     ERC20 public rewardToken;
 
+    /// @notice Status of the Heart, false = stopped, true = beating
+    bool public active;
+
     // Modules
     OlympusPrice internal PRICE;

Note that the numbers below are an underreporting of the gas changes due to this forge issue

diff --git a/tmp/gas_before b/tmp/gas_after
index c793374..9682b00 100644
--- a/tmp/gas_before
+++ b/tmp/gas_after
@@ -336,13 +336,13 @@
 ā•žā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•”
 │ Deployment Cost                              ┆ Deployment Size ┆       ┆        ┆       ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ 934119                                       ┆ 4277            ┆       ┆        ┆       ┆         │
+│ 917440                                       ┆ 4309            ┆       ┆        ┆       ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ Function Name                                ┆ min             ┆ avg   ┆ median ┆ max   ┆ # calls │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ active                                       ┆ 323             ┆ 989   ┆ 323    ┆ 2323  ┆ 3       │
+│ active                                       ┆ 340             ┆ 1006  ┆ 340    ┆ 2340  ┆ 3       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ beat                                         ┆ 5429            ┆ 29228 ┆ 18552  ┆ 61386 ┆ 8       │
+│ beat                                         ┆ 5443            ┆ 28492 ┆ 18566  ┆ 59400 ┆ 8       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ configureDependencies                        ┆ 24123           ┆ 24123 ┆ 24123  ┆ 24123 ┆ 11      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -362,7 +362,7 @@
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ setRewardTokenAndAmount                      ┆ 8222            ┆ 13892 ┆ 13892  ┆ 19562 ┆ 2       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ toggleBeat                                   ┆ 1400            ┆ 7187  ┆ 8455   ┆ 10440 ┆ 4       │
+│ toggleBeat                                   ┆ 1427            ┆ 8416  ┆ 9577   ┆ 13083 ┆ 4       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ withdrawUnspentRewards                       ┆ 8201            ┆ 19621 ┆ 19621  ┆ 31041 ┆ 2       │
 ╰──────────────────────────────────────────────┓─────────────────┓───────┓────────┓───────┓─────────╯

[G‑05] Using calldata instead of memory for read-only arguments in external functions saves gas

When a function with a memory array is called externally, the abi.decode() step has to use a for-loop to copy each index of the calldata to the memory index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length). Using calldata directly, obliviates the need for such a loop in the contract code and runtime execution. Note that even if an interface defines a function as having memory arguments, it's still valid for implementation contracs to use calldata arguments instead.

If the array is passed to an internal function which passes the array to another internal function where the array is modified and therefore memory is used in the external call, it's still more gass-efficient to use calldata when the external function uses modifiers, since the modifiers may prevent the internal functions from being called. Structs have the same overhead as an array of length one

Note that I've also flagged instances where the function is public but can be marked as external since it's not called by the contract, and cases where a constructor is involved

There are 5 instances of this issue:

File: src/modules/PRICE.sol

/// @audit startObservations_
205       function initialize(uint256[] memory startObservations_, uint48 lastObservationTime_)
206           external
207:          permissioned

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L205-L207

File: src/policies/BondCallback.sol

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

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/BondCallback.sol#L152

File: src/policies/Governance.sol

/// @audit proposalURI_
159       function submitProposal(
160           Instruction[] calldata instructions_,
161           bytes32 title_,
162:          string memory proposalURI_

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Governance.sol#L159-L162

File: src/policies/PriceConfig.sol

/// @audit startObservations_
45        function initialize(uint256[] memory startObservations_, uint48 lastObservationTime_)
46            external
47:           onlyRole("price_admin")

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/PriceConfig.sol#L45-L47

File: src/policies/TreasuryCustodian.sol

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

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/TreasuryCustodian.sol#L53

diff --git a/src/modules/PRICE.sol b/src/modules/PRICE.sol
index 55d85d3..5a620d7 100644
--- a/src/modules/PRICE.sol
+++ b/src/modules/PRICE.sol
@@ -202,7 +202,7 @@ contract OlympusPrice is Module {
     /// @param  lastObservationTime_ - Unix timestamp of last observation being provided (in seconds).
     /// @dev    This function must be called after the Price module is deployed to activate it and after updating the observationFrequency
     ///         or movingAverageDuration (in certain cases) in order for the Price module to function properly.
-    function initialize(uint256[] memory startObservations_, uint48 lastObservationTime_)
+    function initialize(uint256[] calldata startObservations_, uint48 lastObservationTime_)
         external
         permissioned
     {
diff --git a/src/policies/BondCallback.sol b/src/policies/BondCallback.sol
index 4da1a3a..902bcfc 100644
--- a/src/policies/BondCallback.sol
+++ b/src/policies/BondCallback.sol
@@ -149,7 +149,7 @@ contract BondCallback is Policy, ReentrancyGuard, IBondCallback {
 
     /// @notice Send tokens to the TRSRY in a batch
     /// @param  tokens_ - Array of tokens to send
-    function batchToTreasury(ERC20[] memory tokens_) external onlyRole("callback_admin") {
+    function batchToTreasury(ERC20[] calldata tokens_) external onlyRole("callback_admin") {
         ERC20 token;
         uint256 balance;
         uint256 len = tokens_.length;
diff --git a/src/policies/Governance.sol b/src/policies/Governance.sol
index 8829e3b..a39dad0 100644
--- a/src/policies/Governance.sol
+++ b/src/policies/Governance.sol
@@ -159,7 +159,7 @@ contract OlympusGovernance is Policy {
     function submitProposal(
         Instruction[] calldata instructions_,
         bytes32 title_,
-        string memory proposalURI_
+        string calldata proposalURI_
     ) external {
         if (VOTES.balanceOf(msg.sender) * 10000 < VOTES.totalSupply() * SUBMISSION_REQUIREMENT)
             revert NotEnoughVotesToPropose();
diff --git a/src/policies/PriceConfig.sol b/src/policies/PriceConfig.sol
index 78887fd..214e0ab 100644
--- a/src/policies/PriceConfig.sol
+++ b/src/policies/PriceConfig.sol
@@ -42,7 +42,7 @@ contract OlympusPriceConfig is Policy {
     /// @param lastObservationTime_ Unix timestamp of last observation being provided (in seconds).
     /// @dev This function must be called after the Price module is deployed to activate it and after updating the observationFrequency
     ///      or movingAverageDuration (in certain cases) in order for the Price module to function properly.
-    function initialize(uint256[] memory startObservations_, uint48 lastObservationTime_)
+    function initialize(uint256[] calldata startObservations_, uint48 lastObservationTime_)
         external
         onlyRole("price_admin")
     {
diff --git a/src/policies/TreasuryCustodian.sol b/src/policies/TreasuryCustodian.sol
index 1c12f2e..a240cd1 100644
--- a/src/policies/TreasuryCustodian.sol
+++ b/src/policies/TreasuryCustodian.sol
@@ -50,7 +50,7 @@ contract TreasuryCustodian is Policy {
     // Anyone can call to revoke a deactivated policy's approvals.
     // TODO Currently allows anyone to revoke any approval EXCEPT activated policies.
     // TODO must reorg policy storage to be able to check for deactivated policies.
-    function revokePolicyApprovals(address policy_, ERC20[] memory tokens_) external {
+    function revokePolicyApprovals(address policy_, ERC20[] calldata tokens_) external {
         if (Policy(policy_).isActive()) revert PolicyStillActive();
 
         // TODO Make sure `policy_` is an actual policy and not a random address.

Note that the numbers below are an underreporting of the gas changes due to this forge issue

diff --git a/tmp/gas_before b/tmp/gas_after
index c793374..513078c 100644
--- a/tmp/gas_before
+++ b/tmp/gas_after
@@ -108,7 +108,7 @@
 ā•žā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•”
 │ Deployment Cost                             ┆ Deployment Size ┆        ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ 1117743                                     ┆ 6851            ┆        ┆        ┆        ┆         │
+│ 1101930                                     ┆ 6772            ┆        ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ Function Name                               ┆ min             ┆ avg    ┆ median ┆ max    ┆ # calls │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -128,7 +128,7 @@
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ getMovingAverage                            ┆ 544             ┆ 812    ┆ 544    ┆ 2420   ┆ 7       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ initialize                                  ┆ 4063            ┆ 432495 ┆ 512316 ┆ 886622 ┆ 24      │
+│ initialize                                  ┆ 2327            ┆ 430562 ┆ 510516 ┆ 883159 ┆ 24      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ initialized                                 ┆ 340             ┆ 955    ┆ 340    ┆ 2340   ┆ 13      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -258,7 +258,7 @@
 ā•žā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•”
 │ Deployment Cost                                     ┆ Deployment Size ┆       ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ 1408325                                             ┆ 6934            ┆       ┆        ┆        ┆         │
+│ 1386305                                             ┆ 6824            ┆       ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ Function Name                                       ┆ min             ┆ avg   ┆ median ┆ max    ┆ # calls │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -266,7 +266,7 @@
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ approvedMarkets                                     ┆ 685             ┆ 685   ┆ 685    ┆ 685    ┆ 2       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ batchToTreasury                                     ┆ 4111            ┆ 12729 ┆ 12068  ┆ 22668  ┆ 4       │
+│ batchToTreasury                                     ┆ 3800            ┆ 12543 ┆ 11920  ┆ 22533  ┆ 4       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ callback                                            ┆ 7678            ┆ 98980 ┆ 85627  ┆ 187927 ┆ 17      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -289,7 +289,7 @@
 ā•žā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•”
 │ Deployment Cost                                        ┆ Deployment Size ┆        ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ 1638243                                                ┆ 8250            ┆        ┆        ┆        ┆         │
+│ 1645850                                                ┆ 8288            ┆        ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ Function Name                                          ┆ min             ┆ avg    ┆ median ┆ max    ┆ # calls │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -317,7 +317,7 @@
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ setActiveStatus                                        ┆ 777             ┆ 1228   ┆ 777    ┆ 3577   ┆ 31      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ submitProposal                                         ┆ 11529           ┆ 176506 ┆ 187247 ┆ 187247 ┆ 25      │
+│ submitProposal                                         ┆ 11364           ┆ 176510 ┆ 187258 ┆ 187258 ┆ 25      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ tokenClaimsForProposal                                 ┆ 684             ┆ 684    ┆ 684    ┆ 684    ┆ 1       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -430,7 +430,7 @@
 ā•žā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•”
 │ Deployment Cost                                          ┆ Deployment Size ┆        ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ 639600                                                   ┆ 3262            ┆        ┆        ┆        ┆         │
+│ 620181                                                   ┆ 3165            ┆        ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ Function Name                                            ┆ min             ┆ avg    ┆ median ┆ max    ┆ # calls │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -440,7 +440,7 @@
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ configureDependencies                                    ┆ 24144           ┆ 24144  ┆ 24144  ┆ 24144  ┆ 5       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ initialize                                               ┆ 10107           ┆ 491657 ┆ 524236 ┆ 895872 ┆ 7       │
+│ initialize                                               ┆ 8371            ┆ 486274 ┆ 519133 ┆ 885906 ┆ 7       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ isActive                                                 ┆ 317             ┆ 317    ┆ 317    ┆ 317    ┆ 5       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -453,7 +453,7 @@
 ā•žā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•”
 │ Deployment Cost                                               ┆ Deployment Size ┆       ┆        ┆       ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ 739696                                                        ┆ 3762            ┆       ┆        ┆       ┆         │
+│ 719277                                                        ┆ 3660            ┆       ┆        ┆       ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ Function Name                                                 ┆ min             ┆ avg   ┆ median ┆ max   ┆ # calls │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -469,7 +469,7 @@
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ requestPermissions                                            ┆ 2061            ┆ 2477  ┆ 2061   ┆ 4561  ┆ 6       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ revokePolicyApprovals                                         ┆ 6956            ┆ 6956  ┆ 6956   ┆ 6956  ┆ 1       │
+│ revokePolicyApprovals                                         ┆ 6842            ┆ 6842  ┆ 6842   ┆ 6842  ┆ 1       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ setActiveStatus                                               ┆ 733             ┆ 733   ┆ 733    ┆ 733   ┆ 6       │
 ╰───────────────────────────────────────────────────────────────┓─────────────────┓───────┓────────┓───────┓─────────╯

[G‑06] 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

There are 4 instances of this issue:

File: src/policies/Operator.sol

206:          Config memory config_ = _config;

385:              Config memory config_ = _config;

440:              Config memory config_ = _config;

666:          Regen memory regen = _status.low;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L206

diff --git a/src/policies/Operator.sol b/src/policies/Operator.sol
index 7573526..e3c5c53 100644
--- a/src/policies/Operator.sol
+++ b/src/policies/Operator.sol
@@ -203,7 +203,7 @@ contract Operator is IOperator, Policy, ReentrancyGuard {
         _updateCapacity(false, 0);
 
         /// Cache config in memory
-        Config memory config_ = _config;
+        Config storage config_ = _config;
 
         /// Check if walls can regenerate capacity
         if (
@@ -437,7 +437,7 @@ contract Operator is IOperator, Policy, ReentrancyGuard {
             uint256 minimumPrice = invCushionPrice.mulDiv(bondScale, oracleScale);
 
             /// Cache config struct to avoid multiple SLOADs
-            Config memory config_ = _config;
+            Config storage config_ = _config;
 
             /// Calculate market capacity from the cushion factor
             uint256 marketCapacity = range.low.capacity.mulDiv(config_.cushionFactor, FACTOR_SCALE);
@@ -663,7 +663,7 @@ contract Operator is IOperator, Policy, ReentrancyGuard {
         /// Update low side regen status with a new observation
         /// Observation is positive if the current price is greater than the MA
         uint32 observe = _config.regenObserve;
-        Regen memory regen = _status.low;
+        Regen storage regen = _status.low;
         if (currentPrice >= movingAverage) {
             if (!regen.observations[regen.nextObservation]) {
                 _status.low.observations[regen.nextObservation] = true;
diff --git a/tmp/gas_before b/tmp/gas_after
index c793374..0d24e75 100644
--- a/tmp/gas_before
+++ b/tmp/gas_after
@@ -371,7 +371,7 @@
 ā•žā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•Ŗā•ā•ā•ā•ā•ā•ā•ā•ā•ā•”
 │ Deployment Cost                             ┆ Deployment Size ┆        ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ 4679925                                     ┆ 25703           ┆        ┆        ┆        ┆         │
+│ 4594613                                     ┆ 25277           ┆        ┆        ┆        ┆         │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ Function Name                               ┆ min             ┆ avg    ┆ median ┆ max    ┆ # calls │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
@@ -397,7 +397,7 @@
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ isActive                                    ┆ 439             ┆ 439    ┆ 439    ┆ 439    ┆ 63      │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
-│ operate                                     ┆ 387             ┆ 122263 ┆ 37958  ┆ 640609 ┆ 104     │
+│ operate                                     ┆ 387             ┆ 118525 ┆ 34414  ┆ 636359 ┆ 104     │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤
 │ regenerate                                  ┆ 3772            ┆ 17622  ┆ 21791  ┆ 29292  ┆ 6       │
 ā”œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¼ā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā•Œā”¤

[G‑07] State variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second+ access of a state variable within a function. Caching of a state variable 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 7 instances of this issue:

File: src/modules/PRICE.sol

/// @audit _movingAverage on line 138
146:          emit NewObservation(block.timestamp, currentPrice, _movingAverage);

/// @audit nextObsIndex on line 185
185:          uint32 lastIndex = nextObsIndex == 0 ? numObservations - 1 : nextObsIndex - 1;

/// @audit numObservations on line 97
100:          observations = new uint256[](numObservations);

/// @audit observationFrequency on line 165
171:              if (updatedAt < block.timestamp - uint256(observationFrequency))

/// @audit observationFrequency on line 242
246:          uint256 newObservations = uint256(movingAverageDuration_ / observationFrequency);

/// @audit movingAverageDuration on line 268
272:          uint256 newObservations = uint256(movingAverageDuration / observationFrequency_);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L146

File: src/policies/Heart.sol

/// @audit reward on line 112
113:          emit RewardIssued(to_, reward);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L113

[G‑08] Multiple accesses of a mapping/array should use a local variable cache

The instances below point to the second+ access of a value inside a mapping/array, within a function. Caching a mapping's value in a local storage or calldata variable when the value is accessed multiple times, saves ~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. Caching an array's struct avoids recalculating the array offsets into memory/calldata

There is 1 instance of this issue:

File: src/Kernel.sol

/// @audit moduleDependents[keycode] on line 309
310:              getDependentIndex[keycode][policy_] = moduleDependents[keycode].length - 1;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/Kernel.sol#L310

[G‑09] The result of function calls should be cached rather than re-calling the function

The instances below point to the second+ call of the function within a single function

There are 3 instances of this issue:

File: src/policies/PriceConfig.sol

/// @audit PRICE.KEYCODE() on line 32
33:           permissions[1] = Permissions(PRICE.KEYCODE(), PRICE.changeMovingAverageDuration.selector);

/// @audit PRICE.KEYCODE() on line 32
34:           permissions[2] = Permissions(PRICE.KEYCODE(), PRICE.changeObservationFrequency.selector);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/PriceConfig.sol#L33

File: src/policies/VoterRegistration.sol

/// @audit VOTES.KEYCODE() on line 34
35:           permissions[1] = Permissions(VOTES.KEYCODE(), VOTES.burnFrom.selector);

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/VoterRegistration.sol#L35

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

Using the addition operator instead of plus-equals saves 113 gas

There are 3 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/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L136

File: src/policies/Heart.sol

103:          lastBeat += frequency();

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L103

[G‑11] 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.

There are 9 instances of this issue:

File: src/Kernel.sol

266:      function _installModule(Module newModule_) internal {

279:      function _upgradeModule(Module newModule_) internal {

295:      function _activatePolicy(Policy policy_) internal {

325:      function _deactivatePolicy(Policy policy_) internal {

351:      function _migrateKernel(Kernel newKernel_) internal {

378:      function _reconfigurePolicies(Keycode keycode_) internal {

409:      function _pruneFromDependents(Policy policy_) internal {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/Kernel.sol#L266

File: src/policies/Heart.sol

111:      function _issueReward(address to_) internal {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L111

File: src/policies/Operator.sol

652       function _addObservation() internal {
653           /// Get latest moving average from the price module
654:          uint256 movingAverage = PRICE.getMovingAverage();

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L652-L654

[G‑12] Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if-statement

require(a <= b); x = b - a => require(a <= b); unchecked { x = b - a }

There is 1 instance of this issue:

File: src/modules/PRICE.sol

/// @audit if-condition on line 135
136:              _movingAverage += (currentPrice - earliestPrice) / numObs;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L136

[G‑13] <array>.length should not be looked up in every loop of a for-loop

The overheads outlined below are PER LOOP, excluding the first loop

  • storage arrays incur a Gwarmaccess (100 gas)
  • memory arrays use MLOAD (3 gas)
  • calldata arrays use CALLDATALOAD (3 gas)

Caching the length changes each of these to a DUP<N> (3 gas), and gets rid of the extra DUP<N> needed to store the stack offset

There is 1 instance of this issue:

File: src/policies/Governance.sol

278:          for (uint256 step; step < instructions.length; ) {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Governance.sol#L278

[G‑14] Optimize names to save gas

public/external function names and public member variable names can be optimized to save gas. See this link for an example of how it works. Below are the interfaces/abstract contracts that can be optimized so that the most frequently-called functions use the least amount of gas possible during method lookup. Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shifted

There are 20 instances of this issue:

File: src/interfaces/IBondCallback.sol

/// @audit callback(), amountsForMarket(), whitelist()
6:    interface IBondCallback {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/interfaces/IBondCallback.sol#L6

File: src/Kernel.sol

/// @audit changeKernel()
62:   abstract contract KernelAdapter {

/// @audit KEYCODE(), VERSION(), INIT()
84:   abstract contract Module is KernelAdapter {

/// @audit setActiveStatus(), configureDependencies(), requestPermissions()
111:  abstract contract Policy is KernelAdapter {

/// @audit executeAction(), grantRole(), revokeRole()
149:  contract Kernel {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/Kernel.sol#L62

File: src/modules/INSTR.sol

/// @audit getInstructions(), store()
10:   contract OlympusInstructions is Module {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/INSTR.sol#L10

File: src/modules/MINTR.sol

/// @audit mintOhm(), burnOhm()
8:    contract OlympusMinter is Module {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/MINTR.sol#L8

File: src/modules/PRICE.sol

/// @audit updateMovingAverage(), getCurrentPrice(), getLastPrice(), getMovingAverage(), initialize(), changeMovingAverageDuration(), changeObservationFrequency()
22:   contract OlympusPrice is Module {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L22

File: src/modules/RANGE.sol

/// @audit updateCapacity(), updatePrices(), regenerate(), updateMarket(), setSpreads(), setThresholdFactor(), range(), capacity(), active(), price(), spread(), market(), lastActive()
16:   contract OlympusRange is Module {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/RANGE.sol#L16

File: src/modules/TRSRY.sol

/// @audit getReserveBalance(), setApprovalFor(), withdrawReserves(), getLoan(), repayLoan(), setDebt()
17:   contract OlympusTreasury is Module, ReentrancyGuard {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/TRSRY.sol#L17

File: src/modules/VOTES.sol

/// @audit mintTo(), burnFrom()
11:   contract OlympusVotes is Module, ERC20 {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/VOTES.sol#L11

File: src/policies/BondCallback.sol

/// @audit batchToTreasury(), setOperator()
17:   contract BondCallback is Policy, ReentrancyGuard, IBondCallback {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/BondCallback.sol#L17

File: src/policies/Governance.sol

/// @audit getMetadata(), getActiveProposal(), submitProposal(), endorseProposal(), activateProposal(), vote(), executeProposal(), reclaimVotes()
51:   contract OlympusGovernance is Policy {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Governance.sol#L51

File: src/policies/Heart.sol

/// @audit beat(), frequency(), resetBeat(), toggleBeat(), setRewardTokenAndAmount(), withdrawUnspentRewards()
21:   contract OlympusHeart is IHeart, Policy, ReentrancyGuard {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L21

File: src/policies/interfaces/IHeart.sol

/// @audit beat(), frequency(), resetBeat(), toggleBeat(), setRewardTokenAndAmount(), withdrawUnspentRewards()
6:    interface IHeart {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/interfaces/IHeart.sol#L6

File: src/policies/interfaces/IOperator.sol

/// @audit operate(), swap(), getAmountOut(), setSpreads(), setThresholdFactor(), setCushionFactor(), setCushionParams(), setReserveFactor(), setRegenParams(), setBondContracts(), initialize(), regenerate(), toggleActive(), fullCapacity(), status(), config()
8:    interface IOperator {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/interfaces/IOperator.sol#L8

File: src/policies/Operator.sol

/// @audit bondPurchase(), setSpreads(), setThresholdFactor(), setCushionFactor(), setCushionParams(), setReserveFactor(), setRegenParams(), setBondContracts(), initialize(), regenerate(), toggleActive(), getAmountOut()
30:   contract Operator is IOperator, Policy, ReentrancyGuard {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L30

File: src/policies/PriceConfig.sol

/// @audit initialize(), changeMovingAverageDuration(), changeObservationFrequency()
7:    contract OlympusPriceConfig is Policy {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/PriceConfig.sol#L7

File: src/policies/TreasuryCustodian.sol

/// @audit grantApproval(), revokePolicyApprovals(), increaseDebt(), decreaseDebt()
15:   contract TreasuryCustodian is Policy {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/TreasuryCustodian.sol#L15

File: src/policies/VoterRegistration.sol

/// @audit issueVotesTo(), revokeVotesFrom()
9:    contract VoterRegistration is Policy {

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/VoterRegistration.sol#L9

[G‑15] 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.

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27 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 11 instances of this issue:

File: src/Kernel.sol

113:      bool public isActive;

181:      mapping(Keycode => mapping(Policy => mapping(bytes4 => bool))) public modulePermissions;

194:      mapping(address => mapping(Role => bool)) public hasRole;

197:      mapping(Role => bool) public isRole;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/Kernel.sol#L113

File: src/modules/PRICE.sol

62:       bool public initialized;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L62

File: src/policies/BondCallback.sol

24:       mapping(address => mapping(uint256 => bool)) public approvedMarkets;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/BondCallback.sol#L24

File: src/policies/Governance.sol

105:      mapping(uint256 => bool) public proposalHasBeenActivated;

117:      mapping(uint256 => mapping(address => bool)) public tokenClaimsForProposal;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Governance.sol#L105

File: src/policies/Heart.sol

33:       bool public active;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Heart.sol#L33

File: src/policies/Operator.sol

63:       bool public initialized;

66:       bool public active;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L63

[G‑16] 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/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/interfaces/IBondCallback.sol#L2

File: src/policies/interfaces/IHeart.sol

2:    pragma solidity >=0.8.0;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/interfaces/IHeart.sol#L2

File: src/policies/interfaces/IOperator.sol

2:    pragma solidity >=0.8.0;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/interfaces/IOperator.sol#L2

[G‑17] ++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too)

Saves 5 gas per 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/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L488

File: src/utils/KernelUtils.sol

49:               i++;

64:               i++;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/utils/KernelUtils.sol#L49

[G‑18] 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 11 instances of this issue:

File: src/modules/PRICE.sol

59:       uint8 public constant decimals = 18;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/PRICE.sol#L59

File: src/modules/RANGE.sol

65:       uint256 public constant FACTOR_SCALE = 1e4;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/modules/RANGE.sol#L65

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/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Governance.sol#L121

File: src/policies/Operator.sol

83:       uint8 public immutable ohmDecimals;

86:       uint8 public immutable reserveDecimals;

89:       uint32 public constant FACTOR_SCALE = 1e4;

https://github.com/code-423n4/2022-08-olympus/blob/b5e139d732eb4c07102f149fb9426d356af617aa/src/policies/Operator.sol#L83

[G‑19] Don't compare boolean

... See the rest this report here

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