Tadle

Tadle
DeFiFoundry
27,750 USDC
View results
Submission Details
Severity: high
Valid

Token sent to bid taker is incorrectly set to offer token instead of point token

Summary

Bid takers must call DeliveryPlace::closeBidTaker to receive the points purchased in the pre-market. The addTokenBalance hook in the TokenManager contract is responsible for transferring the points settled by the ask maker. However, during this process, the address of the point token is incorrectly set, and the quoted token from the offer made by the ask maker is used instead.

Vulnerability Details

As a result, the bid taker does not receive the intended points but instead receives an equivalent amount in the quoted token of the offer.

uint256 pointTokenAmount = offerInfo.settledPointTokenAmount.mulDiv(
userRemainingPoints,
offerInfo.usedPoints,
Math.Rounding.Floor
);
tokenManager.addTokenBalance(
TokenBalanceType.PointToken,
_msgSender(),
@> makerInfo.tokenAddress,
pointTokenAmount
);

Impact

See PoC below:

function test_ask_offer_turbo_eth() public {
vm.startPrank(user);
preMarktes.createOffer{value: 0.012 * 1e18}(
CreateOfferParams(
marketPlace,
address(weth9),
1000,
0.01 * 1e18,
12000,
300,
OfferType.Ask,
OfferSettleType.Turbo
)
);
address offerAddr = GenerateAddress.generateOfferAddress(0);
preMarktes.createTaker{value: 0.005175 * 1e18}(offerAddr, 500);
address stock1Addr = GenerateAddress.generateStockAddress(1);
preMarktes.listOffer(stock1Addr, 0.006 * 1e18, 12000);
address offer1Addr = GenerateAddress.generateOfferAddress(1);
preMarktes.closeOffer(stock1Addr, offer1Addr);
preMarktes.relistOffer(stock1Addr, offer1Addr);
vm.stopPrank();
vm.prank(user1);
systemConfig.updateMarket(
"Backpack",
address(mockPointToken),
0.01 * 1e18,
block.timestamp - 1,
3600
);
vm.startPrank(user);
mockPointToken.approve(address(tokenManager), 10000 * 10 ** 18);
deliveryPlace.settleAskMaker(offerAddr, 500);
deliveryPlace.closeBidTaker(stock1Addr);
vm.stopPrank();
uint256 balanceUserTokenPoints = tokenManager.userTokenBalanceMap(
user,
address(mockPointToken),
TokenBalanceType.PointToken
);
console2.log(balanceUserTokenPoints); //0
assertEq(balanceUserTokenPoints, 0);
uint256 balanceUserWETH = tokenManager.userTokenBalanceMap(
user,
address(weth9),
TokenBalanceType.PointToken
);
console2.log(balanceUserWETH); //5e18
assertEq(balanceUserWETH, 5e18);
}

Tools Used

Manual review.

Recommendations

Load marketplaceInfo struct and use token address set in marketplace when calling the hook:

...
(
OfferInfo memory preOfferInfo,
MakerInfo memory makerInfo,
MarketPlaceInfo memory marketPlaceInfo,
) = getOfferInfo(stockInfo.preOffer);
...
tokenManager.addTokenBalance(
TokenBalanceType.PointToken,
_msgSender(),
marketPlaceInfo.tokenAddress,
pointTokenAmount
);
Updates

Lead Judging Commences

0xnevi Lead Judge about 1 year 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.