Tadle

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

When the `DeliveryPlace::settleAskMaker()` function calls `tokenManager.addTokenBalance()` to update the user balance, the `TokenBalanceType` parameter uses an operation, resulting in a balance update error

Summary

When the DeliveryPlace::settleAskMaker() function calls tokenManager.addTokenBalance() to update the user balance, the TokenBalanceType parameter uses an operation, resulting in a balance update error

Vulnerability Details

The source code snippet of the DeliveryPlace::settleAskMaker() function is as follows. As marked by @>, TokenBalanceType is used incorrectly.

function settleAskMaker(address _offer, uint256 _settledPoints) external {
// SNIP...
@> 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
);
}
// SNIP...
}

Poc

To demonstrate the issue, add the following test code to test/PreMarkets.t.sol and run it:

function testAskPointAndAmount() public {
///////////////////////////
// user create Bid.Offer //
///////////////////////////
vm.prank(user);
// transfer mockUSDCToken 1e16 to capitalPool
preMarktes.createOffer(
CreateOfferParams(
marketPlace,
address(mockUSDCToken),
1000,
0.01 * 1e18,
12000,
300,
OfferType.Ask,
OfferSettleType.Turbo
)
);
// Cache user's offer address
address offerAddr = GenerateAddress.generateOfferAddress(0);
////////////////////////
// user2 create Taker //
////////////////////////
vm.prank(user2);
// transfer mockUSDCToken 1.235e16 to capitalPool
preMarktes.createTaker(offerAddr, 1000);
// Cache user2's stock address
address user2StockAddr = GenerateAddress.generateStockAddress(1);
////////////////////////
// admin updateMarket //
////////////////////////
vm.prank(user1);
systemConfig.updateMarket(
"Backpack",
address(mockPointToken),
0.01 * 1e18,
block.timestamp - 1,
3600
);
//////////////////////////
// user settleAskMaker //
//////////////////////////
vm.startPrank(user);
mockPointToken.approve(address(tokenManager), 10000 * 10 ** 18);
// transfer mockPointToken 1e19 to capitalPool
deliveryPlace.settleAskMaker(offerAddr, 1000);
vm.stopPrank();
//////////////////////////
// user2 closeBidTaker //
//////////////////////////
vm.prank(user2);
deliveryPlace.closeBidTaker(user2StockAddr);
//////////////////////////
// check user balance //
//////////////////////////
// user
console2.log("--------------- user balance ---------------");
balanceHelper(user);
}
// helper
function balanceHelper(address _user) view public {
uint256 usermockUSDCTokenAmount_TaxIncome = tokenManager.userTokenBalanceMap(
address(_user),
address(mockUSDCToken),
TokenBalanceType.TaxIncome
);
console2.log("usermockUSDCTokenAmount_TaxIncome:",usermockUSDCTokenAmount_TaxIncome);
uint256 usermockUSDCTokenAmount_ReferralBonus = tokenManager.userTokenBalanceMap(
address(_user),
address(mockUSDCToken),
TokenBalanceType.ReferralBonus
);
console2.log("usermockUSDCTokenAmount_ReferralBonus:",usermockUSDCTokenAmount_ReferralBonus);
uint256 usermockUSDCTokenAmount_SalesRevenue = tokenManager.userTokenBalanceMap(
address(_user),
address(mockUSDCToken),
TokenBalanceType.SalesRevenue
);
console2.log("usermockUSDCTokenAmount_SalesRevenue:",usermockUSDCTokenAmount_SalesRevenue);
uint256 usermockUSDCTokenAmount_RemainingCash = tokenManager.userTokenBalanceMap(
address(_user),
address(mockUSDCToken),
TokenBalanceType.RemainingCash
);
console2.log("usermockUSDCTokenAmount_RemainingCash:",usermockUSDCTokenAmount_RemainingCash);
uint256 usermockUSDCTokenAmount_MakerRefund = tokenManager.userTokenBalanceMap(
address(_user),
address(mockUSDCToken),
TokenBalanceType.MakerRefund
);
console2.log("usermockUSDCTokenAmount_MakerRefund:",usermockUSDCTokenAmount_MakerRefund);
uint256 usermockUSDCTokenAmount_PointToken = tokenManager.userTokenBalanceMap(
address(_user),
address(mockUSDCToken),
TokenBalanceType.PointToken
);
console2.log("usermockUSDCTokenAmount_PointToken:",usermockUSDCTokenAmount_PointToken);
}

From the following output, we can see that userTokenBalanceMap[user][address(mockUSDCToken)][TokenBalanceType.SalesRevenue] == 22000000000000000(2.2e16), while the actual values ​​should be userTokenBalanceMap[user][address(mockUSDCToken)][TokenBalanceType.SalesRevenue] == 1e16 and userTokenBalanceMap[user][address(mockUSDCToken)][TokenBalanceType.MakerRefund] == 1.2e16

[PASS] testAskPointAndAmount() (gas: 1172769)
Logs:
--------------- user balance ---------------
usermockUSDCTokenAmount_TaxIncome: 300000000000000
usermockUSDCTokenAmount_ReferralBonus: 0
@> usermockUSDCTokenAmount_SalesRevenue: 22000000000000000
usermockUSDCTokenAmount_RemainingCash: 0
@> usermockUSDCTokenAmount_MakerRefund: 0
usermockUSDCTokenAmount_PointToken: 0

Code Snippet

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

Impact

The source code snippet of the DeliveryPlace::settleAskMaker() function is as follows. As marked by @>, TokenBalanceType is used incorrectly.

Tools Used

Manual Review

Recommendations

tokenManager.addTokenBalance(
+ TokenBalanceType.MakerRefund,
- TokenBalanceType.SalesRevenue,
_msgSender(),
makerInfo.tokenAddress,
makerRefundAmount
);

test again:

[PASS] testAskPointAndAmount() (gas: 1224694)
Logs:
--------------- user balance ---------------
usermockUSDCTokenAmount_TaxIncome: 300000000000000
usermockUSDCTokenAmount_ReferralBonus: 0
@> usermockUSDCTokenAmount_SalesRevenue: 10000000000000000
usermockUSDCTokenAmount_RemainingCash: 0
@> usermockUSDCTokenAmount_MakerRefund: 12000000000000000
usermockUSDCTokenAmount_PointToken: 0
Updates

Lead Judging Commences

0xnevi Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-DeliveryPlace-settleAskMaker-addTokenBalance-wrong-TokenBalanceType

Valid low severity, while the token type inputted is wrong, userTokenBalanceMap is still incremented appropriately, so users can still withdraw their funds. So this would technically only affect accounting and public view functions.

Support

FAQs

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