Overlay Protocol contest - pauliax's results

A protocol for trading #DeFi data streams.

General Information

Platform: Code4rena

Start Date: 16/11/2021

Pot Size: $50,000 ETH

Total HM: 11

Participants: 17

Period: 7 days

Judge: LSDan

Total Solo HM: 8

Id: 49

League: ETH

Overlay Protocol

Findings Distribution

Researcher Performance

Rank: 2/17

Findings: 6

Award: $8,098.70

🌟 Selected for report: 10

πŸš€ Solo Findings: 2

Findings Information

🌟 Selected for report: pauliax

Also found by: defsec, hubble

Labels

bug
3 (High Risk)
sponsor confirmed

Awards

0.4476 ETH - $2,068.07

External Links

Handle

pauliax

Vulnerability details

Impact

Overlay uses OZ contracts version 4.3.2:

  dependencies:
    - OpenZeppelin/openzeppelin-contracts@4.3.2

and has a contract that inherits from ERC1155Supply:

  contract OverlayV1OVLCollateral is ERC1155Supply

This version has a recently discovered vulnerability: https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-wmpv-c2jp-j2xg

In your case, function unwind relies on totalSupply when calculating _userNotional, _userDebt, _userCost, and _userOi, so a malicious actor can exploit this vulnerability by first calling 'build' and then on callback 'unwind' in the same transaction before the total supply is updated.

Consider updating to a patched version of 4.3.3.

Findings Information

🌟 Selected for report: pauliax

Labels

bug
2 (Med Risk)

Awards

0.4973 ETH - $2,297.86

External Links

Handle

pauliax

Vulnerability details

Impact

There are contracts that contain functions that change important parameters of the system, e.g. OverlayV1Mothership has setOVL, initializeMarket, disableMarket, enableMarket, initializeCollateral, enableCollateral, disableCollateral, adjustGlobalParams. None of these functions emit events, nor they are timelocked. Usually, it is a good practice to give time for users to react and adjust to changes.

A similar issue was submitted in a previous contest and assigned a severity of Medium: https://github.com/code-423n4/2021-09-swivel-findings/issues/101

Consider using a timelock for critical params of the system and emitting events to inform the outside world.

#0 - commercium-sys

2021-11-24T17:45:38Z

The plan has been to have a timelock at some point in the protocol. Probably on whatever is the admin for the mothership. But this just had to be evaluated. It might be on the market contract itself, or on the addresses granted the role of admin.

#1 - mikeyrf

2021-12-07T14:31:00Z

duplicate #64

#2 - dmvt

2021-12-18T13:45:56Z

I'm removing the duplicate in this case because issue #64 refers exclusively to the events. This issue is focused primarily on the lack of governance timelock, which has traditionally been considered a medium severity issue.

Findings Information

🌟 Selected for report: pauliax

Labels

bug
2 (Med Risk)
disagree with severity
sponsor confirmed

Awards

0.4973 ETH - $2,297.86

External Links

Handle

pauliax

Vulnerability details

Impact

contract OverlayV1OVLCollateral and OverlayV1Governance cache ovl address:

 IOverlayTokenNew immutable public ovl;

This variable is initialized in the constructor and fetched from the mothership contract:

  mothership = IOverlayV1Mothership(_mothership);
  ovl = IOverlayV1Mothership(_mothership).ovl();

ovl is declared as immutable and later contract interacts with this cached version. However, mothership contains a setter function, so the governor can point it to a new address:

function setOVL (address _ovl) external onlyGovernor {
    ovl = _ovl;
}

OverlayV1OVLCollateral and OverlayV1Governance will still use this old cached value.

Consider if this was intended, or you want to remove this cached version and always fetch on the go (this will increase the gas costs though).

#0 - commercium-sys

2021-11-24T17:30:13Z

This is just a detail we were yet to settle on but definitely were going to as we got the contracts to a totally deployable state.

#1 - mikeyrf

2021-12-08T22:48:56Z

disagree w severity reason - would put this at 1 - Low Risk given the governor would be responsible for properly setting

#2 - dmvt

2021-12-20T19:40:36Z

I agree with the warden that this constitutes a medium risk.

