Tadle

Tadle
DeFi
30,000 USDC
View results
Submission Details
Severity: high
Valid

use of incorrect collateralRate in `preMarket::listOffer` for _protected offers_ allows an attacker to drain contract funds via `preMarket::abortAskOffer` , `preMarket::closeOffer` or `DeliveryPlace::settleAskMaker`

Summary

PreMarkets::listOffer uses the preOffers collateralRate instead of the provided one(_collateralRate) to calculate the amount of collateral required for a protected offer ,due to this , an attacker can provide an absurdly large collateral rate that is then used to calculate the refund amount when they cancel/settle/abort their offer. Effectively, they can drain the contract by providing just the right value as their collateral rate

Vulnerability Details

From the code snippet here where the transferAmount is calculated

/// @dev transfer collateral when offer settle type is protected
if (makerInfo.offerSettleType == OfferSettleType.Protected) {
uint256 transferAmount = OfferLibraries.getDepositAmount(
offerInfo.offerType,
-> offerInfo.collateralRate,
_amount,
true,
Math.Rounding.Ceil
);
ITokenManager tokenManager = tadleFactory.getTokenManager();
tokenManager.tillIn{value: msg.value}(
_msgSender(),
makerInfo.tokenAddress,
transferAmount,
false
);
}

The transferAmount deposited by the sender is calculated using the collateralRate of the stockInfo.preOffer not the collateralRate of the offer itself about to be created.
However ,when the offer is closed/settled/aborted , the transferAmount is calculated using the collateralRate of the offer itself. One such case is in PreMarkets::closeOffer

https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/PreMarkets.sol#L433-L456

/**
* @dev update refund token from capital pool to balance
* @dev offer settle type is protected or original offer
*/
if (
makerInfo.offerSettleType == OfferSettleType.Protected ||
stockInfo.preOffer == address(0x0)
) {
-> uint256 refundAmount = OfferLibraries.getRefundAmount(
offerInfo.offerType,
offerInfo.amount,
offerInfo.points,
offerInfo.usedPoints,
offerInfo.collateralRate
);
ITokenManager tokenManager = tadleFactory.getTokenManager();
tokenManager.addTokenBalance(
TokenBalanceType.MakerRefund,
_msgSender(),
makerInfo.tokenAddress,
refundAmount
);
}

When offer type is Ask , getRefundAmount calculates the refund amount as (amount - usedAmount)*collateralRate,

Impact

HIGH/CRITICAL - Severe loss of funds

Tools Used

Manual Review

Recommendations

function listOffer(
address _stock,
uint256 _amount,
uint256 _collateralRate
) external payable {
//...ommited for brevity
/// @dev change abort offer status when offer settle type is turbo
if (makerInfo.offerSettleType == OfferSettleType.Turbo) {
address originOffer = makerInfo.originOffer;
OfferInfo memory originOfferInfo = offerInfoMap[originOffer];
if (_collateralRate != originOfferInfo.collateralRate) {
revert InvalidCollateralRate();
}
originOfferInfo.abortOfferStatus = AbortOfferStatus.SubOfferListed;
}
/// @dev transfer collateral when offer settle type is protected
if (makerInfo.offerSettleType == OfferSettleType.Protected) {
uint256 transferAmount = OfferLibraries.getDepositAmount(
offerInfo.offerType,
- offerInfo.collateralRate,
+ _collateralRate,
_amount,
true,
Math.Rounding.Ceil
);
ITokenManager tokenManager = tadleFactory.getTokenManager();
tokenManager.tillIn{value: msg.value}(
_msgSender(),
makerInfo.tokenAddress,
transferAmount,
false
);
}
address offerAddr = GenerateAddress.generateOfferAddress(stockInfo.id);
if (offerInfoMap[offerAddr].authority != address(0x0)) {
revert OfferAlreadyExist();
}
//...ommited function body for brevity as it is irrelevant
}
Updates

Lead Judging Commences

0xnevi Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-PreMarkets-listOffer-collateralRate-manipulate

Valid high severity, because the collateral rate utilized when creating an offer is stale and retrieved from a previously set collateral rate, it allows possible manipilation of refund amounts using an inflated collateral rate to drain funds from the CapitalPool contract

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.