Lenders might not be able to exit even after the market is closed
Proof of Concept
When a borrower creates a market hooked by a fixed-term hook, all lenders are prohibited from withdrawing their assets from the market before the fixed-term time has elapsed.
The borrower can close the market at any time. However, fixedTermEndTime of the market is not updated, preventing lenders from withdrawing their assets if fixedTermEndTime has not yet elapsed.
Copy below codes to WildcatMarket.t.sol and run forge test --match-test test_closeMarket_BeforeFixedTermExpired:
function test_closeMarket_BeforeFixedTermExpired() external {
//@audit-info deploy a FixedTermLoanHooks template
address fixedTermHookTemplate = LibStoredInitCode.deployInitCode(type(FixedTermLoanHooks).creationCode);
hooksFactory.addHooksTemplate(
fixedTermHookTemplate,
'FixedTermLoanHooks',
address(0),
address(0),
0,
0
);
vm.startPrank(borrower);
//@audit-info borrower deploy a FixedTermLoanHooks hookInstance
address hooksInstance = hooksFactory.deployHooksInstance(fixedTermHookTemplate, '');
DeployMarketInputs memory parameters = DeployMarketInputs({
asset: address(asset),
namePrefix: 'name',
symbolPrefix: 'symbol',
maxTotalSupply: type(uint128).max,
annualInterestBips: 1000,
delinquencyFeeBips: 1000,
withdrawalBatchDuration: 10000,
reserveRatioBips: 10000,
delinquencyGracePeriod: 10000,
hooks: EmptyHooksConfig.setHooksAddress(address(hooksInstance))
});
//@audit-info borrower deploy a market hooked by a FixedTermLoanHooks hookInstance
address market = hooksFactory.deployMarket(
parameters,
abi.encode(block.timestamp + (365 days)),
bytes32(uint(1)),
address(0),
0
);
vm.stopPrank();
//@audit-info lenders can only withdraw their asset one year later
assertEq(FixedTermLoanHooks(hooksInstance).getHookedMarket(market).fixedTermEndTime, block.timestamp + (365 days));
//@audit-info alice deposit 50K asset into market
vm.startPrank(alice);
asset.approve(market, type(uint).max);
WildcatMarket(market).depositUpTo(50_000e18);
vm.stopPrank();
//@audit-info borrower close market in advance
vm.prank(borrower);
WildcatMarket(market).closeMarket();
//@audit-info the market is closed
assertTrue(WildcatMarket(market).isClosed());
//@audit-info however, alice can not withdraw her asset due to the unexpired fixed term.
vm.expectRevert(FixedTermLoanHooks.WithdrawBeforeTermEnd.selector);
vm.prank(alice);
WildcatMarket(market).queueFullWithdrawal();
}
Tools Used
Manual review
Recommended Mitigation Steps
When a market hooked by a fixed-term hook is closed, fixedTermEndTime should be set to block.timestamp if it has not yet elapsed:
Lines of code
https://github.com/code-423n4/2024-08-wildcat/blob/main/src/access/FixedTermLoanHooks.sol#L848-L868 https://github.com/code-423n4/2024-08-wildcat/blob/main/src/access/FixedTermLoanHooks.sol#L943-L946
Vulnerability details
Impact
Lenders might not be able to exit even after the market is closed
Proof of Concept
When a borrower creates a market hooked by a fixed-term hook, all lenders are prohibited from withdrawing their assets from the market before the fixed-term time has elapsed. The borrower can close the market at any time. However,
fixedTermEndTime
of the market is not updated, preventing lenders from withdrawing their assets iffixedTermEndTime
has not yet elapsed.Copy below codes to WildcatMarket.t.sol and run forge test --match-test test_closeMarket_BeforeFixedTermExpired:
Tools Used
Manual review
Recommended Mitigation Steps
When a market hooked by a fixed-term hook is closed,
fixedTermEndTime
should be set toblock.timestamp
if it has not yet elapsed:Assessed type
Timing