Tadle

Tadle
DeFi
30,000 USDC
View results
Submission Details
Severity: low
Invalid

The call to `TokenManger#transfer()` will always revert if the allowance it has of the capital pool != ZER0 but is less than the amount required to `safe_Transfer_From()` the capital pool.

Summary

On the call to TokenManager#transfer() which is used in the tillIn()& withdraw()function respectively, it calls CapitalPool#approve() only in the scenario the from is the CapitalPool and the allowance it has of it is zero, however, there is no way to approve itself of a greater amount in the scenario where the allowance is not zero but is less than the required amount to safe_Tranfer_From() from the CapitalPool causing a DOS where it will be impossible to ever transfer from the capital pool again.

Vulnerability Details

On the call to transfer() the TokenManager is only approved of type(uint256.max) if the current allowance is zero but doesn't handle the situation where the allowance is less than the required amount to safe_``Transfer``_From()

https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/TokenManager.sol#L233C1-L262C6

function _transfer(
address _token,
address _from,
address _to,
uint256 _amount,
address _capitalPoolAddr
) internal {
uint256 fromBalanceBef = IERC20(_token).balanceOf(_from);
uint256 toBalanceBef = IERC20(_token).balanceOf(_to);
//@audit if the amount to `_safe_transfer_from()` != 0 but is < that the current allowance
//`_safe_transfer_from()` will always revert when the from address is the capital pool
if (
_from == _capitalPoolAddr &&
IERC20(_token).allowance(_from, address(this)) == 0x0
) {
ICapitalPool(_capitalPoolAddr).approve(address(this));
}
_safe_transfer_from(_token, _from, _to, _amount);
uint256 fromBalanceAft = IERC20(_token).balanceOf(_from);
uint256 toBalanceAft = IERC20(_token).balanceOf(_to);
if (fromBalanceAft != fromBalanceBef - _amount) {
revert TransferFailed();
}
if (toBalanceAft != toBalanceBef + _amount) {
revert TransferFailed();
}
}

Example of Bug Scenario

  • On first interaction with transfer()when tillIn or withdraw, the allowance of the tokenManger to the capital pool is set to type(uint256).max for a particular token. and the allowance reduces from then onwards. on each call to _safe_transfer_from().

  • Since many users will use the protocol, the allowance will reduce from then onwards till very little amount maybe even in months, a year or 2, point being the allowance from type(uint256).maxcan be reduced to very little amount and the rate which this can happen will depend on the token decimals as it will reduce depending on the decimal of the token, number of users, time, and number of interaction will the protocol.

  • Now the allowance is reduced to a low amount, but allowance is only set when it is zero, if amounts to safe_``Transfer_From()* is greater than the current allowance the call to transfer()will always revert.*

Impact

This will lead to DOS in the TokenManager contract when it tries to make use of funds from the capital pool thereby DOSing all core functions that call the underlying transfer()function.

Tools Used

Manual Review & sleep.

Recommendations

Increase the allowance that the token manager has of the capital pool in the scenario where the amount it needs is greater than the current allowance.

also don't forget to approve to zero first since some token revert on approval to non-zero value if the previous allowance isn't zero.

Updates

Lead Judging Commences

0xnevi Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Other
Assigned finding tags:

[invalid] finding-CapitalPool-USDT-approve-zero-first

I believe this is invalid, - For weird ERC20s with front-running approval protection such as UDST (only known instance so far), max approval is likely only required to be invoked once, considering the supply cap of such tokens. (USDT supply is at 53.8 billion (53.8e9 * 1e9, so this is 100% sufficient) - If approvals are insufficient, a new proxy for tadle market can always be deployed via the TadleFactory contract and migrated

Appeal created

shealtielanz Submitter
10 months ago
0xnevi Lead Judge
10 months ago
0xnevi Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Other
Assigned finding tags:

[invalid] finding-CapitalPool-USDT-approve-zero-first

I believe this is invalid, - For weird ERC20s with front-running approval protection such as UDST (only known instance so far), max approval is likely only required to be invoked once, considering the supply cap of such tokens. (USDT supply is at 53.8 billion (53.8e9 * 1e9, so this is 100% sufficient) - If approvals are insufficient, a new proxy for tadle market can always be deployed via the TadleFactory contract and migrated

Support

FAQs

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