Tadle

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

Ask offer taker cannot receive point tokens even if the offer is settled

Summary

Ask offer maker cannot receive point tokens even if the offer is settled, due to the incorrect PointToken balance update.

Vulnerability Details

When a ask offer is settled, the taker calls closeBidTaker() to to receive point token. However, the PointToken balance is not update correctly.

tokenManager.addTokenBalance(
TokenBalanceType.PointToken,
_msgSender(),
@> makerInfo.tokenAddress,
pointTokenAmount
);

Protocol wrongly sets the token address to the collateral address, as a result, the taker won't be withdraw any point token but the collateral tokens.

Image the following scenario:

  1. The market tokenPerPoint is ;

  2. Alice creates an ask offer, point is , amount is ;

  3. Bob creates taker against Alice's ask offer, pays collateral tokens to buy points;

  4. In AskSettling Phase, Alice settles the ask offer;

  5. Bob calls closeBidTaker() in order to receive point tokens, however, beause of this issue, Bob receives collateral tokens instead of point tokens.

Please run the PoC in PreMarkets.t.sol to verify:

function testAudit_AskOfferBuyerReceivesNoPointTokenWhenTheOfferIsSettled() public {
// Create Market
address auditMarketPlace = GenerateAddress.generateMarketPlaceAddress("AuditMarket");
vm.startPrank(user1);
systemConfig.createMarketPlace("AuditMarket", false);
systemConfig.updateMarket("AuditMarket", address(mockPointToken), 0.5e18, block.timestamp + 30 days, 2 days);
vm.stopPrank();
address alice = makeAddr("Alice");
deal(address(mockUSDCToken), alice, 1200e18);
address bob = makeAddr("Bob");
deal(address(mockUSDCToken), bob, 1005e18);
address aliceOfferAddr = GenerateAddress.generateOfferAddress(preMarktes.offerId());
// Alice creates an Ask offer
vm.startPrank(alice);
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
preMarktes.createOffer(CreateOfferParams({
marketPlace: auditMarketPlace,
tokenAddress: address(mockUSDCToken),
points: 1000,
amount: 1000e18,
collateralRate: 12000,
eachTradeTax: 0,
offerType: OfferType.Ask,
offerSettleType: OfferSettleType.Turbo
}));
vm.stopPrank();
address bobStockAddr = GenerateAddress.generateStockAddress(preMarktes.offerId());
// Bob creates taker
vm.startPrank(bob);
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
preMarktes.createTaker(aliceOfferAddr, 1000);
vm.stopPrank();
// AskSettling
vm.warp(block.timestamp + 31 days);
deal(address(mockPointToken), alice, 500e18);
// Alice settles ask offer
vm.startPrank(alice);
mockPointToken.approve(address(tokenManager), type(uint256).max);
deliveryPlace.settleAskMaker(aliceOfferAddr, 1000);
vm.stopPrank();
// Bob closes bid taker
vm.prank(bob);
deliveryPlace.closeBidTaker(bobStockAddr);
// Bob receives 500e18 collateral tokens instead of point tokens
assertEq(tokenManager.userTokenBalanceMap(bob, address(mockUSDCToken), TokenBalanceType.PointToken), 500e18);
assertEq(tokenManager.userTokenBalanceMap(bob, address(mockUSDCToken), TokenBalanceType.TaxIncome), 0);
assertEq(tokenManager.userTokenBalanceMap(bob, address(mockUSDCToken), TokenBalanceType.ReferralBonus), 0);
assertEq(tokenManager.userTokenBalanceMap(bob, address(mockUSDCToken), TokenBalanceType.SalesRevenue), 0);
assertEq(tokenManager.userTokenBalanceMap(bob, address(mockUSDCToken), TokenBalanceType.RemainingCash), 0);
assertEq(tokenManager.userTokenBalanceMap(bob, address(mockUSDCToken), TokenBalanceType.MakerRefund), 0);
assertEq(tokenManager.userTokenBalanceMap(bob, address(mockPointToken), TokenBalanceType.PointToken), 0);
assertEq(tokenManager.userTokenBalanceMap(bob, address(mockPointToken), TokenBalanceType.TaxIncome), 0);
assertEq(tokenManager.userTokenBalanceMap(bob, address(mockPointToken), TokenBalanceType.ReferralBonus), 0);
assertEq(tokenManager.userTokenBalanceMap(bob, address(mockPointToken), TokenBalanceType.SalesRevenue), 0);
assertEq(tokenManager.userTokenBalanceMap(bob, address(mockPointToken), TokenBalanceType.RemainingCash), 0);
assertEq(tokenManager.userTokenBalanceMap(bob, address(mockPointToken), TokenBalanceType.MakerRefund), 0);
}

Impact

Maker receives no point tokens.

Tools Used

Manual Review

Recommendations

function closeBidTaker(address _stock) external {
...
+ ISystemConfig systemConfig = tadleFactory.getSystemConfig();
+ MarketPlaceInfo memory marketPlaceInfo = systemConfig.getMarketPlaceInfo(makerInfo.marketPlace);
tokenManager.addTokenBalance(
TokenBalanceType.PointToken,
_msgSender(),
- makerInfo.tokenAddress,
+ marketPlaceInfo.tokenAddress,
pointTokenAmount
);
...
}
Updates

Lead Judging Commences

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

finding-DeliveryPlace-settleAskTaker-closeBidTaker-wrong-makerinfo-token-address-addToken-balance

Valid high severity, In `settleAskTaker/closeBidTaker`, by assigning collateral token to user balance instead of point token, if collateral token is worth more than point, this can cause stealing of other users collateral tokens within the CapitalPool contract, If the opposite occurs, user loses funds based on the points they are supposed to receive

Support

FAQs

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