You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
withdrawMarginUsd() uses round down to compute requiredMarginInCollateralX18
May result in fillOrder() part of fee not being collected
Vulnerability Details
in fillOrder()
We will deduct 3 fees settlementFee , orderFee , pnl
fillOrder() -> deductAccountMargin()
function deductAccountMargin(
Data storageself,
FeeRecipients.Data memoryfeeRecipients,
UD60x18 pnlUsdX18,
UD60x18 settlementFeeUsdX18,
UD60x18 orderFeeUsdX18
)
...
for (uint256 i; i < cachedCollateralLiquidationPriorityLength; i++) {
if (settlementFeeUsdX18.gt(UD60x18_ZERO) && ctx.settlementFeeDeductedUsdX18.lt(settlementFeeUsdX18)) {
// attempt to deduct from this collateral difference between settlement fee// and amount of settlement fee paid so far
(ctx.withdrawnMarginUsdX18, ctx.isMissingMargin) =withdrawMarginUsd(
self,
collateralType,
ctx.marginCollateralPriceUsdX18,
settlementFeeUsdX18.sub(ctx.settlementFeeDeductedUsdX18),
feeRecipients.settlementFeeRecipient
);
// update amount of settlement fee paid so far by the amount// that was actually withdraw from this collateral
ctx.settlementFeeDeductedUsdX18 = ctx.settlementFeeDeductedUsdX18.add(ctx.withdrawnMarginUsdX18);
}
// order fee logic same as settlement fee aboveif (orderFeeUsdX18.gt(UD60x18_ZERO) && ctx.orderFeeDeductedUsdX18.lt(orderFeeUsdX18)) {
(ctx.withdrawnMarginUsdX18, ctx.isMissingMargin) =withdrawMarginUsd(
self,
collateralType,
ctx.marginCollateralPriceUsdX18,
orderFeeUsdX18.sub(ctx.orderFeeDeductedUsdX18),
feeRecipients.orderFeeRecipient
);
ctx.orderFeeDeductedUsdX18 = ctx.orderFeeDeductedUsdX18.add(ctx.withdrawnMarginUsdX18);
}
// pnl logic same as settlement & order fee aboveif (pnlUsdX18.gt(UD60x18_ZERO) && ctx.pnlDeductedUsdX18.lt(pnlUsdX18)) {
(ctx.withdrawnMarginUsdX18, ctx.isMissingMargin) =withdrawMarginUsd(
self,
collateralType,
ctx.marginCollateralPriceUsdX18,
pnlUsdX18.sub(ctx.pnlDeductedUsdX18),
feeRecipients.marginCollateralRecipient
);
ctx.pnlDeductedUsdX18 = ctx.pnlDeductedUsdX18.add(ctx.withdrawnMarginUsdX18);
}
// if there is no missing margin then exit the loop// since all amounts have been deducted
@>if (!ctx.isMissingMargin) {
break;
}
}
function withdrawMarginUsd(
Data storageself,
addresscollateralType,
UD60x18 marginCollateralPriceUsdX18,
UD60x18 amountUsdX18,
addressrecipient
)
internalreturns (UD60x18 withdrawnMarginUsdX18, boolisMissingMargin)
{
MarginCollateralConfiguration.Data storage marginCollateralConfiguration =
MarginCollateralConfiguration.load(collateralType);
UD60x18 marginCollateralBalanceX18 =getMarginCollateralBalance(self, collateralType);
@> UD60x18 requiredMarginInCollateralX18 = amountUsdX18.div(marginCollateralPriceUsdX18);
uint256 amountToTransfer;
@>if (marginCollateralBalanceX18.gte(requiredMarginInCollateralX18)) {
withdraw(self, collateralType, requiredMarginInCollateralX18);
amountToTransfer =
marginCollateralConfiguration.convertUd60x18ToTokenAmount(requiredMarginInCollateralX18);
IERC20(collateralType).safeTransfer(recipient, amountToTransfer);
withdrawnMarginUsdX18 = amountUsdX18;
isMissingMargin =false;
} else {
withdraw(self, collateralType, marginCollateralBalanceX18);
amountToTransfer = marginCollateralConfiguration.convertUd60x18ToTokenAmount(marginCollateralBalanceX18);
IERC20(collateralType).safeTransfer(recipient, amountToTransfer);
withdrawnMarginUsdX18 = marginCollateralPriceUsdX18.mul(marginCollateralBalanceX18);
isMissingMargin =true;
}
}
The problem is this line: UD60x18 requiredMarginInCollateralX18 = amountUsdX18.div(marginCollateralPriceUsdX18);
This is using round down , which may cause dust pnl to change isMissingMargin to false, causing break loop.
Example.
The user has two collaterals
marginCollateralBalance = [Collateral_A = 1e18 usd , Collateral_B= 2000e18 usd ]
settlementFee =100e18
orderFee=100e18
pnl = 9
marginCollateralPrice= 10e18
Bug Report
M-08. when fillOrder() , small pnl can cause orderFee/settlementFee to not be fully collected
Submitted by giraffe0x, 0x1982. Selected submission by: 0x1982.
Summary
withdrawMarginUsd()
usesround down
to computerequiredMarginInCollateralX18
May result in
fillOrder()
part offee
not being collectedVulnerability Details
in
fillOrder()
We will deduct 3 fees
settlementFee
,orderFee
,pnl
fillOrder()
->deductAccountMargin()
The problem is this line:
UD60x18 requiredMarginInCollateralX18 = amountUsdX18.div(marginCollateralPriceUsdX18);
This is using
round down
, which may causedust pnl
to changeisMissingMargin
to false, causingbreak loop
.Example.
The user has two collaterals
marginCollateralBalance = [Collateral_A = 1e18 usd , Collateral_B= 2000e18 usd ]
settlementFee =100e18
orderFee=100e18
pnl = 9
marginCollateralPrice= 10e18
When charging a pnl, due to the use of
round down
requiredMarginInCollateralX18 = amountUsdX18.div(marginCollateralPriceUsdX18) = 9 * 1e18 / 10e18 ==0
This caused the loop to jump out of the loop early by incorrectly overriding
isMissingMargin=false
.miss fees: settlementFee = 99e18 + orderFee = 100e18
Impact
dust pnl can cause orderFee/settlementFee to not be fully collected
Tools Used
Recommendations
use round up
The text was updated successfully, but these errors were encountered: