Tadle

Tadle
DeFi
30,000 USDC
View results
Submission Details
Severity: low
Invalid

Protocol does not consider ETH when calling tokenManager.tillIn()

Summary

Users cannot settle offers Involving wrappedNativeToken

Vulnerability Details

Within tokenManager.tillIn() there's a check if the tokenAddress to fill is wrappedNativeToken which checks that the msg.value sent following the call matches or exceeds the supplied amount otherwise the call would revert.

function tillIn(
address _accountAddress,
address _tokenAddress,
uint256 _amount,
bool _isPointToken
)
external
payable
onlyRelatedContracts(tadleFactory, _msgSender())
onlyInTokenWhiteList(_isPointToken, _tokenAddress)
{
//--snipped for brevity
if (_tokenAddress == wrappedNativeToken) {//@audit-info
/**
* @dev token is native token
* @notice check msg value
* @dev if msg value is less than _amount, revert
* @dev wrap native token and transfer to capital pool
*/
if (msg.value < _amount) {
revert Errors.NotEnoughMsgValue(msg.value, _amount);
}
IWrappedNativeToken(wrappedNativeToken).deposit{value: _amount}();
_safe_transfer(wrappedNativeToken, capitalPoolAddr, _amount);
} else {
/// @notice token is ERC20 token
_transfer(
_tokenAddress,
_accountAddress,
capitalPoolAddr,
_amount,
capitalPoolAddr
);
}
emit TillIn(_accountAddress, _tokenAddress, _amount, _isPointToken);
}

The issue is that when tokenManager.tillIn is invoked within DeliveryPlace.sol it does not consider the case of tokenAddress address being wrappedNativeToken and should pass the msg.value following the call.

https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/DeliveryPlace.sol#L267

https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/DeliveryPlace.sol#L377

function settleAskMaker(address _offer, uint256 _settledPoints) external {
--snipped for brevity
ITokenManager tokenManager = tadleFactory.getTokenManager();
if (settledPointTokenAmount > 0) {
tokenManager.tillIn(//@audit-info this does not send msg.value to the tokenmanager assuming marketPlaceInfo.tokenAddress is wrappedNativeToken.
_msgSender(),
marketPlaceInfo.tokenAddress,
settledPointTokenAmount,
true
);
}
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
);
}
/**
* @notice Settle ask taker
* @dev caller must be stock authority
* @dev market place status must be AskSettling
* @param _stock stock address
* @param _settledPoints settled points
* @notice _settledPoints must be less than or equal to stock points
*/
function settleAskTaker(address _stock, uint256 _settledPoints) external {
IPerMarkets perMarkets = tadleFactory.getPerMarkets();
StockInfo memory stockInfo = perMarkets.getStockInfo(_stock);
(
OfferInfo memory offerInfo,
MakerInfo memory makerInfo,
MarketPlaceInfo memory marketPlaceInfo,
MarketPlaceStatus status
) = getOfferInfo(stockInfo.preOffer);
if (stockInfo.stockStatus != StockStatus.Initialized) {
revert InvalidStockStatus();
}
if (marketPlaceInfo.fixedratio) {
revert FixedRatioUnsupported();
}
if (stockInfo.stockType == StockType.Bid) {
revert InvalidStockType();
}
if (_settledPoints > stockInfo.points) {
revert InvalidPoints();
}
if (status == MarketPlaceStatus.AskSettling) {
if (_msgSender() != offerInfo.authority) {
revert Errors.Unauthorized();
}
} else {
if (_msgSender() != owner()) {
revert Errors.Unauthorized();
}
if (_settledPoints > 0) {
revert InvalidPoints();
}
}
uint256 settledPointTokenAmount = marketPlaceInfo.tokenPerPoint *
_settledPoints;
ITokenManager tokenManager = tadleFactory.getTokenManager();
if (settledPointTokenAmount > 0) {
tokenManager.tillIn(//@audit-info
_msgSender(),
marketPlaceInfo.tokenAddress,
settledPointTokenAmount,
true
);
tokenManager.addTokenBalance(
TokenBalanceType.PointToken,
offerInfo.authority,
makerInfo.tokenAddress,
settledPointTokenAmount
);
}
uint256 collateralFee = OfferLibraries.getDepositAmount(
offerInfo.offerType,
offerInfo.collateralRate,
stockInfo.amount,
false,
Math.Rounding.Floor
);
if (_settledPoints == stockInfo.points) {
tokenManager.addTokenBalance(
TokenBalanceType.RemainingCash,
_msgSender(),
makerInfo.tokenAddress,
collateralFee
);
} else {
tokenManager.addTokenBalance(
TokenBalanceType.MakerRefund,
offerInfo.authority,
makerInfo.tokenAddress,
collateralFee
);
}
--Snipped for brevity
);
}

Impact

Offers Involving WrappedNativeToken cannot be settled.

