Lucene search

K
code423n4Code4renaCODE423N4:2022-06-NOTIONAL-COOP-FINDINGS-ISSUES-229
HistoryJun 14, 2022 - 12:00 a.m.

Wrapped idiosyncratic (non-tradable) fCash can possibly not be unwrapped prior to maturity

2022-06-1400:00:00
Code4rena
github.com
4

Lines of code

Vulnerability details

What is idiosyncratic fCash?

> Markets may not always trade at the exact maturities of all fCash assets. fCash that does not fall on an exact maturity is called idiosyncratic fCash. To value these assets, Notional takes the linear interpolation of the rates of the two nearest markets. For example, an fCash asset at 9 months to maturity will be valued at a rate halfway between the current 6 month and 1 year markets. Idiosyncratic fCash cannot exist beyond the furthest on chain market by design.

Cited from here.

Impact

To unwrap idiosyncratic wfCash tokens (non-tradable wrapped fCash), a smart contract (which does not implement IERC1155TokenReceiver) has to wait for the wfCash tokens to reach maturity.

One of the consequences of being β€œstuck” with wrapped fCash tokens is that wrapped fCash can not be used as collateral for borrowing on Notional. Hence, not being able to unwrap fCash restricts the holder.

Proof of Concept

Consider a given contract (which does not implement IERC1155TokenReceiver) and the following scenario:

  1. The contract owns idiosyncratic fCash tokens (not matured)

  2. Contract transfers those fCash tokens to wfCash contract, wfCashLogic.onERC1155Received receive hook is called and β€œwraps” received fCash tokens (minting)

  3. Sender contract receives wrapped fCash tokens

  4. Now the contract tries to unwrap fCash tokens again (prior to maturity) by using any of the following functions:

* wfCashLogic.redeem
* wfCashLogic.redeemToAsset
* wfCashLogic.redeemToUnderlying
* wfCashERC4626.withdraw
* wfCashERC4626.redeem
  1. All above-mentioned functions call wfCashLogic._burn internally. However, due to the following 2 possible issues, the function reverts:

Issue #1

If the wfCashLogic._burn function is called with opts.transferfCash = false (false is used by default), the first if branch is skipped (fCash is not matured) and the 3rd branch is executed. Due to _sellfCash not being able to trade idiosyncratic fCash, this will revert.

wfCashLogic.sol#L249-L256

} else {
    _sellfCash(
        opts.receiver,
        amount,
        opts.redeemToUnderlying,
        opts.maxImpliedRate
    ); // @audit-info reverts because the idiosyncratic wrapped fCash token has a market index of 0 which prevents trading (market index 0 reverts within Notional CashGroup.loadMarket function)
}

Notional - CashGroup.loadMarket

require(1 <= marketIndex && marketIndex <= cashGroup.maxMarketIndex, "Invalid market");

Issue #2

If the wfCashLogic._burn function is called with opts.transferfCash = true and opts.receiver set to the contract address (by using wfCashLogic.redeem), the first if branch is skipped (fCash is not matured) and the 2nd branch is executed. As the contract does not implement IERC1155TokenReceiver and is missing the onERC1155Received hook, this will revert.

Notional - ERC1155Action.sol#L182-L188

if (Address.isContract(to)) {
      require(
          IERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) ==
              ERC1155_ACCEPTED,
          "Not accepted"
      );
  }

Tools Used

Manual review

Recommended mitigation steps

Consider preventing wrapped idiosyncratic fCash tokens in wfCashBase.sol#L39 by checking marketIndex and isIdiosyncratic in wfCashBase.initialize while initializing a fCash wrapper contract.


The text was updated successfully, but these errors were encountered:

All reactions