Skip to content

Commit

Permalink
c4-016 Documentation fixes (#135)
Browse files Browse the repository at this point in the history
* c4-016 Documentation fixes

* Minor documentation/error fixes

* Improve docs for swap fee

* c4-009 Variable Pool liquidity check can be bypassed with a sandwitch attack (#128)
  • Loading branch information
aviggiano committed Jul 15, 2024
1 parent 49ef874 commit 9eb0670
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 9 deletions.
9 changes: 4 additions & 5 deletions src/interfaces/ISize.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ interface ISize is ISizeView, ISizeAdmin, IMulticall {
/// @notice Repay a debt position by transferring the amount due of borrow tokens to the protocol, which are deposited to the Variable Pool for the lenders to claim
/// Partial repayment are currently unsupported
/// @dev The Variable Pool liquidity index is snapshotted at the time of the repayment in order to calculate the accrued interest for lenders to claim
/// The liquidator overdue reward is cleared from the borrower debt upon repayment
/// @param params RepayParams struct containing the following fields:
/// - uint256 debtPositionId: The id of the debt position to repay
function repay(RepayParams calldata params) external payable;
Expand All @@ -122,7 +121,7 @@ interface ISize is ISizeView, ISizeAdmin, IMulticall {
/// @param params LiquidateParams struct containing the following fields:
/// - uint256 debtPositionId: The id of the debt position to liquidate
/// - uint256 minimumCollateralProfit: The minimum collateral profit that the liquidator is willing to accept from the borrower (keepers might choose to pass a value below 100% of the cash they bring and take the risk of liquidating unprofitably)
/// @return liquidatorProfitCollateralToken The amount of collateral tokens the liquidator received from the liquidation
/// @return liquidatorProfitCollateralToken The amount of collateral tokens the the fee recipient received from the liquidation
function liquidate(LiquidateParams calldata params)
external
payable
Expand All @@ -137,7 +136,7 @@ interface ISize is ISizeView, ISizeAdmin, IMulticall {

/// @notice Liquidate a debt position with a replacement borrower
/// @dev This function works exactly like `liquidate`, with an added logic of replacing the borrower on the storage
/// When liquidating with replacement, nothing changes from the lender's perspective, but a spread is created between the previous borrower rate and the new borrower rate.
/// When liquidating with replacement, nothing changes from the lenders' perspective, but a spread is created between the previous borrower rate and the new borrower rate.
/// As a result of the spread of these borrow aprs, the protocol is able to profit from the liquidation. Since the choice of the borrower impacts on the protocol's profit, this method is permissioned
/// @param params LiquidateWithReplacementParams struct containing the following fields:
/// - uint256 debtPositionId: The id of the debt position to liquidate
Expand All @@ -153,10 +152,10 @@ interface ISize is ISizeView, ISizeAdmin, IMulticall {
returns (uint256 liquidatorProfitCollateralToken, uint256 liquidatorProfitBorrowToken);

/// @notice Compensate a borrower's debt with his credit in another loan
/// The compensation can not exceed both 1) the credit the lender of `debtPositionToRepayId` to the borrower and 2) the credit the lender of `creditPositionToCompensateId`
/// The compensation can not exceed both 1) the credit the lender of `creditPositionWithDebtToRepayId` to the borrower and 2) the credit the lender of `creditPositionToCompensateId`
// @dev The caller may pass type(uint256).max as the creditPositionId in order to represent "mint a new DebtPosition/CreditPosition pair"
/// @param params CompensateParams struct containing the following fields:
/// - uint256 debtPositionToRepayId: The id of the debt position to repay
/// - uint256 creditPositionWithDebtToRepayId: The id of the credit position ID with debt to repay
/// - uint256 creditPositionToCompensateId: The id of the credit position to compensate
/// - uint256 amount: The amount of tokens to compensate (in decimals, e.g. 1_000e6 for 1000 aUSDC)
function compensate(CompensateParams calldata params) external payable;
Expand Down
8 changes: 5 additions & 3 deletions src/libraries/AccountingLibrary.sol
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ library AccountingLibrary {
/// The credit amount cannot be reduced below the minimum credit.
/// This operation breaks the initial sum of credit equal to the debt position future value.
/// If the loan is in REPAID status, this is expected, as lenders grdually claim their credit.
/// If the loan is in ACTIVE status, a debt reduction must be performed together with a credit reduction (See reduceDebtAndCredit).
/// If the loan is in ACTIVE/OVERDUE status, a debt reduction must be performed together with a credit reduction (See reduceDebtAndCredit).
/// @param state The state object
/// @param creditPositionId The credit position id
function reduceCredit(State storage state, uint256 creditPositionId, uint256 amount) public {
Expand Down Expand Up @@ -176,6 +176,8 @@ library AccountingLibrary {
}

/// @notice Get the swap fee for a given cash amount and tenor
/// @dev The intention for the swap fee is to for it to be charged on the "issuance value" of the credit and it is a predefined APR
/// The issuance value is defined as the amount of credit sold discounted by the chosen rate
/// @param state The state object
/// @param cash The cash amount
/// @param tenor The tenor
Expand Down Expand Up @@ -265,7 +267,7 @@ library AccountingLibrary {
);
fees = Math.mulDivUp(cashAmountOut, swapFeePercent, PERCENT) + state.feeConfig.fragmentationFee;
} else {
// for maxCashAmountOutFragmentation < amountOut < maxCashAmountOut we are in an inconsistent situation
// for maxCashAmountOutFragmentation < cashAmountOut < maxCashAmountOut we are in an inconsistent situation
// where charging the swap fee would require to sell a credit that exceeds the max possible credit

revert Errors.NOT_ENOUGH_CASH(maxCashAmountOutFragmentation, cashAmountOut);
Expand Down Expand Up @@ -306,7 +308,7 @@ library AccountingLibrary {
creditAmountOut = Math.mulDivDown(netCashAmountIn, PERCENT + ratePerTenor, PERCENT);
fees = getSwapFee(state, netCashAmountIn, tenor) + state.feeConfig.fragmentationFee;
} else {
revert Errors.NOT_ENOUGH_CREDIT(maxCashAmountIn, cashAmountIn);
revert Errors.NOT_ENOUGH_CASH(maxCashAmountIn, cashAmountIn);
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/libraries/CapsLibrary.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ library CapsLibrary {
/// @dev Reverts if the Variable Pool does not have enough liquidity
/// This safety mechanism prevents takers from matching orders that could not be withdrawn from the Variable Pool.
/// Nevertheless, the Variable Pool may still fail to withdraw the cash due to other factors (such as a pause, etc),
/// which is understood as an acceptable risk.
/// which is understood as an acceptable risk, since it can be mitigated by a multicall.
/// This check can be bypassed with a sandwitch attack that supplies just enough to make the pool liquid again,
/// which we understand as an acceptable risk, since it can be mitigated by a multicall.
/// @param state The state struct
/// @param amount The amount of cash to withdraw
function validateVariablePoolHasEnoughLiquidity(State storage state, uint256 amount) public view {
Expand Down

0 comments on commit 9eb0670

Please sign in to comment.