From the judging criteria (emphasis mine):

2 β€” Med (M): vulns have a risk of 2 and are considered β€œMedium” severity when assets are not at direct risk, but the function of the protocol or its availability could be impacted, or leak value with a hypothetical attack path with stated assumptions, but external requirements.

Findings Information

🌟 Selected for report: defsec

Also found by: WatchPug, cmichel, gzeon, nathaniel, pauliax

Labels

bug
duplicate
2 (Med Risk)

Awards

0.0489 ETH - $226.14

External Links

Handle

pauliax

Vulnerability details

Impact

OverlayV1Mothership has declared variables named MIN_FEE and MAX_FEE, or MIN_MARGIN_MAINTENANCE and MAX_MARGIN_MAINTENANCE, however, none of these variables are used anywhere, e.g. I expected to see a fee validated with these min/max boundaries, but now governance can set any arbitrary value.

Consider either using these variables where they were intended or removing them to reduce the confusion.

#0 - commercium-sys

2021-11-24T17:48:26Z

I wouldn't call this much of a bug, quite possibly something we would have included. At any rate, it is on us to get the parameterization of the contracts correct and we know this.

#1 - mikeyrf

2021-12-06T23:37:29Z

duplicate #77 - bounds on governance params

Findings Information

🌟 Selected for report: pauliax

Also found by: Meta0xNull, pants, ye0lde

Labels

bug
1 (Low Risk)
sponsor confirmed

Awards

0.0302 ETH - $139.60

External Links

Handle

pauliax

Vulnerability details

Impact

There are TODOs left in the code. While this does not cause any direct issue, it indicates a bad smell and uncertainty. In previous reports, such submissions were assigned a score of 'low' so I think it's a fair game to submit this as an issue here also. Reference: https://github.com/code-423n4/2021-09-swivel-findings/issues/67 https://github.com/code-423n4/2021-10-tempus-findings/issues/39

