Tadle

Tadle
DeFiFoundry
27,750 USDC
View results
Submission Details
Severity: medium
Invalid

Offers can NOT be fully filled due to wrong validation

Summary

Offers can NOT be fully filled due to wrong validation

Vulnerability Details

The protocol offers users the ability to list funds in a bid-ask order book like market. When a market maker list an offer they have to provide collateral and also amount of points that the offer will hold.

function createOffer(CreateOfferParams calldata params) external payable {
/**
* @dev points and amount must be greater than 0
* @dev eachTradeTax must be less than 100%, decimal scaler is 10000
* @dev collateralRate must be more than 100%, decimal scaler is 10000
*/
if (params.points == 0x0 || params.amount == 0x0) {
revert Errors.AmountIsZero();
}
...
makerInfoMap[makerAddr] = MakerInfo({
offerSettleType: params.offerSettleType,
authority: _msgSender(),
marketPlace: params.marketPlace,
tokenAddress: params.tokenAddress,
originOffer: offerAddr,
platformFee: 0,
eachTradeTax: params.eachTradeTax
});
/// @dev update offer info
offerInfoMap[offerAddr] = OfferInfo({
id: offerId,
authority: _msgSender(),
maker: makerAddr,
offerStatus: OfferStatus.Virgin,
offerType: params.offerType,
=> points: params.points,
amount: params.amount,
collateralRate: params.collateralRate,
abortOfferStatus: AbortOfferStatus.Initialized,
usedPoints: 0,
tradeTax: 0,
settledPoints: 0,
settledPointTokenAmount: 0,
settledCollateralAmount: 0
});
/// @dev update stock info
stockInfoMap[stockAddr] = StockInfo({
id: offerId,
stockStatus: StockStatus.Initialized,
stockType: params.offerType == OfferType.Ask
? StockType.Bid
: StockType.Ask,
authority: _msgSender(),
maker: makerAddr,
preOffer: address(0x0),
offer: offerAddr,
=> points: params.points,
amount: params.amount
});
...
}

points are proportionally tied to the amount of tokens deposited. Take for example an Ask maker deposits 200 tokenA and sets 100 points (2:1). When a taker wants to buy 50 point that would equal 100 tokenA. Points are used to segregate the initial offer into smaller ones if needed and as a currency.

As seen in the createTaker function when an order is filled the usedPoints amount of the order is updated with the equivalent amount of points.

function createTaker(address _offer, uint256 _points) external payable {
if (_points == 0x0) {
revert Errors.AmountIsZero();
}
OfferInfo storage offerInfo = offerInfoMap[_offer];
MakerInfo storage makerInfo = makerInfoMap[offerInfo.maker];
...
=> offerInfo.usedPoints = offerInfo.usedPoints + _points;
...

The problem is that due to a wrong check in the createTaker function the order can NOT be fully filled - all points used - offerInfo.usedPoints == offerInfo.points

function createTaker(address _offer, uint256 _points) external payable {
...
=> if (offerInfo.points < _points + offerInfo.usedPoints) {
revert NotEnoughPoints(
offerInfo.points,
offerInfo.usedPoints,
_points
);
}

Impact

Loss of funds since the maker has to either close the offer to get all (or part) of his funds back or settle it in which case they lose some funds.

Tools Used

Manual Review, Protocol Docs

Recommendations

In the createTaker function ket users be able to fully fill maker offers

function createTaker(address _offer, uint256 _points) external payable {
/**
* @dev offer must be virgin
* @dev points must be greater than 0
* @dev total points must be greater than used points plus _points
*/
if (_points == 0x0) {
revert Errors.AmountIsZero();
}
OfferInfo storage offerInfo = offerInfoMap[_offer];
MakerInfo storage makerInfo = makerInfoMap[offerInfo.maker];
if (offerInfo.offerStatus != OfferStatus.Virgin) {
revert InvalidOfferStatus();
}
- if (offerInfo.points < _points + offerInfo.usedPoints) {
+ if (offerInfo.points <= _points + offerInfo.usedPoints) {
revert NotEnoughPoints(
offerInfo.points,
offerInfo.usedPoints,
_points
);
}
/// @dev market place must be online
ISystemConfig systemConfig = tadleFactory.getSystemConfig();
{
...
Updates

Lead Judging Commences

0xnevi Lead Judge
about 1 year ago
0xnevi Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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