Summary
In Turbo Mode, the original seller deposits crypto as collateral, enabling subsequent traders to buy and sell points without additional collateral. Upon settlement, the original seller transfers tokens to all remaining buyers. Additionally, the original seller earns a maker bonus on every subsequent trade made from their original offer.
Vulnerability Details
If seller is not original one, he does not deposit additional collateral, so he should not receive maker refund during settlementPeriod.
function settleAskMaker(address _offer, uint256 _settledPoints) external {
__SNIP__
uint256 makerRefundAmount;
if (_settledPoints == offerInfo.usedPoints) {
if (offerInfo.offerStatus == OfferStatus.Virgin) {
@> makerRefundAmount = OfferLibraries.getDepositAmount(
offerInfo.offerType,
offerInfo.collateralRate,
offerInfo.amount,
true,
Math.Rounding.Floor
);
} else {
@> uint256 usedAmount = offerInfo.amount.mulDiv(
offerInfo.usedPoints,
offerInfo.points,
Math.Rounding.Floor
);
makerRefundAmount = OfferLibraries.getDepositAmount(
offerInfo.offerType,
offerInfo.collateralRate,
usedAmount,
true,
Math.Rounding.Floor
);
}
tokenManager.addTokenBalance(
TokenBalanceType.SalesRevenue,
_msgSender(),
makerInfo.tokenAddress,
makerRefundAmount
);
}
IPerMarkets perMarkets = tadleFactory.getPerMarkets();
perMarkets.settledAskOffer(
_offer,
_settledPoints,
settledPointTokenAmount
);
emit SettleAskMaker(
makerInfo.marketPlace,
offerInfo.maker,
_offer,
_msgSender(),
_settledPoints,
settledPointTokenAmount,
makerRefundAmount
);
}
As you can see in above code, implementation doesn't check if OfferSettleType (owner) of offer is Turbo or not.
Owner of listOffer can receive refunds, but this is not deposited before.
Impact
Protocol loses refund amount for each listOffer, and breaks accounting system.
Tools Used
Manual review
Recommendations
function settleAskMaker(address _offer, uint256 _settledPoints) external { // @audit all functions are working when marketPlace is offline
__SNIP__
uint256 makerRefundAmount;
- if (_settledPoints == offerInfo.usedPoints) {
+ if (_settledPoints == offerInfo.usedPoints && makerInfo.offerSettleType == OfferSettleType.Protected) {
if (offerInfo.offerStatus == OfferStatus.Virgin) {
makerRefundAmount = OfferLibraries.getDepositAmount(
offerInfo.offerType,
offerInfo.collateralRate,
offerInfo.amount,
true,
Math.Rounding.Floor
);
} else {
uint256 usedAmount = offerInfo.amount.mulDiv(
offerInfo.usedPoints,
offerInfo.points,
Math.Rounding.Floor
);
makerRefundAmount = OfferLibraries.getDepositAmount(
offerInfo.offerType,
offerInfo.collateralRate,
usedAmount,
true,
Math.Rounding.Floor
);
}
tokenManager.addTokenBalance(
TokenBalanceType.SalesRevenue, // @audit remainingCash
_msgSender(),
makerInfo.tokenAddress,
makerRefundAmount
);
}
IPerMarkets perMarkets = tadleFactory.getPerMarkets();
perMarkets.settledAskOffer(
_offer,
_settledPoints,
settledPointTokenAmount
);
emit SettleAskMaker(
makerInfo.marketPlace,
offerInfo.maker,
_offer,
_msgSender(),
_settledPoints,
settledPointTokenAmount,
makerRefundAmount
);
}