Markets entered automatically can be exited
Description
In the Comptroller contract, enterAllMarkets
is a newly added function that enters all of the nondeprecated markets upon request from a TToken contract:
+function enterAllMarkets(address account) public override returns (uint256[] memory) {
+ address[] memory assetsAddresses = new address[](allMarkets.length);
+
+ bool isRTokenContract;
+
+ for (uint256 i = 0; i < allMarkets.length; i++) {
+ assetsAddresses[i] = address(allMarkets[i]);
+
+ if (assetsAddresses[i] == msg.sender) {
+ isRTokenContract = true;
+ }
+ }
+
+ require(isRTokenContract, "Sender must be a TToken contract");
+
+ uint256 len = assetsAddresses.length;
+
+ uint256[] memory results = new uint256[](len);
+
+ for (uint256 i = 0; i < len; i++) {
+ // Prevent paused asset enter market
+ if (borrowGuardianPaused[assetsAddresses[i]]) continue;
+
+ TToken tToken = TToken(assetsAddresses[i]);
+ results[i] = uint256(addToMarketInternal(tToken, account));
+ }
+
+ return results;
+}
This function is called in TToken whenever someone mints assets:
function mintFresh(address minter, uint256 mintAmount) internal returns (uint256, uint256) {
// [...]
+ comptroller.enterAllMarkets(minter);
return (uint256(Error.NO_ERROR), vars.actualMintAmount);
}
However, although this additional call makes depositors enter all the markets, the exitMarket
function was not changed at all, and users can still exit markets for which they do not have any borrow balance.
Impact
Users may bundle transactions or operate through a smart wallet in order to immediately exit all the markets they do not wish to stay entered in, after minting assets. This reduces this contract change from a restriction to a convenience, because the action can be undone transactionally.
Additionally, this implementation favors sophisticated users, because regular users will either not realize that they can exit markets they do not wish to stay entered in or exit in a separate manually submitted public-mempool transaction, which risks a liquidation occuring before the exit is confirmed.
Recommendations
If remaining in all nondeprecated markets is mandatory, then we recommend disabling exitMarket
for the markets that enterAllMarkets
enters.
Alternatively, if this is a single-transaction convenience, then we recommend adding a parameter to the mint function that allows users to opt out of this automatic entry, so that unsophisticated users have the same easy ability to opt out of this convenience as sophisticated ones. Another way to implement this option, if the function interface for minting should not be changed from the upstream implementation, is to have an account-specific setting that users can arbitrarily change by calling a function on the Comptroller contract.
Remediation
This issue has been acknowledged by Takara Lend, and a fix was implemented in commit 7ee9b938↗. The enterAllMarkets
function has been removed.