Description
In the PreMarkets::createTaker
function, there is a potential vulnerability where the msg.sender
(the user initiating the transaction) can act as their own referrer. This creates a scenario where users can exploit the referral system by referring themselves, thereby earning referral rewards without actually bringing in new participants. This behavior undermines the intended purpose of the referral program, which is to incentivize users to attract new participants to the platform.
function createTaker(address _offer, uint256 _points) external payable {
* @dev offer must be virgin
* @dev points must be greater than 0
* @dev total points must be greater than used points plus _points
*/
if (_points == 0x0) {
revert Errors.AmountIsZero();
}
OfferInfo storage offerInfo = offerInfoMap[_offer];
MakerInfo storage makerInfo = makerInfoMap[offerInfo.maker];
if (offerInfo.offerStatus != OfferStatus.Virgin) {
revert InvalidOfferStatus();
}
if (offerInfo.points < _points + offerInfo.usedPoints) {
revert NotEnoughPoints(offerInfo.points, offerInfo.usedPoints, _points);
}
ISystemConfig systemConfig = tadleFactory.getSystemConfig();
{
MarketPlaceInfo memory marketPlaceInfo = systemConfig.getMarketPlaceInfo(makerInfo.marketPlace);
marketPlaceInfo.checkMarketPlaceStatus(block.timestamp, MarketPlaceStatus.Online);
}
@>>>> ReferralInfo memory referralInfo = systemConfig.getReferralInfo(_msgSender());
uint256 platformFeeRate = systemConfig.getPlatformFeeRate(_msgSender());
address stockAddr = GenerateAddress.generateStockAddress(offerId);
if (stockInfoMap[stockAddr].authority != address(0x0)) {
revert StockAlreadyExist();
}
uint256 depositAmount = _points.mulDiv(offerInfo.amount, offerInfo.points, Math.Rounding.Ceil);
uint256 platformFee = depositAmount.mulDiv(platformFeeRate, Constants.PLATFORM_FEE_DECIMAL_SCALER);
uint256 tradeTax = depositAmount.mulDiv(makerInfo.eachTradeTax, Constants.EACH_TRADE_TAX_DECIMAL_SCALER);
ITokenManager tokenManager = tadleFactory.getTokenManager();
_depositTokenWhenCreateTaker(platformFee, depositAmount, tradeTax, makerInfo, offerInfo, tokenManager);
offerInfo.usedPoints = offerInfo.usedPoints + _points;
stockInfoMap[stockAddr] = StockInfo({
id: offerId,
stockStatus: StockStatus.Initialized,
stockType: offerInfo.offerType == OfferType.Ask ? StockType.Bid : StockType.Ask,
authority: _msgSender(),
maker: offerInfo.maker,
preOffer: _offer,
points: _points,
amount: depositAmount,
offer: address(0x0)
});
offerId = offerId + 1;
uint256 remainingPlatformFee =
_updateReferralBonus(platformFee, depositAmount, stockAddr, makerInfo, referralInfo, tokenManager);
makerInfo.platformFee = makerInfo.platformFee + remainingPlatformFee;
_updateTokenBalanceWhenCreateTaker(_offer, tradeTax, depositAmount, offerInfo, makerInfo, tokenManager);
emit CreateTaker(_offer, msg.sender, stockAddr, _points, depositAmount, tradeTax, remainingPlatformFee);
}
Impact
Users may exploit the referral system by creating multiple accounts or using the same account to refer themselves, leading to an unearned accumulation of rewards. The platform may incur financial losses due to the payout of undeserved referral rewards, affecting the platform's sustainability and financial health.
Tools Used
Manual Review
Recommendations
To mitigate this vulnerability, implement a check in the PreMarkets::createTaker
function to ensure that msg.sender
cannot be the same as the referrer. This can be done by adding the following condition:
function createTaker(address _offer, uint256 _points) external payable {
/**
* @dev offer must be virgin
* @dev points must be greater than 0
* @dev total points must be greater than used points plus _points
*/
if (_points == 0x0) {
revert Errors.AmountIsZero();
}
OfferInfo storage offerInfo = offerInfoMap[_offer];
MakerInfo storage makerInfo = makerInfoMap[offerInfo.maker];
if (offerInfo.offerStatus != OfferStatus.Virgin) {
revert InvalidOfferStatus();
}
if (offerInfo.points < _points + offerInfo.usedPoints) {
revert NotEnoughPoints(offerInfo.points, offerInfo.usedPoints, _points);
}
/// @dev market place must be online
ISystemConfig systemConfig = tadleFactory.getSystemConfig();
{
MarketPlaceInfo memory marketPlaceInfo = systemConfig.getMarketPlaceInfo(makerInfo.marketPlace);
marketPlaceInfo.checkMarketPlaceStatus(block.timestamp, MarketPlaceStatus.Online);
}
+ if (_msgSender() == referralInfo.referrer) {
+ revert InvalidReferrer();
+ }
ReferralInfo memory referralInfo = systemConfig.getReferralInfo(_msgSender());
uint256 platformFeeRate = systemConfig.getPlatformFeeRate(_msgSender());
/// @dev generate stock address
address stockAddr = GenerateAddress.generateStockAddress(offerId);
if (stockInfoMap[stockAddr].authority != address(0x0)) {
revert StockAlreadyExist();
}
/// @dev Transfer token from user to capital pool as collateral
uint256 depositAmount = _points.mulDiv(offerInfo.amount, offerInfo.points, Math.Rounding.Ceil);
uint256 platformFee = depositAmount.mulDiv(platformFeeRate, Constants.PLATFORM_FEE_DECIMAL_SCALER);
uint256 tradeTax = depositAmount.mulDiv(makerInfo.eachTradeTax, Constants.EACH_TRADE_TAX_DECIMAL_SCALER);
ITokenManager tokenManager = tadleFactory.getTokenManager();
_depositTokenWhenCreateTaker(platformFee, depositAmount, tradeTax, makerInfo, offerInfo, tokenManager);
offerInfo.usedPoints = offerInfo.usedPoints + _points;
/// @dev update stock info
stockInfoMap[stockAddr] = StockInfo({
id: offerId,
stockStatus: StockStatus.Initialized,
stockType: offerInfo.offerType == OfferType.Ask ? StockType.Bid : StockType.Ask,
authority: _msgSender(),
maker: offerInfo.maker,
preOffer: _offer,
points: _points,
amount: depositAmount,
offer: address(0x0)
});
offerId = offerId + 1;
uint256 remainingPlatformFee =
_updateReferralBonus(platformFee, depositAmount, stockAddr, makerInfo, referralInfo, tokenManager);
makerInfo.platformFee = makerInfo.platformFee + remainingPlatformFee;
_updateTokenBalanceWhenCreateTaker(_offer, tradeTax, depositAmount, offerInfo, makerInfo, tokenManager);
/// @dev emit CreateTaker
emit CreateTaker(_offer, msg.sender, stockAddr, _points, depositAmount, tradeTax, remainingPlatformFee);
}