Closed code423n4 closed 2 years ago
The fCash token should be added as a component via the _updateSetTokenPositions method in _mintFCashPosition automatically. So the manager does not have to do this manually. (which is not possible anyway since the addComponent
method is callable via authorized set protocol modules anyway.
I don't see any reasoning / evidence here that points to this mechanism not working.
Lines of code
https://github.com/code-423n4/2022-06-notional-coop/blob/6f8c325f604e2576e2fe257b6b57892ca181509a/index-coop-notional-trade-module/contracts/protocol/modules/v1/NotionalTradeModule.sol#L156
Vulnerability details
Proof-of-Concept
Assume that the manager decided to add a fCash position called "Wrapped fDAI @ 10 October 2022", which will mature at 10 October 2022, to the SetToken.
To do so, the manager will call the
NotionalTradeModule.mintFCashPosition
function. The manager set the_sendToken
to the asset token, which iscDAI
in this example. Within the function, it will check if the_sendToken
is a component in the SetToken. If not, the function will revert with the "Send token must be an index component" error. At this stage, it is clear-cut to the manager that the_sendToken
, which iscDAI
in this example, has to be added to the components of the SetToken. Otherwise, the manager cannot proceed with the minting process.https://github.com/code-423n4/2022-06-notional-coop/blob/6f8c325f604e2576e2fe257b6b57892ca181509a/index-coop-notional-trade-module/contracts/protocol/modules/v1/NotionalTradeModule.sol#L156
Assume the manager called the
NotionalTradeModule.mintFCashPosition
and the transaction has completed. At this point, the SetToken holds 100 "Wrapped fDAI @ 10 October 2022" tokens.In this scenario, let assume that manager did not call the
setToken.addComponent("Wrapped fDAI @ 10 October 2022")
function to add the newly added fCash position to the components of the SetToken before calling theNotionalTradeModule.mintFCashPosition
function (minting process).Note that when the
`NotionalTradeModule.mintFCashPosition
function is called, it only check whether the_sendToken
is a component of the SetToken. However, it did not check whether the fCash ("Wrapped fDAI @ 10 October 2022") is a component of the SetToken before minting.Thus, it is possible to reach a state where the fCash ("Wrapped fDAI @ 10 October 2022") is minted to the SetToken, but the fCash ("Wrapped fDAI @ 10 October 2022") is not a component of this SetToken.
Impact
If this happens, it will cause a number of issues as described below:
_redeemMaturedPositions
function works is that it will loop through all the components of the SetToken and attempt to redeem them if a fCash has matured. If a fCash address is not added to the components, this means that this particular matured fCash will never be redeemed._redeemMaturedPositions
is called in many occasions (e.g. Within SetToken issuance and redemption hooks ) and failure of this function can lead to internal accounting issue.Recommended Mitigation Steps
To eliminate such an event from happening, it is recommended to implement additional validation check to ensure that the system will never reach a state where a fCash has been minted to a SetToken, but this fCash is not a component of the SetToken.
Without this control in place, it is likely that this issue will happen as no one can ensure that all managers that utilise the
NotionalTradeModule
module will know that they are required to add the fCash to components of the SetToken before callingNotionalTradeModule.mintFCashPosition
function.Additionally, human-error might happen and they might forget to perform this step. Thus, it is essential for the protocol to implement the necessary controls to prevent their manager from falling into this pitfall.
Additional
require
statement can be added as shown below: