Summary
Offers can be for a market that does not have token address and without matching market token if market has.
Vulnerability Details
https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/SystemConfig.sol#L90C4-L109C6
https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/PreMarkets.sol#L39-L157
In SystemConfig.sol::createMarketPlace
function, when a market is created it becomes online that makes it available to createOffer.
But it does initialize market token address. Because of this offers can be created with the token that does not have market.
function createMarketPlace(
string calldata _marketPlaceName,
bool _fixedratio
) external onlyOwner {
...
...
marketPlaceInfo.status = MarketPlaceStatus.Online;
marketPlaceInfo.fixedratio = _fixedratio;
emit CreateMarketPlaceInfo(_marketPlaceName, marketPlace, _fixedratio);
}
And In the PreMarkets.sol::createOffer
function it does not check that the offer we are creating for the token it matches to market's token address;
function createOffer(CreateOfferParams calldata params) external payable {
...
ISystemConfig systemConfig = tadleFactory.getSystemConfig();
MarketPlaceInfo memory marketPlaceInfo = systemConfig
.getMarketPlaceInfo(params.marketPlace);
marketPlaceInfo.checkMarketPlaceStatus(
block.timestamp,
MarketPlaceStatus.Online
);
makerInfoMap[makerAddr] = MakerInfo({
offerSettleType: params.offerSettleType,
authority: _msgSender(),
marketPlace: params.marketPlace,
tokenAddress: params.tokenAddress,
originOffer: offerAddr,
platformFee: 0,
eachTradeTax: params.eachTradeTax
});
offerInfoMap[offerAddr] = OfferInfo({
id: offerId,
authority: _msgSender(),
maker: makerAddr,
offerStatus: OfferStatus.Virgin,
offerType: params.offerType,
points: params.points,
amount: params.amount,
collateralRate: params.collateralRate,
abortOfferStatus: AbortOfferStatus.Initialized,
usedPoints: 0,
tradeTax: 0,
settledPoints: 0,
settledPointTokenAmount: 0,
settledCollateralAmount: 0
});
stockInfoMap[stockAddr] = StockInfo({
id: offerId,
stockStatus: StockStatus.Initialized,
stockType: params.offerType == OfferType.Ask
? StockType.Bid
: StockType.Ask,
authority: _msgSender(),
maker: makerAddr,
preOffer: address(0x0),
offer: offerAddr,
points: params.points,
amount: params.amount
});
}
Impact
Offers created with Market which does not have token address.
Tools Used
Foundry
Recommendations
Add these codes in existing code.
function createMarketPlace(
string calldata _marketPlaceName,
bool _fixedratio,
```diff
+ address _tokenAddress
```
) external onlyOwner {
...
...
marketPlaceInfo.status = MarketPlaceStatus.Online;
marketPlaceInfo.fixedratio = _fixedratio;
```diff
+ marketPlaceInfo.tokenAddress = _tokenAddress;
```
emit CreateMarketPlaceInfo(_marketPlaceName, marketPlace, _fixedratio);
}
function createOffer(CreateOfferParams calldata params) external payable {
if (params.points == 0x0 || params.amount == 0x0) {
revert Errors.AmountIsZero();
}
```diff
+ if (params.tokenAddress == address(0)){
revert ZeroTokenAddress()
}
```
if (params.eachTradeTax > Constants.EACH_TRADE_TAX_DECIMAL_SCALER) {
revert InvalidEachTradeTaxRate();
}
if (params.collateralRate < Constants.COLLATERAL_RATE_DECIMAL_SCALER) {
revert InvalidCollateralRate();
}
ISystemConfig systemConfig = tadleFactory.getSystemConfig();
MarketPlaceInfo memory marketPlaceInfo = systemConfig
.getMarketPlaceInfo(params.marketPlace);
marketPlaceInfo.checkMarketPlaceStatus(
block.timestamp,
MarketPlaceStatus.Online
);
```diff
+ if (params.tokenAddress != marketPlaceInfo.tokenAddress) {
+ revert MismatchedToken();
}
```
makerInfoMap[makerAddr] = MakerInfo({
offerSettleType: params.offerSettleType,
authority: _msgSender(),
marketPlace: params.marketPlace,
tokenAddress: params.tokenAddress,
originOffer: offerAddr,
platformFee: 0,
eachTradeTax: params.eachTradeTax
});
offerInfoMap[offerAddr] = OfferInfo({
id: offerId,
authority: _msgSender(),
maker: makerAddr,
offerStatus: OfferStatus.Virgin,
offerType: params.offerType,
points: params.points,
amount: params.amount,
collateralRate: params.collateralRate,
abortOfferStatus: AbortOfferStatus.Initialized,
usedPoints: 0,
tradeTax: 0,
settledPoints: 0,
settledPointTokenAmount: 0,
settledCollateralAmount: 0
});
stockInfoMap[stockAddr] = StockInfo({
id: offerId,
stockStatus: StockStatus.Initialized,
stockType: params.offerType == OfferType.Ask
? StockType.Bid
: StockType.Ask,
authority: _msgSender(),
maker: makerAddr,
preOffer: address(0x0),
offer: offerAddr,
points: params.points,
amount: params.amount
});
}