Tadle

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

Insufficient balance handling in `TokenManager::withdraw`

https://github.com/tadle-com/market-evm/blob/main/src/core/TokenManager.sol#L146-L203

Summary

The contract lacks proper handling for insufficient balance situations in the withdraw function. When a user attempts to claim his claimable amount TokenManager is responsible to check the balance of the CapitalPool contract since the claimAbleAmount is stored there.

Vulnerability Details

No checks are made for the balance of CapitalPool contract as we can see here:

TokenManager.sol
137: function withdraw(
138: address _tokenAddress,
139: TokenBalanceType _tokenBalanceType
140: ) external whenNotPaused { // @audit - potential reentrancy issue
141: uint256 claimAbleAmount = userTokenBalanceMap[_msgSender()][
142: _tokenAddress
143: ][_tokenBalanceType];
144:
145: if (claimAbleAmount == 0) {
146: return;
147: }
148:
149: address capitalPoolAddr = tadleFactory.relatedContracts(
150: RelatedContractLibraries.CAPITAL_POOL
151: );
152:
153: if (_tokenAddress == wrappedNativeToken) {
154: /**
155: * @dev token is native token
156: * @dev transfer from capital pool to msg sender
157: * @dev withdraw native token to token manager contract
158: * @dev transfer native token to msg sender
159: */
160: _transfer(
161: wrappedNativeToken,
162: capitalPoolAddr,
163: address(this),
164: claimAbleAmount,
165: capitalPoolAddr
166: );
167:
168: IWrappedNativeToken(wrappedNativeToken).withdraw(claimAbleAmount);
169: payable(msg.sender).transfer(claimAbleAmount);
170: } else {
171: /**
172: * @dev token is ERC20 token
173: * @dev transfer from capital pool to msg sender
174: */
175: _safe_transfer_from(
176: _tokenAddress,
177: capitalPoolAddr,
178: _msgSender(),
179: claimAbleAmount
180: );
181: }
182:
183: emit Withdraw(
184: _msgSender(),
185: _tokenAddress,
186: _tokenBalanceType,
187: claimAbleAmount
188: );
189: }

withdraw function: The function assumes that the capital pool always has sufficient funds to cover the withdrawal request. There's no check to ensure that the claimAbleAmount is available in the capital pool before attempting the transfer.
If the balance is insufficient, the transfer will fail, but there's no error handling mechanism or pre-check to prevent this from happening.

Impact

Lack of balance checks can lead to failed transactions without informative errors, causing user dissatisfaction and potential disruption of contract operations. Furthermore, repeated failed transactions might incur unnecessary gas costs.

Tools Used

Manual review

Recommendations

Add balance checks before transfers

function withdraw(
address _tokenAddress,
TokenBalanceType _tokenBalanceType
) external whenNotPaused {
uint256 claimAbleAmount = userTokenBalanceMap[_msgSender()][_tokenAddress][_tokenBalanceType];
address capitalPoolAddr = tadleFactory.relatedContracts(RelatedContractLibraries.CAPITAL_POOL);
+ uint256 capitalPoolBalance = IERC20(_tokenAddress).balanceOf(capitalPoolAddr);
+ require(capitalPoolBalance >= claimAbleAmount, "Capital pool has insufficient balance");
address capitalPoolAddr = tadleFactory.relatedContracts(
RelatedContractLibraries.CAPITAL_POOL
);
. . .
emit Withdraw(_msgSender(), _tokenAddress, _tokenBalanceType, claimAbleAmount);
}
Updates

Lead Judging Commences

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

Support

FAQs

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