Tadle

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

TokenManager.sol: TokenManager.tillIn( ) allows transfer of zero msg.value

Summary

The function TokenManager.tillIn( ) has a non zero msg.value check . However , there's no explicit check to ensure that the amount is non-zero for ERC20 tokens before attempting the transfer

Vulnerability Details

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

The issue lies in the fact that msg.value check occurs inside of the if-else block so it does not apply to all token types

function tillIn(
address _accountAddress,
address _tokenAddress,
uint256 _amount,
bool _isPointToken
)
external
payable
onlyRelatedContracts(tadleFactory, _msgSender())
onlyInTokenWhiteList(_isPointToken, _tokenAddress)
{
//
if (_tokenAddress == wrappedNativeToken) {
// @audit-issue
if (msg.value < _amount) {
revert Errors.NotEnoughMsgValue(msg.value, _amount);
}
IWrappedNativeToken(wrappedNativeToken).deposit{value: _amount}();
_safe_transfer(wrappedNativeToken, capitalPoolAddr, _amount);
} else {
//
}

This affects all functions that depends on it to transfer tokens .

https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/PreMarkets.sol#L96

https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/PreMarkets.sol#L356

https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/PreMarkets.sol#L515

Proof of concept

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.13;
import {Test, console2} from "forge-std/Test.sol";
import {SystemConfig} from "../src/core/SystemConfig.sol";
import {CapitalPool} from "../src/core/CapitalPool.sol";
import {TokenManager} from "../src/core/TokenManager.sol";
import {PreMarktes} from "../src/core/PreMarkets.sol";
import {DeliveryPlace} from "../src/core/DeliveryPlace.sol";
import {TadleFactory} from "../src/factory/TadleFactory.sol";
import {OfferStatus, StockStatus, AbortOfferStatus, OfferType, StockType, OfferSettleType} from "../src/storage/OfferStatus.sol";
import {IPerMarkets, OfferInfo, StockInfo, MakerInfo, CreateOfferParams} from "../src/interfaces/IPerMarkets.sol";
import {TokenBalanceType, ITokenManager} from "../src/interfaces/ITokenManager.sol";
import {GenerateAddress} from "../src/libraries/GenerateAddress.sol";
import {MockERC20Token} from "./mocks/MockERC20Token.sol";
import {WETH9} from "./mocks/WETH9.sol";
import {UpgradeableProxy} from "../src/proxy/UpgradeableProxy.sol";
contract Create_OfferWith_Zero_Transfer is Test {
SystemConfig systemConfig;
CapitalPool capitalPool;
TokenManager tokenManager;
PreMarktes preMarktes;
DeliveryPlace deliveryPlace;
address marketPlace;
WETH9 weth9;
MockERC20Token mockUSDCToken;
MockERC20Token mockPointToken;
address admin = vm.addr(0x123);
address user = vm.addr(0xd123);
uint256 basePlatformFeeRate = 5_000;
uint256 baseReferralRate = 300_000;
bytes4 private constant INITIALIZE_OWNERSHIP_SELECTOR =
bytes4(keccak256(bytes("initializeOwnership(address)")));
function setUp() public {
// deploy mocks
weth9 = new WETH9();
TadleFactory tadleFactory = new TadleFactory(admin);
mockUSDCToken = new MockERC20Token();
mockPointToken = new MockERC20Token();
SystemConfig systemConfigLogic = new SystemConfig();
CapitalPool capitalPoolLogic = new CapitalPool();
TokenManager tokenManagerLogic = new TokenManager();
PreMarktes preMarktesLogic = new PreMarktes();
DeliveryPlace deliveryPlaceLogic = new DeliveryPlace();
bytes memory deploy_data = abi.encodeWithSelector(
INITIALIZE_OWNERSHIP_SELECTOR,
admin
);
vm.startPrank(admin);
address systemConfigProxy = tadleFactory.deployUpgradeableProxy(
1,
address(systemConfigLogic),
bytes(deploy_data)
);
address preMarktesProxy = tadleFactory.deployUpgradeableProxy(
2,
address(preMarktesLogic),
bytes(deploy_data)
);
address deliveryPlaceProxy = tadleFactory.deployUpgradeableProxy(
3,
address(deliveryPlaceLogic),
bytes(deploy_data)
);
address capitalPoolProxy = tadleFactory.deployUpgradeableProxy(
4,
address(capitalPoolLogic),
bytes(deploy_data)
);
address tokenManagerProxy = tadleFactory.deployUpgradeableProxy(
5,
address(tokenManagerLogic),
bytes(deploy_data)
);
// Deploy Proxy
systemConfig = SystemConfig(systemConfigProxy);
capitalPool = CapitalPool(capitalPoolProxy);
tokenManager = TokenManager(tokenManagerProxy);
preMarktes = PreMarktes(preMarktesProxy);
deliveryPlace = DeliveryPlace(deliveryPlaceProxy);
// initialize
systemConfig.initialize(basePlatformFeeRate, baseReferralRate);
tokenManager.initialize(address(weth9));
address[] memory tokenAddressList = new address[](2);
tokenAddressList[0] = address(mockUSDCToken);
tokenAddressList[1] = address(weth9);
tokenManager.updateTokenWhiteListed(tokenAddressList, true);
// create market place
systemConfig.createMarketPlace("Backpack", false);
marketPlace = GenerateAddress.generateMarketPlaceAddress("Backpack");
deal(address(mockUSDCToken), admin, 100000000 * 10 ** 18);
vm.stopPrank();
vm.warp(1719826275);
}
function test_Create_Offer_With_Zero_MsgValue () public {
// Give user some balance
deal(user, 1);
deal(address(mockUSDCToken), user, 1 ether);
uint256 offerId = 0 ;
// Generate makerAddr Address
address makerAddr = GenerateAddress.generateMakerAddress(offerId);
// Generate offer Address
address offerAddr = GenerateAddress.generateOfferAddress(offerId);
// Generate offer Stock Address
address stockAddr = GenerateAddress.generateStockAddress(offerId);
// points
uint256 _points = 500;
// amount
uint256 _amount = 63 ;
// Collateral Rate
uint256 collateralRate = 12000;
// tax
uint256 tax = 500 ;
// Create offer params
CreateOfferParams memory params = CreateOfferParams(
marketPlace,
address(mockUSDCToken),
_points,
_amount,
collateralRate,
tax,
OfferType.Ask,
OfferSettleType.Turbo
);
vm.startPrank(user);
// Aprove TokenManager
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
// create Offer with msg.value == 0
preMarktes.createOffer{value: 0 }(params);
// We can as well Create Taker with msg.value == 0
preMarktes.createTaker{value:0 }(offerAddr, _points);
vm.stopPrank();
}
}

Impact

Attacker can ddos the system

Tools Used

Manual Review

Recommendations

Move the msg.value check outside of the if-else block so it applies to all token types

// ... (rest of code)
+ if (msg.value < _amount) {
+ revert Errors.NotEnoughMsgValue(msg.value, _amount);
}
if (_tokenAddress == wrappedNativeToken) {
- if (msg.value < _amount) {
- revert Errors.NotEnoughMsgValue(msg.value, _amount);
}
// ... (rest of the wrapped token logic)
} else {
// ... (ERC20 token logic)
}
Updates

Lead Judging Commences

0xnevi Lead Judge
about 1 year ago
0xnevi Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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