Light ModeLight
Light ModeDark

One Bug Per Day

One H/M every day from top Wardens

Checkmark

Join over 1130 wardens!

Checkmark

Receive the email at any hour!

Ad

Fragmentation fee is not taken if user compensates with newly created position

mediumCode4rena

Lines of code

https://github.com/code-423n4/2024-06-size/blob/main/src/libraries/actions/Compensate.sol#L119-L125 https://github.com/code-423n4/2024-06-size/blob/main/src/libraries/actions/Compensate.sol#L136

Vulnerability details

Impact

User can split his debt credit using the compensate function without paying the fragmentation fee.

Proof of Concept

The fragmentation fee normally applies when the initial credit amount is split, creating a new credit position with the fractionalized amount. However, under specific conditions when the compensate function is invoked, this fee is not charged.

Consider the following scenario, the user compensates half of his debt (amountToCompensate = creditPositionWithDebtToRepay.credit / 2) with a new credit position (params.creditPositionToCompensateId == RESERVED_ID) to the lender:

solidity
function executeCompensate(State storage state, CompensateParams calldata params) external { emit Events.Compensate( params.creditPositionWithDebtToRepayId, params.creditPositionToCompensateId, params.amount ); CreditPosition storage creditPositionWithDebtToRepay = state.getCreditPosition(params.creditPositionWithDebtToRepayId); DebtPosition storage debtPositionToRepay = state.getDebtPositionByCreditPositionId(params.creditPositionWithDebtToRepayId); >> uint256 amountToCompensate = Math.min(params.amount, creditPositionWithDebtToRepay.credit); CreditPosition memory creditPositionToCompensate; if (params.creditPositionToCompensateId == RESERVED_ID) { >> creditPositionToCompensate = state.createDebtAndCreditPositions({ lender: msg.sender, borrower: msg.sender, futureValue: amountToCompensate, dueDate: debtPositionToRepay.dueDate }); ---SNIP---

In the above code snippet, a new credit position is created with credit = futureValue = amountToCompensate. This results in creditPositionToCompensate.credit always equating to amountToCompensate, leading exiterCreditRemaining to consistently be zero:

solidity
---SNIP--- >> uint256 exiterCreditRemaining = creditPositionToCompensate.credit - amountToCompensate; // credit emission state.createCreditPosition({ exitCreditPositionId: params.creditPositionToCompensateId == RESERVED_ID ? state.data.nextCreditPositionId - 1 : params.creditPositionToCompensateId, lender: creditPositionWithDebtToRepay.lender, credit: amountToCompensate }); >> if (exiterCreditRemaining > 0) { // charge the fragmentation fee in collateral tokens, capped by the user balance uint256 fragmentationFeeInCollateral = Math.min( state.debtTokenAmountToCollateralTokenAmount(state.feeConfig.fragmentationFee), state.data.collateralToken.balanceOf(msg.sender) ); state.data.collateralToken.transferFrom( msg.sender, state.feeConfig.feeRecipient, fragmentationFeeInCollateral );

As a result, despite splitting creditPositionWithDebtToRepay.credit in half and creating a new credit position, the fragmentation fee is not deducted as expected.

Tools Used

Manual review

Recommended Mitigation Steps

diff
+ uint256 initialCredit = params.creditPositionToCompensateId == RESERVED_ID ? creditPositionWithDebtToRepay.credit : creditPositionToCompensate.credit; + uint256 exiterCreditRemaining = initialCredit - amountToCompensate;

Assessed type

Other