IVX engaged Guardian to review the security of its Options protocol. From the 4th of September to the 12th of September, a team of 6 auditors reviewed the source code in scope.
- Published
- Review window
- September 4 to 12, 2023
- Language
- Solidity
- Chains
- Arbitrum, Avalanche
- Sector
- Derivatives
- 7 Critical
- 9 High
- 25 Medium
- 16 Low
- 0 Informational
Scope
Overview
IVX engaged Guardian to review the security of its Options protocol. From the 4th of September to the 12th of September, a team of 6 auditors reviewed the source code in scope.
Findings 57
-
LP-1 Critical Required Margin Miscalculated Logical Error Pending
Description
When computing the position maintenance margin required, the maintenance margin is miscalculated because the
positionMaintenanceMarginis called with the option premium as the spot price of the asset and the spot price of the asset as the option premium.This drastically miscalculates the required
positionMaintenanceMarginand results in an invalidutilizationRatio, causing inflated or insufficient interest rates and undermines themaxUtilizationRatiovalidation.Recommendation
Provide the spot price as the X value and the premium as the Y value.
-
DIEM-1 Critical Liquidations Halted Due To DoS DoS Pending
Description
Proof of concept: PoC
There is no limit to the amount of trades a user may open in their portfolio. Therefore it is possible for a user to open so many trades that functions that need to iterate through all of them multiple times, such as
liquidation, cannot occur.The
maxBatchTradingvalidation fails to protect against this DoS as it limits only the amount of trades that can be opened in a singleopenTradesfunction call.Recommendation
Implement a cap on the amount of trades that can belong to any single portfolio.
-
DIEM-2 Critical Sell Premium Paid Twice Logical Error Pending
Description
Proof of concept: PoC
In the
openTradesfunction, when a user opens a!isBuytrade their portfolio receives thetotalPremiumimmediately.However upon settling and closing the expired trade, when the premium of the expired option is 0 the
_trade.averageEntry.mulDivUp(closedUnits, 1e18), which represents thetotalPremiumupon opening the sell trade, is credited as PNL in the_calculatePnlfunction and later transferred to the user’s portfolio a second time.Recommendation
Do not credit the initial
totalPremiumas PNL when the sell trade is closed as this amount has already been paid out. -
PORT-1 Critical swapMargin Used To Game The AMM Griefing Pending
Description
Proof of concept: PoC
Users are able to use the
swapMarginfunction to siphon funds from their portfolio well past the point of insolvency.- A user can sell options contracts to the AMM and collect the premium in their portfolio immediately.
- The user can then repeatedly use the
swapMarginfunction on their portfolio and sandwich themselves to extract roughly all the value from the portfolio. - The user retains their initial margin deposit as well as the premium from selling the options contracts to the AMM, without having any margin remaining in their portfolio to exercise these contracts upon expiry.
A malicious user can use this attack to drain nearly all of the available funds in the
IVXLPcontract, after removing the balance from their portfolio they will then be insolvently liquidated.Recommendation
Consider removing the ability to swap margin balances as it poses an inherent risk to the system. Otherwise add validation that the portfolio is not liquidatable at the end of
swapMarginfunction:if (IIVXDiem(diemContract).isPortfolioLiquidatable(this)) revert IVXPortfolio_PortfolioLiquidatable();
-
DIEMT-1 Critical DeltaT Rounds Down To 0 Bricking Trades Rounding Pending
Description
Proof of concept: PoC
secondsToExpiryis divided by 15 to producedeltaT. Options with <15 seconds of expiry will havedeltaTrounded down to 0 and will cause functions such ascalculateCosts(),interestRate()andutilizationRatio()to revert.The above functions call
Binomial._optionPriceswhich calculates N period for binomial pricing:uint256 N = inputs.secondsToExpiry / inputs.deltaT;
Because
inputs.deltaTis 0 after rounding down, a division by 0 revert occurs. While the option is included in theactiveOptionIds, all other options will be impacted:interestRate()andcalculateCosts()are needed forpnlOperations, which will completely prevent closing and liquidation trades from occurring due to the revert. This will negatively impact liquidity providers and traders.calculateCosts()andutilizationRatio()are called when opening a trade, completely preventing trades from being opened in the protocol.
Recommendation
Use the default
inputs.deltaTvalue to prevent N from becoming 0. Furthermore, carefully monitor expired options and ensure they are settled to prevent bloating the active options list. -
PORT-2 Critical Incorrect Approval Prevents Liquidation Logical Error Pending
Description
In the
removeMarginfunction, before a swap occurs an amount is approved. This is supposed to be the token amount that is being swapped.However the
USDvalue of the token amount is approved instead. This is especially detrimental when a token’sUSDvalue is less than $1 because the approval will be insufficient for the swap causing a revert and making liquidations viaremoveMarginimpossible.Recommendation
Approve the token amount that will be swapped instead of the
USDamount. -
DIEM-3 Critical Liquidation will fail due to insufficient funds Logical Error Pending
Description
In the
liquidatefunction when a position is insolvently liquidated thePnLamount transferred to theIVXLPcontract is determined to be the total balance of the portfolio in a dollar amount.If a token in the portfolio is not
USDC, it will be swapped forUSDCand incur a fee as well as slippage before theUSDCamount is received in theDiemcontract.Because the Diem contract receives less
USDC, it will not have sufficient funds to transfer to all necessary stakeholders. The lack of funds will lead to the transaction reverting, making it impossible to liquidate a position that requires a swap.Recommendation
Use the amount received after
liquidate()to perform the liquidation during insolvent closes. This will ensure that there are enough funds to finish execution. -
DIEMT-2 High vegaDifference Fee Not Valued At The Asset Price Logical Error Pending
Description
In the
_calculateFeefunction, thefeeTakenis incremented by thevegaDifferencefactored with theVEGA_MAKER_FACTORorVEGA_TAKER_FACTOR.However the
vegaDifferenceis not valued at the underlying asset price withOracle.getValuePricedlike thedeltaDifferenceis. Therefore the additional fees from thevegaDifferenceare negligible.Because of this, the effect that the action has on the amm’s net vega exposure is not accounted for in the calculated fees and the fees are significantly cheaper than intended.
Recommendation
Calculate the fees from the
vegaDifferencewith Oracle.getValuePriced(vegaDifference.mulDivUp(MakerTakerFactors[_asset].VEGA_MAKER_FACTOR, 1e18), _asset). -
RSKE-1 High Borrowing Fees Accounted For Twice Logical Error Pending
Description
The borrowing fees are included in the
maintenanceMarginForPositionswhich is the numerator for the health factor. However the borrowing fees are also deducted from the PnL of the position in thecalculatePnlfunction, therefore reducing the denominator of the health factor.Therefore the effect of the borrowing fees is doubled when determining the health factor of positions, leading to positions being errantly liquidated.
Recommendation
Adjust the PnL of a trade such that it does not include the borrowing fee, or remove the borrowing fees from the
maintenanceMarginForPositions. -
LP-2 High hedgerTotalLiq Errantly Counted As Debt Logical Error Pending
Description
The
hedgerTotalLiqrepresents the present value of the GMX positions belonging to the protocol. This amount is an asset for liquidity providers in addition to theNAVamount held in the contract.This is in contrast to the other two variables in the numerator of the
utilizationRatio, theutilizedCollateralis a loaned amount andMMis a margin maintenance amount. ThehedgerTotalLiqis not an amount of theNAVthat is being utilized, but rather an additional amount of value belonging to the LPs.Therefore including the
hedgerTotalLiqin the numerator of theutilizationRatiomisrepresents the ratio of assets being utilized in theIVXLP.Recommendation
Do not include the
hedgerTotalLiqin the numerator of theutilizationRatio, instead consider factoring it into theNAVor including it in the denominator of theutilizationRatio. -
LP-3 High Users Can Withdraw Reserved Utilized Collateral Logical Error Pending
Description
The utilized collateral amount is not factored in when users are depositing and withdrawing, additionally, positions may remain open for a significant amount of time after they are expired. Therefore the utilized collateral can surpass the
NAV.As a result, the
totalAvailableAssetsview function will underflow panic revert and theutilizationRatiowill exceed 100% and perturb the interest rate calculations.Recommendation
Refactor the option settlement logic such that utilized collateral is reduced to 0 at the end of an epoch when withdrawals are executed.
-
DIEM-4 High Users Can Avoid Borrowing Fees Logical Error Pending
Description
When users pay borrowing fees, the current interest rate is computed from the
IVXLPcontract and projected across the period from[trade.timestamp, block.timestamp]. However the interest rate is variable and will not have been this same value for that entire period.Users can update their positions when the interest rate drops to lock in the lower rate for the
[trade.timestamp, block.timestamp]period, even though the interest rate was in fact higher during the majority of that period.As the interest rate is dependent on the utilized collateral, a malicious actor can wait until another trader closes their
isBuy == truetrade and decreases the interest rate to then update their own trade to lock in the lower rate. Additionally, a malicious actor could front-run other user’s who are closing their trades and increase the interest rate to cause grief.Recommendation
Refactor the method used to track borrowing fees, such as a per-size approach: Trades are marked with an initial
borrowingFeePerSizeand upon closing a trade the borrowing fees are computed as the delta between the trade’slatestBorrowingFeePerSizeand the currentborrowingFeePerSize.The
borrowingFeePerSizeis updated according to the previous interest rate over the previous period whenever the interest rate is changed. -
DIEM-5 High mulDivUp Leads To Unliquidatable Position Rounding Pending
Description
In the
_closeTradefunctionmulDivUpis used to compute the borrowed amount to be subtracted from the utilized collateral. In some cases, when a trade is closed in multiple transactions the resulting decrease of the utilized collateral will be greater than the corresponding increase that opening that trade incurred.Therefore in some cases the computed
borrowedAmountto decrease may be greater than the currentutilizedCollateralamount.When the computed
borrowedAmountis greater than theutilizedCollateralin theIVXLPcontract, the transaction will underflow revert and prevent the position from being closed. Malicious actors can leverage this to halt liquidations and grief other users, preventing positions from being closed.Recommendation
Refactor the
subUtilizedCollateralfunction such that if the provided_amountvalue is greater than the existingutilizedCollateralthe function does not revert but instead assigns theutilizedCollateralto 0. -
PORT-3 High swapOnUniswap has 0 slippage protection slippage Pending
Description
The
removeMarginandliquidatefunctions have no slippage protection and are therefore vulnerable to sandwich attacks.These actions can be sandwiched by MEVers to extract a significant amount of value from both users and the AMM.
Recommendation
Allow users to provide a slippage tolerance when they are initiating an action that should have a slippage tolerance.
Upon liquidations, consider refactoring the design such that liquidators are able to seize the assets and transfer them to the IVXLP contract without swapping them. The seized assets can be swapped in a separate transaction.
-
RSKE-2 High Expired Options Increase Maintenance Margin Logical Error Pending
Description
In the
healthFactorfunction, thepositionMaintenanceMarginis added for options which may be expired, however expired options should not increase the required margin for an account — their result is already factored into PnL and that result cannot change.Recommendation
Skip expired options for the
positionMaintenanceMargincalculation. -
PORT-4 High Supported Token Removal Gamed Protocol Manipulation Pending
Description
When a token is removed from the
assetsArraywith theremoveAssetfunction, it can no longer be used as margin when a portfolio is liquidated.When the owner calls the
removeAssetfunction, a malicious actor can front-run the transaction and make a large trade using the soon-to-be-removed token as margin. After the admin's transaction goes through, the attacker can freely withdraw the now unsupported token through thewithdrawAssetsfunction.When the portfolio is attempted to be liquidated, the liquidation will fail as the
PnLis assigned to the dollar value of the portfolio, which is 0, and thetotalFeeis attempted to be subtracted from thePnL.Recommendation
Only remove supported tokens when options are not tradeable or the protocol is paused.
-
GLOBAL-1 Medium Unlimited Centralized Controls Centralization Pending
Description
Throughout the codebase critical values are allowed to be set without appropriate limits on the configured values.
Some of these critical values include:
collateralFactorFEE_TAKEN_PROFITSdeltaCutoffwithdrawFeelimitProcessinterestRateParams
Recommendation
Implement limits on the range of valid values for each variable.
-
BIN-1 Medium Option May Have Time Value But Zero Premium Logical Error Pending
Description
An option’s premium is calculated to be
Premium = Time Value + Intrinsic Value. Consequently, even if an option is out-the-money (no intrinsic value), if there is still time until expiration the premium should be non-zero.Due to the specific 15 periods and volatility factors used for binomial calculation, it is possible for all of the terminal payoffs to be zero and result in the option’s premium to be zero.
- Trader may buy a contract for 0 premium and then sell it as price moves in their favor allowing for nearly risk-free trades.
- Health Ratio Not Validated Correctly
- Put-Call Parity Equation Is Invalidated
Recommendation
Consistently monitor volatility parameters and consider increasing how many periods are used for Binomial options pricing.
-
DIEM-6 Medium PNL Always Decreased By borrowedAmount Logical Error Pending
Description
In the
_calculatePnlfunction, for expired buy trades the PNL is decreased by the entireborrowedAmountno matter theclosedUnitsprovided. This is fine during the_closeTradefunction call as the_amountContractsis set to the entiretrade.contractsOpen, however other functions that rely on the_calculatePnlfunction do not make this adjustment.For example the
calculatePnlfunction does not make such an adjustment and therefore can give results that may be used to manipulate systems interacting with IVX and relying on the returned value.Recommendation
Replace
structured.PNL -= int256(_trade.borrowedAmount)withstructured.PNL -= int256(_trade.borrowedAmount.mulDivUp(closedUnits, _trade.contractsOpen)).
Otherwise make the
closedUnits = _trade.contractsOpenadjustment when the option is expired in thecalculatePnl viewfunction. -
DIEM-7 Medium Borrowing Fees Extend Past Option Expiry Unexpected Behavior Pending
Description
When a trader closes a trade their borrowing fees are computed based on the period from the
trade.timestampto the currentblock.timestamp. However, when an option expires the trader will be closing the trade at a timestamp that is past the expiry time of the option.Therefore borrowing fees accrue for the option even past it’s expiry, when the result has already settled and cannot change. The segment of time between the option expiry and the timestamp of the block in which the trader closes the trade should not be factored when the borrowing fees are computed.
Recommendation
Compute borrowing fees for the period where the option was tradeable and not expired.
-
DIEM-8 Medium averageEntry Always Rounds Up Rounding Pending
Description
In the
_openTradefunction, when theaverageEntryis computed with an earlier trade,mulDivUpis used. However, a higheraverageEntryis more beneficial for contracts whereisBuy == false. Malicious traders are therefore able to increase their resulting PNL by splitting their trades up and abusing the round up behavior.Recommendation
Round up for
isBuy == trueand round down forisBuy == false. -
DIEMT-3 Medium Errant Fee Applied Logical Error Pending
Description
The settlement fee is taken on the premium based on the
FEE_TAKEN_PROFITS, however this calculated amount is not all profit forisBuycontracts and is in fact all loss for!isBuycontracts.Recommendation
Do not fee this amount if it represents a loss, additionally compute the fee after the
PNLhas been determined, this way true profits are feed with theFEE_TAKEN_PROFITSamount. -
QUEUE-1 Medium Missing queuedTimestamp Update Logical Error Pending
Description
The
reduceQueuedDepositfunction neglects to update thequeuedTimestampof thequeuedDeposit, however in thereduceQueuedWithdrawalfunction thequeuedTimestampis updated. Any systems relying on this information would be misinformed as thequeuedTimestampis not correctly updated.Recommendation
Update the
queuedTimestampin thereduceQueuedDepositfunction for consistency. -
QUEUE-2 Medium Withdrawal Fees Can Be Gamed Protocol Manipulation Pending
Description
The accumulated fees from withdrawals during an epoch are distributed to the LP contract after all withdrawals and deposits for that epoch have taken place. This means that the depositors in epoch 10 will receive at least a share of the withdrawal fees from the withdrawals that happened in the same epoch number 10.
This way a profit seeking depositor can observe that many withdrawals are queued for the current epoch and queue a deposit right before the epoch ends to collect these withdrawal fees from individuals who withdrew in the same epoch.
Recommendation
Distribute the withdrawal fees to the depositors who remained in the vault after withdrawals are processed, but before deposits are processed for the current epoch.
-
LP-4 Medium utilizationRate May Exceed Max Value Logical Error Pending
Description
It is possible for the
utilizationRatioto exceed themaxUtilization, as the premium value of traded options and the liquidity in hedged positions changes over time. In this scenario theutilizationRatiofunction will return a ratio greater than themaxUtilization, perturbing the interest rate calculations in theinterestRatefunction.Recommendation
Return the
interestRateParams.MaxUtilizationif theutilizationRatioexceeds it.// (utilized collateral + MM + money on gmx) / NAV + if(ConvertDecimals.convertTo18(ConvertDecimals.convertFrom18AndRoundUp + (_utilizedCollateral + MM + hedgerTotalLiq, _decimals) + .mulDivUp( + 10 ** _decimals, NAV_Priced), _decimals) >= interestRateParams.MaxUtilization) + return interestRateParams.MaxUtilization
return ConvertDecimals.convertTo18( ConvertDecimals.convertFrom18AndRoundUp(_utilizedCollateral + MM + hedgerTotalLiq, _decimals).mulDivUp( 10 ** _decimals, NAV_Priced ), _decimals );
-
RSKE-3 Medium Incorrect X and Y in positionMaintenanceMargin Logical Error Pending
Description
When computing the margin
positionMaintenanceMarginfunction, the X and Y values are in an incorrect order.According to documentation:
Maintenance Margin=a * Max(b * X+c * Y ; d * Y+e * X)Recommendation
function positionMaintenanceMargin(uint256 X, uint256 Y, address _asset) public view returns (uint256 margin) { AssetAttributes memory asset = assetAttributes[_asset]; margin = asset.marginFactors.marginFactorA.mulDivUp( Math.max( (asset.marginFactors.marginFactorB * X) + (asset.marginFactors.marginFactorC * Y),
- (asset.marginFactors.marginFactorD * X) + (asset.marginFactors.marginFactorE * Y) +
- (asset.marginFactors.marginFactorD * Y) + (asset.marginFactors.marginFactorE * X) ), 1e36 ); }
-
DIEM-9 Medium Fees Apply To Insolvent Liquidations Logical Error Pending
Description
In cases where a portfolio is insolvently liquidated the fees are still distributed at their original value. This can lead to positions being unable to get liquidated in the event that the
portfolioDollarMarginis less than thetotalFee. Though this case may be rare, it is possible with high borrowing fees across many positions and should be handled.Recommendation
Cap the fees to what is payable in the event of an insolvent liquidation, otherwise consider ignoring them entirely for insolvent liquidations.
-
GLOBAL-2 Medium Inherent AMM Risk Protocol Risk Pending
Description
The AMM currently only hedges to be delta neutral. However, it is still vulnerable to volatility risk as the AMM is not vega neutral. Inherently as part of the hedging process, the AMM will have to buy at higher prices and sell at lower prices to maintain delta neutral status. Alongside the volatility in the market, there is a risk that the AMM may not have positive expected value.
Recommendation
Carefully monitor AMM status and increase fees when necessary to protect against losses due to vega non-neutrality.
-
OCL-1 Medium Risk-Free Trade by Sandwiching Volatility Updates Race Condiion Pending
Description
When
setStrikeVolatility()function is called, it presents an opportunity for an attacker to see that a volatility change will occur and sandwich attack the transaction. In this sandwich attack, the attacker will front-run the volatility change with a buy and then back-run the volatility change with a sell.By sandwich attacking the transaction, the attacker can profit from the volatility change without exposing themselves to any risk of a price change. This will be a risk-free trade for the attacker at the expense of the LPs.
Recommendation
Increase fees to ensure that the profits from the attack will be less than the fees incurred. Otherwise consider implementing a two-step execution for trading options on the exchange, where a keeper performs the execution of a trade on the behalf of a user.
-
DIEM-10 Medium Liquidation Bonus Comes From The Protocol Logical Error Pending
Description
In the
liquidate()function when a user’s portfolio is liquidated, aliqBonusis given to themsg.senderwho initiates the liquidation.However the
liqBonusis subtracted from thepnlamount which is to be transferred to theIVXLPcontract. Instead theliqBonusought to be deducted from the user’s remaining portfolio amount if there are leftovers.Recommendation
Deduct the
liqBonusfrom the user’s remaining margin amount if it is sufficient rather than deducting it from the amount that theIVXLPcontract will receive. -
COND-1 Medium Rounding Down Causes Traders Loss Rounding Pending
Description
Once a trader sells an option, their balance is expected to be increased by the option’s premium relative to the number of contracts they sold. However, due to the conversion of 18 decimal precision to the precision of the asset, it is possible for a seller to receive no payment for taking on the risk of selling an option.
if (assetDecimals < 18) { // Taking the ceil of 10^(18-decimals) will ensure the first n (asset decimals) have precision when converting amount = Math.floor(amount, 10 ** (assetDecimals)); }
Any amount below 1 whole unit of an asset will round down to 0, leading to no funds gained on
transferCollateral.Recommendation
Enforce a minimum amount of contracts to be traded to avoid rounding issues.
-
RSKE-4 Medium Contract can be initialized many times Access Control Pending
Description
In the
IVXRiskEnginecontract theinitializefunction lacks aninitializermodifier or any other means to limit the initialization to a single instance.Therefore the owner may initialize the contract multiple times and change key addresses that otherwise should not change after the
IVXRiskEngineis in use.Recommendation
Add validation that the
initializefunction in theIVXRiskEnginecannot be called multiple times. -
GLOBAL-3 Medium Liquidation Will Fail if Oracle is Down Unexpected Behavior Pending
Description
In extreme cases, like when oracles go offline or token prices drop to zero, liquidations can get stuck. This poses serious risks to the protocol's financial health.
During these times, it's crucial to allow liquidations to keep the protocol solvent. However, any liquidation-related actions will fail for debt holders of the affected token. For example, Chainlink has stopped their oracles in rare situations, such as the UST collapse, to avoid giving wrong data to protocols.
If a token's value crashes or the oracle system breaks down, trying to use the
liquidatefunction will fail. This is because it depends on the oracle's price information. As a result, users with the affected asset won't face liquidations. This can weaken the protocol's response to solvency issues. There's a risk that a user's asset value could drop below their debts. This would remove any reason to liquidate and push the protocol closer to financial trouble.Recommendation
Ensure there is a safeguard in place to protect against this possibility. Such as a backup oracle.
-
OCL-2 Medium Missing Grace Period Check Validation Pending
Description
The
getOraclePricefunction lacks grace period validation, therefore if any specific feed is not updated within the grace period a stale price could be used.Recommendation
Implement a grace period check for the
getOraclePricefunction. -
DIEMT-4 Medium Alpha Calculation Unused Unused Feature Pending
Description
The expiry of an option cannot exceed the end of a queue epoch:
//if option expiry bigger than the lp queue next epoch, dont allow creation if (_option.expiry > IIVXQueue(LP.queueContract()).nextEpochStartTimestamp()) { revert CannotCreateOptionWithExpiryAfterNextEpoch(); }
Because of this coupling, the expiry of an option is currently limited to 1 day after creation time. This renders any alpha calculation and price blending mechanism useless, as the cutoff of 4 days is never reached. If the blending mechanism were to be used, depositors and withdrawers would have to wait 4 days before depositing/withdrawing funds from the LP.
Recommendation
Consider adjusting the price blending formula and modify the epoch duration appropriately.
-
LP-5 Medium First Depositor Inflation Attack Protocol Manipulation Pending
Description
The
IVXLPvault is susceptible to the first deposit inflation attack.- Bob calls
addLiquiditywith 1 wei and then the queue is processed. - Bob observes Alice's
addLiquiditycall in the mempool for 100 tokens and frontruns it by
transferring 100 tokens directly to the vault to inflate the
NAV. 3) Once the queue is processed, Alice will mint 0 shares but Bob's 1 share is now worth the entire balance of the vault.Although this is less likely because only the queue contract can call
mintandburn, the epoch duration is variable and it is a potential risk.Recommendation
Consider creating “dead” shares by burning some shares on the first deposit or tracking LP balance internally.
- Bob calls
-
PORT-5 Medium Liquidation Can Fail Due to Rounding Rounding Pending
Description
In the
removeMarginfunction, theamountToRemoverounds up when being transferred. The issue with this is that by rounding up, it is possible foramountToRemoveto be greater than the available balance.This will cause the transfer to revert and potentially prevent liquidations via the
removeMarginfunction when the effective margin of the asset is at 100%.Recommendation
When converting from the price amount to the token amount, do not round up.
-
EXNG-1 Medium Fixed Pool Can Lead to Bad Swaps Logical Error Pending
Description
Uniswap pools with the same token pair are differentiated by their configured fee tier. Currently, the
poolFeeis a constant, which means that swaps of that token pair can only occur in the specific pool that has that fee tier.If liquidity is low in this pool, swaps will occur with a larger price impact than they would otherwise in a different fee tier pool. This will lead to users and the protocol losing funds on swaps unnecessarily.
Recommendation
Allow
poolFeeto be changed so that swaps can happen in the most advantageous pool. -
DIEM-11 Medium Rounded Funds Stuck in Diem Contract Logical Error Pending
Description
The function
convertFrom18AndRoundDownrounds down by subtracting a value determined byreturn x - (x % assetDecimals). This deducted amount is then left in theIVXDiemcontract.While the amount being locked in the contract is not large, there is no way to access these funds, and each time
convertFrom18AndRoundDownis called in theIVXDiemcontract, funds will be locked.Recommendation
Excess funds that remain after conversions should be claimable by the owner of the portfolio or by the protocol.
-
QUEUE-3 Medium Malicious User Can Push Deposits DoS Pending
Description
A malicious user can prevent the epoch from rolling over by calling
addLiquiditywith multiple 1 wei positions past thelimitProcess.This will cause the admin to have to execute multiple transactions to process the queue and expend a potentially significant amount of gas.
Recommendation
Add a minimum deposit amount and consider adding a deposit fee to dissuade these manipulations
-
OCL-3 Medium Impossible to Close Option DoS Pending
Description
During the expiration of an option, it should is finalized using the
settleOptionsExpiredfunction. Within this function, thegetSpotPriceAtTimemethod is invoked, which contains an unbounded loop.If this loop runs for an extended period, it can exhaust more gas than what's permissible in a single transaction, rendering the option impossible to close. The loop's duration is determined by the number of rounds that transpire between the option's expiration and the invocation of
settleOptionsExpired.For tokens with high volatility, the number of rounds can escalate rapidly, potentially leading to a Denial of Service (DoS) situation sooner than anticipated.
Recommendation
Closely monitor the closing of options and ensure the function is called with adequate time before a DoS is possible.
-
EXNG-2 Low No Price Limit on Swaps Logical Error Pending
Description
There is no price limit set when swaps are performed. With no price limit, a swap can move the price to any amount. This will be especially noticeable when making large trades or when a swap goes through a less liquid pool.
Including traditional slippage protection is a higher priority, but implementing a price limit gives users another way to control slippage.
Recommendation
Consider implementing a price limit that is either set at a fixed percentage of the current price or allow users to choose their own price limit.
-
DIEMT-5 Low ID of 0 is valid for the idModule modifier Validation Pending
Description
The
idModulemodifier allows ids of 0 to pass validation, however an id of 0 is certainly not valid for an option as there are no previous buy/sell call/put combinations.Impact is limited as all functions utilizing the
idModulewill revert for various reasons upon receiving an id of 0.Recommendation
Consider specifically disallowing an id of 0 for options in the
idModulemodifier. -
PORT-6 Low averageEntry is not being updated on full close trade. Logical Error Pending
Description
Upon closing a trade with the
closeTradefunction, the average entry is not assigned to zero. This may result in unexpected behavior for systems relying on this piece of state.Recommendation
function closeTrade(uint256 _optionId) external onlyAllowedContract(diemContract) { IIVXDiem.Trade memory traded = optionIdTrade[_optionId]; traded.timestamp = 0; + traded.averageEntry = 0; traded.contractsOpen = 0; traded.borrowedAmount = 0; optionIdTrade[_optionId] = traded;
//remove optionId from openOptionIds uint256 openOptionIdsLength = openOptionIds.length; for (uint256 i; i < openOptionIdsLength; ++i) { if (openOptionIds[i] == _optionId) { openOptionIds[i] = openOptionIds[openOptionIdsLength - 1]; openOptionIds.pop(); break; } } }
-
QUEUE-4 Low Unnecessary block.timestamp Emitted Optimization Pending
Description
The
block.timestampis unnecessary to emit in the event as it can be retrieved from the block in which the event was emitted.Recommendation
Remove the timestamp from the
DepositQueuedandWithdrawQueuedevents. -
OCL-4 Low Misnamed Variable Typo Pending
Description
In the
setValuesfunction, the parameter ofEncodedDatais labeled asdecodedData.Recommendation
Either change the name of the
EncodedDatastruct or rename thedecodedDatavariable. -
DIEMT-6 Low EnumerableSet Should Be Used Improvement Pending
Description
Throughout the
IVXDiemTokencontract error prone logic is used to add and remove items from theunderlyingsarray as if it were a set.Recommendation
Avoid this error prone logic and use OpenZeppelin’s
EnumerableSetfor theunderlyings. -
QUEUE-5 Low Superfluous Processed Check Superfluous Code Pending
Description
Checking the
depositsProcessedandwithdrawalsProcessedin theprocessCurrentQueuefunction is superfluous as they can only every be assigned to true together in the_rolloverEpochfunction where thecurrentEpochIdis incremented such that thisepochDatawill never be used again in theprocessCurrentQueuefunction.Recommendation
Remove the unnecessary
depositsProcessedandwithdrawalsProcessedchecks. -
QUEUE-6 Low Inefficient Use Of Storage Variable Optimization Pending
Description
A stack variable is stored for the
currentEpochIdstorage variable, however thecurrentEpochIdin storage is still referenced on line 128.Recommendation
Use the
_currentEpochIdstack variable on line 128. -
QUEUE-7 Low Missing Events For reduceQueued Functions Events Pending
Description
The
reduceQueuedWithdrawalandreduceQueuedDepositfunctions lack emitted events to signify that the queued action has been reduced.Recommendation
Implement events for the
reduceQueuedWithdrawalandreduceQueuedDepositfunctions. -
DIEM-12 Low Typo Typo Pending
Description
The comment on line 246 reads
was already transfered to this contractwhere transferred is misspelled astransfered.Recommendation
Correct the spelling error.
-
DIEM-13 Low Blacklist Warning Blacklist Pending
Description
Funds are pushed to the
treasury,staker, andlpaddresses every time a fee is taken. If any of these addresses are ever blacklisted for the collateral token, the protocol will be DoS’d.Recommendation
Be aware of this risk and have a contingency plan in place. Otherwise consider refactoring the logic such that funds can be pulled to non-critical addresses such as the treasury.
-
RSKE-5 Low assetMarginParams Set For An Unsupported Asset Validation Pending
Description
The
changeAssetMarginParamsfunction can be used to set attributes for an unsupported asset as there is not validation that the provided_assetis indeed supported.Recommendation
Add a requirement that the
supportedAssets[_asset]entry istrue. -
DIEMT-7 Low Parity Check Optimization Optimization Pending
Description
In the
createOptionfunction,i % 2 == 0is used to determine the parity ofi, howeveri & 1 == 0is a more efficient check.Recommendation
Use
i & 1 == 0to check the parity ofi. -
GLOBAL-4 Low Too Many Options Is Not A Good Thing Warning Pending
Description
The IVX Protocol often relies on enumerating all options contracts and specific trades to compute risk parameters such as the portfolio health factor and LP utilization rate. However this approach is constrained by the block gas limit and gas expenditure in general. Therefore limiting the reasonable amount of options and activity that the protocol can support.
Recommendation
Consider re-designing the architecture such that expensive computation does not have to occur for each option contract, and each trade in a user’s portfolio. Thereby avoiding
forloops as much as possible and removing expensive computation from theforloops that are necessary. -
RSKE-6 Low Inaccurate Variable Names Naming Pending
Description
Vega_shockLossis set to the delta shock value andDelta_shockLossis set to the vega shock value.if (SumDeltaShock_negative < SumDeltaShock_positive) { Vega_shockLoss = SumDeltaShock_negative; } else { Vega_shockLoss = SumDeltaShock_positive; } if (SumVegaShock_negative < SumVegaShock_positive) { Delta_shockLoss = SumVegaShock_negative; } else { Delta_shockLoss = SumVegaShock_positive; }
Recommendation
Switch the variable names to accurately reflect the values they represent.
-
QUEUE-8 Low Liquidity Amount Does Not Include Fee Logical Error Pending
Description
LP.withdrawLiquidityreturns the amount of liquidity withdrawn without accounting for the withdrawal fee.This amount is then stored in the mapping
depositEpochQueueand does not accurately reflect how much liquidity was returned to the depositor.Recommendation
Consider whether the amount after the fee is necessary, and if so set
_amountto_amount-_fee.
No findings match.
Put your code through the same review.
This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.