Also, there are some misleading comments, e.g.:

    /// @notice Internal update function to price, cap, and pay funding.
    function update () public virtual returns (

the comment says that function is internal but it is actually declared as public.

Consider implementing or removing TODOs and updating misleading comments.

#0 - commercium-sys

2021-11-24T17:49:09Z

I wouldn't call these things a bug, they have just been waiting for house cleaning when we get the contracts to a final, really going to deploy state.

Findings Information

🌟 Selected for report: pauliax

Labels

bug
1 (Low Risk)
sponsor acknowledged

Awards

0.1658 ETH - $765.95

External Links

Handle

pauliax

Vulnerability details

Impact

IOverlayV1Market declares a function adjustParams but OverlayV1Market does not have such a function. There may be more examples where interface and contract differ.

Consider explicitly inheriting an interface to enforce compile-time check:

 contract OverlayV1Market is IOverlayV1Market

#0 - commercium-sys

2021-12-08T22:37:32Z

We hadn't reached the point where we were going to make sure a number of things about the contract, including absolute adherence between the interfaces and the contracts. So we acknowledge.

Findings Information

🌟 Selected for report: pauliax

Also found by: WatchPug, defsec, gzeon

Labels

bug
G (Gas Optimization)
sponsor acknowledged

Awards

0.0029 ETH - $13.54

External Links

Handle

pauliax

Vulnerability details

Impact

Unused state variables or functions can be removed to save some deployment gas. Examples of such code are:

  uint16 public constant MIN_FEE = 1; // 0.01%
  uint16 public constant MAX_FEE = 100; // 1.00%
  uint16 public constant MIN_MARGIN_MAINTENANCE = 100; // 1% maintenance
  uint16 public constant MAX_MARGIN_MAINTENANCE = 6000; // 60% maintenance
  bytes32 public constant MINTER = keccak256("MINTER");
  bytes32 public constant BURNER = keccak256("BURNER");
  modifier onlyGuardian
  event log(string k, uint v);
  event log_addr(string k, address v);
  uint256 public leverageMax;

#0 - commercium-sys

2021-11-24T17:43:47Z

Yeah this is just languishing there until we get to the point of finalizing the contracts. Not much of a bug in my opinion though.

Findings Information

🌟 Selected for report: pauliax

Labels

bug
G (Gas Optimization)
sponsor disputed

Awards

0.0161 ETH - $74.28

External Links

Handle

pauliax

Vulnerability details

Impact

There are places where the same storage variable is accessed multiple times in the same function. It would be more gas efficient to cache these variables and re-use them where necessary. E.g. functions initializeCollateral, enableCollateral, disableCollateral access ovl 4 times, function fetchPricePoint accesses macroWindow 4 times and microWindow 3 times. There are many more places where repeated storage access can be cached.

#0 - commercium-sys

2021-12-08T22:40:27Z

We are not very interested in saving gas on governance functions that will only be used once in a blue moon. Furthermore, micro and macro window are immutables - so there is no read cost for them.

#1 - dmvt

2021-12-21T14:10:26Z

Whether you want to save gas here or not, the report is valid.

Findings Information

🌟 Selected for report: pauliax

Labels

bug
G (Gas Optimization)
sponsor acknowledged

Awards

0.0161 ETH - $74.28

External Links

Handle

pauliax

Vulnerability details

Impact

Before:

  require(currentAllowance >= amount + burnt, "OVL:allowance<amount+burnt");
  unchecked { _approve(sender, msg.sender, currentAllowance - amount - burnt); }

After:

  uint totalAmount = amount + burnt;
  require(currentAllowance >= totalAmount, "OVL:allowance<amount+burnt");
  unchecked { _approve(sender, msg.sender, currentAllowance - totalAmount); }

This approach can be applied in several functions, e.g. transferFromBurn, _transferBurn.

#0 - commercium-sys

2021-11-24T17:42:17Z

Super tiny savings here.

Findings Information

🌟 Selected for report: pauliax

Labels

bug
G (Gas Optimization)
sponsor acknowledged

Awards

0.0161 ETH - $74.28

External Links

Handle

pauliax

Vulnerability details

Impact

Can eliminate the subtraction here by substituting the order of operations:

  positions.push(Position.Info({
      market: _market,
      isLong: _isLong,
      leverage: _leverage,
      pricePoint: _pricePointNext,
      oiShares: 0,
      debt: 0,
      cost: 0
  }));

  positionId_ = positions.length - 1;

After:

  positionId_ = positions.length;
  positions.push...

#0 - commercium-sys

2021-11-24T17:41:42Z

Super tiny gas savings there - maybe 20 gas? Less?

Findings Information

🌟 Selected for report: pauliax

Also found by: gzeon

Labels

bug
G (Gas Optimization)
sponsor acknowledged

Awards

0.0072 ETH - $33.42

External Links

Handle

pauliax

Vulnerability details

Impact

There are some structs that could be optimized for storage space. Struct variables are stored in 32 bytes each so you can group smaller types to occupy less storage, e.g. I don't think that leverage or pricePoint need to occupy uint256.

Consider using smaller types for values that are not expected to reach their limits. You can read more here: https://fravoll.github.io/solidity-patterns/tight_variable_packing.html or in the official documentation: https://docs.soliditylang.org/en/v0.4.21/miscellaneous.html

#0 - commercium-sys

2021-11-24T17:41:16Z

Yeah, this is something I have been intending on doing.

It would have been great to have seen a concrete idea on how to do this best.

I am currently considering using a bytes32 array to see how much we can pack in there, independently of the types provided by Solidity.

Findings Information

🌟 Selected for report: defsec

Also found by: pauliax

Labels

bug
duplicate
G (Gas Optimization)

Awards

0.0072 ETH - $33.42

External Links

Handle

pauliax

Vulnerability details

Impact

Assigned operations to constant variables are re-evaluated every time. See https://github.com/ethereum/solidity/issues/9232

  bytes32 public constant GOVERNOR = keccak256("GOVERNOR");
  bytes32 public constant GUARDIAN = keccak256("GUARDIAN");
  bytes32 public constant MINTER = keccak256("MINTER");
  bytes32 public constant BURNER = keccak256("BURNER");

Change 'constant' to 'immutable'.

#0 - commercium-sys

2021-11-24T17:43:56Z

Thanks, I did not know this.

#1 - mikeyrf

2021-12-08T22:38:56Z

duplicate #111

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