[L-01] IT IS POSSIBLE THAT beat FUNCTION IS NOT CALLED FOR ENTIRE TIME DURING A FREQUENCY
It is possible that the following beat function is not called by anybody for the entire time during a frequency. In this case, PRICE.updateMovingAverage() is not executed for that frequency. The price information for that frequency is not recorded, and the moving average becomes out-of-date as it is not updated with that frequency's price. Later, after someone calls beat again during a new frequency, the price information for the skipped frequency is still missing, and the duration between the current and earliest observations will be larger than specified. Because of this, the moving average deviates from the time-weighted average price to be more like an observation-weighted average price, which is also not as specified. To avoid these bookkeeping discrepancies, it can be beneficial to set up a bot to call beat for once during each frequency just in case nobody calls it during a frequency.
function beat() external nonReentrant {
if (!active) revert Heart_BeatStopped();
if (block.timestamp < lastBeat + frequency()) revert Heart_OutOfCycle();
// Update the moving average on the Price module
PRICE.updateMovingAverage();
// Trigger price range update and market operations
_operator.operate();
// Update the last beat timestamp
lastBeat += frequency();
// Issue reward to sender
_issueReward(msg.sender);
emit Beat(block.timestamp);
}
[L-02] UNRESOLVED TODO COMMENTS
Comment regarding todo indicates that there is an unresolved action item for implementation, which need to be addressed before protocol deployment. Please review the todo comments in the following code.
/// 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?
// 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 {
if (Policy(policy_).isActive()) revert PolicyStillActive();
// TODO Make sure `policy_` is an actual policy and not a random address.
uint256 len = tokens_.length;
for (uint256 j; j < len; ) {
TRSRY.setApprovalFor(policy_, tokens_[j], 0);
unchecked {
++j;
}
}
emit ApprovalRevoked(policy_, tokens_);
}
[L-03] MISSING ZERO-ADDRESS CHECK FOR CRITICAL ADDRESSES
To prevent unintended behaviors, the critical address inputs should be checked against address(0).
[N-01] ESUBMISSION_REQUIREMENT IS USED TO COMPARE AGAINST 10000 BUT ENDORSEMENT_THRESHOLD AND EXECUTION_THRESHOLD ARE USED TO COMPARE AGAINST 100
ESUBMISSION_REQUIREMENT, ENDORSEMENT_THRESHOLD, and EXECUTION_THRESHOLD in the following code are all used to represent percents. However, ESUBMISSION_REQUIREMENT is used to compare against 10000 while ENDORSEMENT_THRESHOLD and EXECUTION_THRESHOLD are used to compare against 100. This inconsistency can cause confusions and typos in the future. Please consider unifying these constants so they can be used to compare against the same number.
/// @notice The amount of votes a proposer needs in order to submit a proposal as a percentage of total supply (in basis points).
/// @dev This is set to 1% of the total supply.
uint256 public constant SUBMISSION_REQUIREMENT = 100;
...
/// @notice Endorsements required to activate a proposal as percentage of total supply.
uint256 public constant ENDORSEMENT_THRESHOLD = 20;
/// @notice Net votes required to execute a proposal on chain as a percentage of total supply.
uint256 public constant EXECUTION_THRESHOLD = 33;
[N-02] Unreachable code
return true; is unreachable in the following code. It can be removed for better readability and maintainability.
function transfer(address to_, uint256 amount_) public pure override returns (bool) {
revert VOTES_TransferDisabled();
return true;
}
[N-03] decimals CAN BE NAMED USING CAPITAL LETTERS AND UNDERSCORES
Because the following decimals is a constant, it can be named using capital letters and underscores by convention, which improves readability and maintainability.
[N-04] CONSTANTS CAN BE USED INSTEAD OF MAGIC NUMBERS
To improve readability and maintainability, constants can be used instead of the magic numbers. Please consider replacing the magic numbers used in the following code with constants.
NatSpec comments provide rich code documentation. @param or @return comments are missing for the following functions. Please consider completing NatSpec comments for them.
Kernel.sol
235: function executeAction(Actions action_, address target_) external onlyExec
351: function _migrateKernel(Kernel newKernel_) internal
439: function grantRole(Role role_, address addr_) public onlyAdmin {
451: function revokeRole(Role role_, address addr_) public onlyAdmin {
modules\TRSRY.sol
64: function setApprovalFor(
75: function withdrawReserves(
92: function getLoan(ERC20 token_, uint256 amount_) external permissioned {
105: function repayLoan(ERC20 token_, uint256 amount_) external nonReentrant {
122: function setDebt(
modules\VOTES.sol
45: function transfer(address to_, uint256 amount_) public pure override returns (bool) {
51: function transferFrom(
[N-06] MISSING NATSPEC COMMENTS
NatSpec comments provide rich code documentation. NatSpec comments are missing for the following functions. Please consider adding them.
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 {
378: function _reconfigurePolicies(Keycode keycode_) internal {
391: function _setPolicyPermissions(
409: function _pruneFromDependents(Policy policy_) internal {
modules\MINTR.sol
33: function mintOhm(address to_, uint256 amount_) public permissioned {
37: function burnOhm(address from_, uint256 amount_) public permissioned {
modules\TRSRY.sol
47: function KEYCODE() public pure override returns (Keycode) {
51: function VERSION() external pure override returns (uint8 major, uint8 minor) {
59: function getReserveBalance(ERC20 token_) external view returns (uint256) {
137: function _checkApproval(
modules\VOTES.sol
35: function mintTo(address wallet_, uint256 amount_) external permissioned {
39: function burnFrom(address wallet_, uint256 amount_) external permissioned {
utils\KernelUtils.sol
11: function toKeycode(bytes5 keycode_) pure returns (Keycode) {
16: function fromKeycode(Keycode keycode_) pure returns (bytes5) {
21: function toRole(bytes32 role_) pure returns (Role) {
26: function fromRole(Role role_) pure returns (bytes32) {
31: function ensureContract(address target_) view {
40: function ensureValidKeycode(Keycode keycode_) pure {
55: function ensureValidRole(Role role_) pure {
Agreed on all except 0x0 address check as that adds unnecessary code when the system is heavily permissioned and we can control for that on our own outside of code.
[L-01] IT IS POSSIBLE THAT beat FUNCTION IS NOT CALLED FOR ENTIRE TIME DURING A FREQUENCY
It is possible that the following
beat
function is not called by anybody for the entire time during a frequency. In this case,PRICE.updateMovingAverage()
is not executed for that frequency. The price information for that frequency is not recorded, and the moving average becomes out-of-date as it is not updated with that frequency's price. Later, after someone callsbeat
again during a new frequency, the price information for the skipped frequency is still missing, and the duration between the current and earliest observations will be larger than specified. Because of this, the moving average deviates from the time-weighted average price to be more like an observation-weighted average price, which is also not as specified. To avoid these bookkeeping discrepancies, it can be beneficial to set up a bot to callbeat
for once during each frequency just in case nobody calls it during a frequency.https://github.com/code-423n4/2022-08-olympus/blob/main/src/policies/Heart.sol#L92-L109
[L-02] UNRESOLVED TODO COMMENTS
Comment regarding todo indicates that there is an unresolved action item for implementation, which need to be addressed before protocol deployment. Please review the todo comments in the following code.
https://github.com/code-423n4/2022-08-olympus/blob/main/src/policies/Operator.sol#L657
https://github.com/code-423n4/2022-08-olympus/blob/main/src/policies/TreasuryCustodian.sol#L51-L67
[L-03] MISSING ZERO-ADDRESS CHECK FOR CRITICAL ADDRESSES
To prevent unintended behaviors, the critical address inputs should be checked against
address(0)
.Please consider checking
ohm_
in the following constructor. https://github.com/code-423n4/2022-08-olympus/blob/main/src/modules/MINTR.sol#L15-L17Please consider checking the addresses of
ohmEthPriceFeed_
andreserveEthPriceFeed_
in the following constructor. https://github.com/code-423n4/2022-08-olympus/blob/main/src/modules/PRICE.sol#L71-L77Please consider checking the addresses in
tokens_
in the following constructor. https://github.com/code-423n4/2022-08-olympus/blob/main/src/modules/RANGE.sol#L77-L81Please consider checking the addresses of
aggregator_
andohm_
in the following constructor. https://github.com/code-423n4/2022-08-olympus/blob/main/src/policies/BondCallback.sol#L38-L45Please consider checking the addresses in
tokens_
in the following constructor. https://github.com/code-423n4/2022-08-olympus/blob/main/src/policies/Operator.sol#L92-L98Please consider checking the address of
rewardToken_
in the following constructor. https://github.com/code-423n4/2022-08-olympus/blob/main/src/policies/Heart.sol#L54-L59[N-01] ESUBMISSION_REQUIREMENT IS USED TO COMPARE AGAINST 10000 BUT ENDORSEMENT_THRESHOLD AND EXECUTION_THRESHOLD ARE USED TO COMPARE AGAINST 100
ESUBMISSION_REQUIREMENT
,ENDORSEMENT_THRESHOLD
, andEXECUTION_THRESHOLD
in the following code are all used to represent percents. However,ESUBMISSION_REQUIREMENT
is used to compare against 10000 whileENDORSEMENT_THRESHOLD
andEXECUTION_THRESHOLD
are used to compare against 100. This inconsistency can cause confusions and typos in the future. Please consider unifying these constants so they can be used to compare against the same number.https://github.com/code-423n4/2022-08-olympus/blob/main/src/policies/Governance.sol#L119-L133
[N-02] Unreachable code
return true;
is unreachable in the following code. It can be removed for better readability and maintainability.https://github.com/code-423n4/2022-08-olympus/blob/main/src/modules/VOTES.sol#L45-L48
[N-03] decimals CAN BE NAMED USING CAPITAL LETTERS AND UNDERSCORES
Because the following
decimals
is a constant, it can be named using capital letters and underscores by convention, which improves readability and maintainability.https://github.com/code-423n4/2022-08-olympus/blob/main/src/modules/PRICE.sol#L59
[N-04] CONSTANTS CAN BE USED INSTEAD OF MAGIC NUMBERS
To improve readability and maintainability, constants can be used instead of the magic numbers. Please consider replacing the magic numbers used in the following code with constants.
[N-05] INCOMPLETE NATSPEC COMMENTS
NatSpec comments provide rich code documentation. @param or @return comments are missing for the following functions. Please consider completing NatSpec comments for them.
[N-06] MISSING NATSPEC COMMENTS
NatSpec comments provide rich code documentation. NatSpec comments are missing for the following functions. Please consider adding them.