Tools Used

Manual Review

Recommendations

https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/DeliveryPlace.sol#L222

- function settleAskMaker(address _offer, uint256 _settledPoints) external {
+ function settleAskMaker(address _offer, uint256 _settledPoints) external payable {
(
OfferInfo memory offerInfo,
MakerInfo memory makerInfo,
MarketPlaceInfo memory marketPlaceInfo,
MarketPlaceStatus status
) = getOfferInfo(_offer);
if (_settledPoints > offerInfo.usedPoints) {
revert InvalidPoints();
}
if (marketPlaceInfo.fixedratio) {
revert FixedRatioUnsupported();
}
if (offerInfo.offerType == OfferType.Bid) {
revert InvalidOfferType(OfferType.Ask, OfferType.Bid);
}
if (
offerInfo.offerStatus != OfferStatus.Virgin &&
offerInfo.offerStatus != OfferStatus.Canceled
) {
revert InvalidOfferStatus();
}
if (status == MarketPlaceStatus.AskSettling) {
if (_msgSender() != offerInfo.authority) {
revert Errors.Unauthorized();
}
} else {
if (_msgSender() != owner()) {
revert Errors.Unauthorized();
}
if (_settledPoints > 0) {
revert InvalidPoints();
}
}
uint256 settledPointTokenAmount = marketPlaceInfo.tokenPerPoint *
_settledPoints;
ITokenManager tokenManager = tadleFactory.getTokenManager();
if (settledPointTokenAmount > 0) {
- tokenManager.tillIn(
+ tokenManager.tillIn{value: msg.value}(
_msgSender(),
marketPlaceInfo.tokenAddress,
settledPointTokenAmount,
true
);
}
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
);
}

https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/DeliveryPlace.sol#L335

- function settleAskTaker(address _stock, uint256 _settledPoints) external {
+ function settleAskTaker(address _stock, uint256 _settledPoints) external payable {
IPerMarkets perMarkets = tadleFactory.getPerMarkets();
StockInfo memory stockInfo = perMarkets.getStockInfo(_stock);
(
OfferInfo memory offerInfo,
MakerInfo memory makerInfo,
MarketPlaceInfo memory marketPlaceInfo,
MarketPlaceStatus status
) = getOfferInfo(stockInfo.preOffer);
if (stockInfo.stockStatus != StockStatus.Initialized) {
revert InvalidStockStatus();
}
if (marketPlaceInfo.fixedratio) {
revert FixedRatioUnsupported();
}
if (stockInfo.stockType == StockType.Bid) {
revert InvalidStockType();
}
if (_settledPoints > stockInfo.points) {
revert InvalidPoints();
}
if (status == MarketPlaceStatus.AskSettling) {
if (_msgSender() != offerInfo.authority) {
revert Errors.Unauthorized();
}
} else {
if (_msgSender() != owner()) {
revert Errors.Unauthorized();
}
if (_settledPoints > 0) {
revert InvalidPoints();
}
}
uint256 settledPointTokenAmount = marketPlaceInfo.tokenPerPoint *
_settledPoints;
ITokenManager tokenManager = tadleFactory.getTokenManager();
if (settledPointTokenAmount > 0) {
- tokenManager.tillIn(
+ tokenManager.tillIn{value: msg.value}(
_msgSender(),
marketPlaceInfo.tokenAddress,
settledPointTokenAmount,
true
);
tokenManager.addTokenBalance(
TokenBalanceType.PointToken,
offerInfo.authority,
makerInfo.tokenAddress,
settledPointTokenAmount
);
}
uint256 collateralFee = OfferLibraries.getDepositAmount(
offerInfo.offerType,
offerInfo.collateralRate,
stockInfo.amount,
false,
Math.Rounding.Floor
);
if (_settledPoints == stockInfo.points) {
tokenManager.addTokenBalance(
TokenBalanceType.RemainingCash,
_msgSender(),
makerInfo.tokenAddress,
collateralFee
);
} else {
tokenManager.addTokenBalance(
TokenBalanceType.MakerRefund,
offerInfo.authority,
makerInfo.tokenAddress,
collateralFee
);
}
perMarkets.settleAskTaker(
stockInfo.preOffer,
_stock,
_settledPoints,
settledPointTokenAmount
);
emit SettleAskTaker(
makerInfo.marketPlace,
offerInfo.maker,
_stock,
stockInfo.preOffer,
_msgSender(),
_settledPoints,
settledPointTokenAmount,
collateralFee
);
}
Updates

Lead Judging Commences

0xnevi Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

[invalid] finding-PreMarkets-settleAskMaker-settleAskTaker-no-msg.value-sent

Invalid, in `settleAskMaker` and `settleAskTaker` you are settling the point token to be given to the takers, which is an ERC20 token, so no native ETH is involved and thus no msg.value is required.

Support

FAQs

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