Open code423n4 opened 2 years ago
Very nice QA report imo. All the issues regarding the NotionalTradeModule are valid and actionable. Also good format and concise description of the issues and proposed mitigation / fix.
Would move these to Non-critical [L-02] Use of floating pragma [L-03] Events not emitted for important state changes [L-08] Contracts are using outdated OpenZeppelin version ^3.4.2-solc-0.7
QA Report
Codebase Impressions & Summary
Overall, the contracts are very well implemented and the code quality is very high. Protocol developers provided adequate documentation and information on the protocol.
Running the test suite is extensive and covers most of the code, however, a few things stood out:
test_wrapped_fcash.test_transfer_fcash_contract
is skippedwhales.DAI_EOA
has insufficient DAI tokens)notionalTradeModule.spec
test suite has many openTODOs
Table of Contents
isETH
return value fromwfCashBase.getToken
instead of checking equality withETH_ADDRESS
NotionalTradeModule.initialize
NotionalTradeModule._mintFCashPosition
function commentswfCashLogic._burn
functionERC1155
transfer
^3.4.2-solc-0.7
wfCashERC4626
contract does not conform toEIP4626
Non-Critical Findings
[NC-01] Use the
isETH
return value fromwfCashBase.getToken
instead of checking equality withETH_ADDRESS
Description
The
wfCashBase.getToken
returnsbool isETH
which can be used to figure out if the returned token is the native ETH token.Findings
NotionalTradeModule.sol#L400-L403
Recommended mitigation steps
Consider using the returned
isETH
variable:Low Risk
[L-01] Zero-address checks are missing
Description
Zero-address checks are a best practice for input validation of critical address parameters. While the codebase applies this to most cases, there are many places where this is missing in constructors and setters.
Impact: Accidental use of zero-addresses may result in exceptions, burn fees/tokens, or force redeployment of contracts.
Findings
NotionalTradeModule.sol
L140 -
wrappedfCashFactory = _wrappedfCashFactory;
\ L141 -weth = _weth;
wfCashBase.sol
L30 -
NotionalV2 = _notional;
\ L31 -WETH = _weth;
WrappedfCashFactory.sol
L18 -
BEACON = _beacon;
Recommended mitigation steps
Add zero-address checks, e.g.:
[L-02] Use of floating pragma
Description
Contracts should be deployed with the same compiler version and flags that they have been tested with thoroughly. Locking the pragma helps to ensure that contracts do not accidentally get deployed using, for example, an outdated compiler version that might introduce bugs that affect the contract system negatively.
https://swcregistry.io/docs/SWC-103
Findings
wfCashERC4626.sol
pragma solidity ^0.8.0;
Recommended mitigation steps
Lock the pragma version to the same version as used in the other contracts and also consider known bugs (https://github.com/ethereum/solidity/releases) for the compiler version that is chosen.
Pragma statements can be allowed to float when a contract is intended for consumption by other developers, as in the case with contracts in a library or EthPM package. Otherwise, the developer would need to manually update the pragma in order to compile it locally.
[L-03] Events not emitted for important state changes
Description
When changing state variables events are not emitted. Emitting events allows monitoring activities with off-chain monitoring tools.
Findings
NotionalTradeModule.sol#L301
function setRedeemToUnderlying(ISetToken _setToken, bool _toUnderlying)
Recommended mitigation steps
Emit events for state variable changes.
[L-04] Matured fCash positions not automatically redeemed in
NotionalTradeModule.initialize
Description
The function comments imply that matured fCash positions are redeemed within
NotionalTradeModule.initialize
. However, this redemption is not implemented in this function.Findings
NotionalTradeModule.sol#L216
Recommended mitigation steps
Consider calling the function
_redeemMaturedPositions
to redeem matured fCash positions or adapt the function comments.[L-05] Misleading
NotionalTradeModule._mintFCashPosition
function commentsDescription
The function comments imply that the given fCash position is redeemed. However, this function implements minting fCash tokens.
Findings
NotionalTradeModule.sol#L415
Recommended mitigation steps
Fix the comments to mention minting instead of redeeming.
[L-06] Misleading comment in
wfCashLogic._burn
functionDescription
The comment next to the
_withdrawCashToAccount
function call implies that thefrom
address is the withdrawal receiver. However,opts.receiver
is the receiver.Findings
wfCashLogic.sol#L230
Recommended mitigation steps
Fix the comment.
[L-07] Matured fCash can still be wrapped via
ERC1155
transfer
Description
Contrary to the key invariants stated in the README ("After maturity, wrapped fCash can no longer be minted."), matured fCash can be sent to this
wfCash
contract to receive wrapped fCash tokens in return.Findings
https://github.com/code-423n4/2022-06-notional-coop/blob/6f8c325f604e2576e2fe257b6b57892ca181509a/notional-wrapped-fcash/contracts/wfCashLogic.sol#L107-L152
Recommended mitigation steps
Consider adding a check for fCash maturity:
[L-08] Contracts are using outdated OpenZeppelin version
^3.4.2-solc-0.7
Description
Within
notional-wrapped-fcash
package.json
, an outdated OZ version is used (which has known vulnerabilities, see https://snyk.io/vuln/npm%3A%40openzeppelin%2Fcontracts).However, as
Brownie
is used to install dependencies and compile the contracts, using this outdated version declared in thepackage.json
does not impose any risks so far.Anyway, to prevent any issues in the future (e.g. using solely
hardhat
to compile and deploy the contracts), upgrade the used OZ packages within thepackage.json
to the latest versions.See similar findings:
Findings
https://github.com/code-423n4/2022-06-notional-coop/blob/main/notional-wrapped-fcash/package.json#L14
Recommended mitigation steps
Consider using the latest OZ packages within
package.json
.[L-09]
wfCashERC4626
contract does not conform toEIP4626
Description
The
wfCashERC4626
contract implements theEIP4626
standard (EIP-4626: Tokenized Vault Standard).However, according to
EIP4626
, the below-mentioned functions do not fully adhere to the specs. They possibly revert due torequire
checks or revert due to external calls reverting.Findings
L47 -
function totalAssets() public view override returns (uint256)
Possibly reverts due to
_getMaturedValue
and_getPresentValue
reverting.L52 -
function convertToShares(uint256 assets) public view override returns (uint256 shares)
Possibly reverts due to
_getPresentValue
andtotalAssets
reverting.L64 -
function convertToAssets(uint256 shares) public view override returns (uint256 assets)
Possibly reverts due to
_getPresentValue
andtotalAssets
reverting.L85 -
function maxWithdraw(address owner) public view override returns (uint256)
Possibly reverts due to
convertToShares
withinpreviewWithdraw
reverting.Recommended mitigation steps
Given the circumstances, in most of the mentioned cases, it's not possible to implement it without ever reverting. Nevertheless, I want to point out that this contract does not fully conform with the
EIP4626
standard.