Platform: Code4rena
Start Date: 09/12/2022
Pot Size: $90,500 USDC
Total HM: 35
Participants: 84
Period: 7 days
Judge: GalloDaSballo
Total Solo HM: 12
Id: 192
League: ETH
Rank: 29/84
Findings: 3
Award: $442.95
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: minhtrng
Also found by: 0Kage, Aymen0909, HollaDieWaldfee, Jeiwan, KingNFT, bin2chen, hansfriese, rvierdiiev
201.2303 USDC - $201.23
The protocol doesn't receive fees when margin is added via the addToPosition
function. An equal amount of tigUSD is minted even though it's not backed by margin.
The protocol takes fees from margin provided by users: for example, when opening a market order:
_marginAfterFees
).Thus, traders provide full margin when opening orders and fees are subtracted from accounted margin. If trader closes an order, they'll receive margin - fees.
The protocol also allows traders to add margin to existing positions at a different price, via the addToPosition
function (Trading.sol#L255). This function, however, subtracts fees from margin before transferring margin from traders:
_handleDeposit( _trade.tigAsset, _marginAsset, _addMargin - _fee, // @audit trader won't pay fees _stableVault, _permitData, _trader );
As a result, the protocol doesn't receive fees when margin is added via addToPosition
.
Manual review
Consider not subtracting fees from _addMargin
in addToPosition
when calling _handleDeposit
.
#0 - c4-judge
2022-12-20T16:16:33Z
GalloDaSballo marked the issue as duplicate of #659
#1 - c4-judge
2023-01-18T13:58:43Z
GalloDaSballo changed the severity to 3 (High Risk)
#2 - c4-judge
2023-01-22T17:43:20Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: 0xA5DF
Also found by: HollaDieWaldfee, Jeiwan, KingNFT
230.0301 USDC - $230.03
The open interest trackers in the PairsContract
will accumulate non-removable residual due to fees not being excluded from open interest values. The residual will grow over time.
Open interest is the total leveraged value deposited by traders to the protocol. It excludes fees since fees are subtracted from traders' positions and sent to a treasury. For example, when opening a market order, open interest is changed by the position size, (margin - fees) * leverage
(Trading.sol#L198-L200):
uint256 _marginAfterFees = _tradeInfo.margin - _handleOpenFees(_tradeInfo.asset, _tradeInfo.margin*_tradeInfo.leverage/1e18, _trader, _tigAsset, false); uint256 _positionSize = _marginAfterFees * _tradeInfo.leverage / 1e18; ... if (_tradeInfo.direction) { tradingExtension.modifyLongOi(_tradeInfo.asset, _tigAsset, true, _positionSize); } else { tradingExtension.modifyShortOi(_tradeInfo.asset, _tigAsset, true, _positionSize); }
For limit orders, however, accounting is different. Fees are not applied when a limit order is created because limit orders are not active until they're executed (Trading.sol#L330-L346):
// @audit-info full margin is deposited _handleDeposit(_tigAsset, _tradeInfo.marginAsset, _tradeInfo.margin, _tradeInfo.stableVault, _permitData, _trader); ... // @audit-info full margin is accounted position.mint( IPosition.MintTrade( _trader, _tradeInfo.margin, _tradeInfo.leverage, _tradeInfo.asset, _tradeInfo.direction, _price, _tradeInfo.tpPrice, _tradeInfo.slPrice, _orderType, _tigAsset ) );
However, when a limit order is executed, the change in open interest doesn't account for fees (Trading.sol#L513-L517):
// @audit-info fees are calculated on the full position size, this is correct uint _fee = _handleOpenFees(trade.asset, trade.margin*trade.leverage/1e18, trade.trader, trade.tigAsset, true); ... if (trade.direction) { // @audit wrong: fees are not subtracted from the position size tradingExtension.modifyLongOi(trade.asset, trade.tigAsset, true, trade.margin*trade.leverage/1e18); } else { // @audit wrong: fees are not subtracted from the position size tradingExtension.modifyShortOi(trade.asset, trade.tigAsset, true, trade.margin*trade.leverage/1e18); } ... // @audit-info accounted margin excludes fees, correct position.executeLimitOrder(_id, trade.price, trade.margin - _fee);
As a result, open interest will be increased by an amount that is bigger than the actual value deposited to the protocol. However, when positions are closed or liquidated, open interest is reduced by values that include fees (TradingExtension.sol#L70-L74, Trading.sol#L546-L550). This will create a residual in open interest that will accumulate over time and may, in the worst case, cause a denial of service when new positions are opened: the modifyLongOi
and modifyShortOi
revert when maximal open interest is reached (PairsContract.sol#L157, PairsContract.sol#L177).
Manual review
Consider excluding fees from the position size before updating open interest in the executeLimitOrder
function.
#0 - c4-sponsor
2023-01-05T22:55:34Z
TriHaz marked the issue as sponsor confirmed
#1 - c4-judge
2023-01-15T16:21:28Z
GalloDaSballo marked the issue as duplicate of #576
#2 - c4-judge
2023-01-22T17:54:17Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: rbserver
Also found by: 0x52, 0xDecorativePineapple, 0xdeadbeef0x, 8olidity, Jeiwan, Rolezn, __141345__, bin2chen, eierina, fs0c, gzeon, joestakey, koxuan, kwhuo68, ladboy233, rvierdiiev, yixxas
11.6941 USDC - $11.69
The deprecated interface doesn't provide additional data that can make integration with Chainlink more reliable. This can potentially lead the protocol to be more sensitive to disruptions or non-standard behavior of Chainlink oracles.
The verifyPrice
function of the TradingLibrary
library uses a deprecated Chainlink interface (TradingLibrary.sol#L113):
int256 assetChainlinkPriceInt = IPrice(_chainlinkFeed).latestAnswer();
As per the Chainlink documentation, the latestAnswer
function is deprecated.
This interface doesn't allow to check the timestamp of when the latest price was reported and ensure that the price is not stale. Also, the Chainlink data feed aggregator provides minimal and maximal price values per each supported token to improve validation of prices returned by oracles. Using the old interface may result in the protocol not detecting stale or wrong prices reported by Chainlink feeds.
Manual review
Consider using the latestRoundData
function instead of latestAnswer
and consider following the Monitoring Data Feeds recommendations by Chainlink.
#0 - c4-judge
2022-12-20T16:34:47Z
GalloDaSballo marked the issue as duplicate of #655
#1 - c4-judge
2023-01-22T17:30:43Z
GalloDaSballo marked the issue as satisfactory