Halborn Logo

DexodusV2 - Dexodus


Prepared by:

Halborn Logo

HALBORN

Last Updated 01/13/2025

Date of Engagement by: December 17th, 2024 - January 7th, 2025

Summary

100% of all REPORTED Findings have been addressed

All findings

21

Critical

0

High

0

Medium

3

Low

7

Informational

11


1. Introduction

Dexodus engaged our security analysis team to conduct a comprehensive security assessment of their smart contract ecosystem. The primary aim was to meticulously assess the security architecture of the smart contracts to pinpoint vulnerabilities, evaluate existing security protocols, and offer actionable insights to bolster security and operational efficacy of their smart contract framework. Our assessment was strictly confined to the smart contracts provided, ensuring a focused and exhaustive analysis of their security features.

2. Assessment Summary

Our engagement with Dexodus spanned a 2-week period, during which we dedicated one full-time security engineer equipped with extensive experience in blockchain security, advanced penetration testing capabilities, and profound knowledge of various blockchain protocols. The objectives of this assessment were to:

- Verify the correct functionality of smart contract operations.

- Identify potential security vulnerabilities within the smart contracts.

- Provide recommendations to enhance the security and efficiency of the smart contracts.

3. Test Approach and Methodology

Our testing strategy employed a blend of manual and automated techniques to ensure a thorough evaluation. While manual testing was pivotal for uncovering logical and implementation flaws, automated testing offered broad code coverage and rapid identification of common vulnerabilities. The testing process included:

- A detailed examination of the smart contracts' architecture and intended functionality.

- Comprehensive manual code reviews and walkthroughs.

- Functional and connectivity analysis utilizing tools like Solgraph.

- Customized script-based manual testing and testnet deployment using Foundry.

This executive summary encapsulates the pivotal findings and recommendations from our security assessment of Dexodus smart contract ecosystem. By addressing the identified issues and implementing the recommended fixes, Dexodus can significantly boost the security, reliability, and trustworthiness of its smart contract platform.

4. RISK METHODOLOGY

Every vulnerability and issue observed by Halborn is ranked based on two sets of Metrics and a Severity Coefficient. This system is inspired by the industry standard Common Vulnerability Scoring System.
The two Metric sets are: Exploitability and Impact. Exploitability captures the ease and technical means by which vulnerabilities can be exploited and Impact describes the consequences of a successful exploit.
The Severity Coefficients is designed to further refine the accuracy of the ranking with two factors: Reversibility and Scope. These capture the impact of the vulnerability on the environment as well as the number of users and smart contracts affected.
The final score is a value between 0-10 rounded up to 1 decimal place and 10 corresponding to the highest security risk. This provides an objective and accurate rating of the severity of security vulnerabilities in smart contracts.
The system is designed to assist in identifying and prioritizing vulnerabilities based on their level of risk to address the most critical issues in a timely manner.

4.1 EXPLOITABILITY

Attack Origin (AO):
Captures whether the attack requires compromising a specific account.
Attack Cost (AC):
Captures the cost of exploiting the vulnerability incurred by the attacker relative to sending a single transaction on the relevant blockchain. Includes but is not limited to financial and computational cost.
Attack Complexity (AX):
Describes the conditions beyond the attacker’s control that must exist in order to exploit the vulnerability. Includes but is not limited to macro situation, available third-party liquidity and regulatory challenges.
Metrics:
EXPLOITABILITY METRIC (mem_e)METRIC VALUENUMERICAL VALUE
Attack Origin (AO)Arbitrary (AO:A)
Specific (AO:S)
1
0.2
Attack Cost (AC)Low (AC:L)
Medium (AC:M)
High (AC:H)
1
0.67
0.33
Attack Complexity (AX)Low (AX:L)
Medium (AX:M)
High (AX:H)
1
0.67
0.33
Exploitability EE is calculated using the following formula:

E=meE = \prod m_e

4.2 IMPACT

Confidentiality (C):
Measures the impact to the confidentiality of the information resources managed by the contract due to a successfully exploited vulnerability. Confidentiality refers to limiting access to authorized users only.
Integrity (I):
Measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of data stored and/or processed on-chain. Integrity impact directly affecting Deposit or Yield records is excluded.
Availability (A):
Measures the impact to the availability of the impacted component resulting from a successfully exploited vulnerability. This metric refers to smart contract features and functionality, not state. Availability impact directly affecting Deposit or Yield is excluded.
Deposit (D):
Measures the impact to the deposits made to the contract by either users or owners.
Yield (Y):
Measures the impact to the yield generated by the contract for either users or owners.
Metrics:
IMPACT METRIC (mIm_I)METRIC VALUENUMERICAL VALUE
Confidentiality (C)None (I:N)
Low (I:L)
Medium (I:M)
High (I:H)
Critical (I:C)
0
0.25
0.5
0.75
1
Integrity (I)None (I:N)
Low (I:L)
Medium (I:M)
High (I:H)
Critical (I:C)
0
0.25
0.5
0.75
1
Availability (A)None (A:N)
Low (A:L)
Medium (A:M)
High (A:H)
Critical (A:C)
0
0.25
0.5
0.75
1
Deposit (D)None (D:N)
Low (D:L)
Medium (D:M)
High (D:H)
Critical (D:C)
0
0.25
0.5
0.75
1
Yield (Y)None (Y:N)
Low (Y:L)
Medium (Y:M)
High (Y:H)
Critical (Y:C)
0
0.25
0.5
0.75
1
Impact II is calculated using the following formula:

I=max(mI)+mImax(mI)4I = max(m_I) + \frac{\sum{m_I} - max(m_I)}{4}

4.3 SEVERITY COEFFICIENT

Reversibility (R):
Describes the share of the exploited vulnerability effects that can be reversed. For upgradeable contracts, assume the contract private key is available.
Scope (S):
Captures whether a vulnerability in one vulnerable contract impacts resources in other contracts.
Metrics:
SEVERITY COEFFICIENT (CC)COEFFICIENT VALUENUMERICAL VALUE
Reversibility (rr)None (R:N)
Partial (R:P)
Full (R:F)
1
0.5
0.25
Scope (ss)Changed (S:C)
Unchanged (S:U)
1.25
1
Severity Coefficient CC is obtained by the following product:

C=rsC = rs

The Vulnerability Severity Score SS is obtained by:

S=min(10,EIC10)S = min(10, EIC * 10)

The score is rounded up to 1 decimal places.
SeverityScore Value Range
Critical9 - 10
High7 - 8.9
Medium4.5 - 6.9
Low2 - 4.4
Informational0 - 1.9

5. SCOPE

Files and Repository
(a) Repository: dexodus
(b) Assessed Commit ID: 0684a17
(c) Items in scope:
  • src/DexodusV2/Position.sol
  • src/DexodusV2/User.sol
  • src/DexodusV2/FeeManager.sol
↓ Expand ↓
Out-of-Scope:
Remediation Commit ID:
  • 2a9423c
Out-of-Scope: New features/implementations after the remediation commit IDs.

6. Assessment Summary & Findings Overview

Critical

0

High

0

Medium

3

Low

7

Informational

11

Security analysisRisk levelRemediation Date
Negative Slippage Allowed During Position DecreaseMediumSolved - 01/10/2025
Arbitrary Price Band Width Update Causes Inconsistent Band MappingMediumSolved - 01/10/2025
Missing Position Validation for Orders and Potential DOS via Invalid OrdersMediumSolved - 01/10/2025
Unhandled Underflow in Modify Position During Size ValidationLowSolved - 01/10/2025
Missed Liquidations Due to Limited Price Band WindowLowRisk Accepted - 01/10/2025
Missing Basis Points Validation in Affiliate and Referral SettingsLowSolved - 01/10/2025
Potential Fee Calculation Underflow in Discount and Referral SettingsLowSolved - 01/10/2025
Empty Referral Name Allowed in Custom Name CreationLowSolved - 01/10/2025
Excessive Fee Configuration During Market InitializationLowSolved - 01/10/2025
Inconsistent Array Length Validation in Update and Set FunctionsLowSolved - 01/10/2025
Missing Parameter Validation in Public Modify Position FunctionInformationalAcknowledged - 01/10/2025
Inconsistent Fee Calculation for Decrease Position with Referrer RewardsInformationalAcknowledged - 01/10/2025
Inconsistent PRICE_BAND_WIDTH Across Contracts May Cause Data MismatchesInformationalAcknowledged - 01/10/2025
Duplicate Logic for Perpetual Price CalculationInformationalAcknowledged - 01/10/2025
Redundant Calls to Perpetual Price Calculation in Modify PositionInformationalAcknowledged - 01/10/2025
Inefficient Position Management in Price BandsInformationalAcknowledged - 01/10/2025
Duplicate Logic in Position Deletion and Liquidation ExecutionInformationalAcknowledged - 01/10/2025
Duplicated Verification and Logic Across Automation ContractsInformationalAcknowledged - 01/10/2025
Oversized Array Allocation for PositionsInformationalAcknowledged - 01/10/2025
Inconsistent Basis Points Across ContractsInformationalAcknowledged - 01/10/2025
TxType Could Be Using EnumsInformationalAcknowledged - 01/10/2025

7. Findings & Tech Details

7.1 Negative Slippage Allowed During Position Decrease

// Medium

Description

The _checkSlippage function does not properly validate slippage conditions when decreasing positions, particularly for long positions. The function applies different logic based on the isLong value, leading to inconsistent and potentially unsafe checks.

For long positions, a decrease in size may result in a negative slippage scenario that is not caught by the function. This allows position modifications with prices outside the acceptable slippage range, potentially resulting in unintended losses for users.

Proof of Concept

The provided POC demonstrates a scenario where a long position decrease with a requested price of 2605 does not revert even though the final price (2600) falls outside the acceptable slippage range:

   function test_decrease_no_slippage() public {
        vm.startPrank(owner);

        IERC20(usdcAddr).approve(futuresCoreAddr, IERC20(usdcAddr).balanceOf(owner));

        uint256 collateral = 100e6;
        uint256 size = 1000e6;

        uint256 indexPrice = 2300e6;
        bool isLong = true;
        bool isIncrease = true;
        bytes32 key = keccak256(abi.encode("ETH/USD"));

        console.log("Index price", indexPrice);
        console.log("Perpetual price", futuresCore.getPerpetualPrice(key, indexPrice));
        console.log("Perpetual price position", futuresCore.getPerpetualPriceForPosition(key, indexPrice, int256(size), isLong));

        futuresCore.modifyPosition(owner, collateral, size, indexPrice, isLong, isIncrease, key, futuresCore.getPerpetualPrice(key, indexPrice), 1000, true);

        collateral = 50e6;
        size = 1000e6;
        indexPrice;
        indexPrice = 2600e6;
        isLong = true;
        isIncrease = false;
        key = keccak256(abi.encode("ETH/USD"));

        console.log("Index price", indexPrice);
        console.log("Perpetual price", futuresCore.getPerpetualPrice(key, indexPrice));
        console.log("Perpetual price position (-)", futuresCore.getPerpetualPriceForPosition(key, indexPrice, -int256(size), isLong));

        futuresCore.modifyPosition(owner, collateral, size, indexPrice, isLong, isIncrease, key, futuresCore.getPerpetualPrice(key, indexPrice), 0, true);

        vm.stopPrank();
    }

This behavior arises because the _checkSlippage function treats long and short positions differently instead of applying a unified range check.

POC output
BVSS
Recommendation

Update _checkSlippage to use a standardized range validation for both long and short positions. For example:

if (finalPrice > value2max || finalPrice < value2min) {
    return false;
}

This approach eliminates the need for separate logic paths based on isLong and ensures consistent validation for all scenarios, including position decreases.

Additionally, re-test the POC after applying the fix to confirm that slippage validation properly rejects prices outside the specified range.

Remediation

SOLVED: _checkSlippage is now performing validation on the range independently of the position those considering the reduction case for a long position.

Remediation Hash
2a9423c22a20ceddb37bd7c4a166e686817373a6

7.2 Arbitrary Price Band Width Update Causes Inconsistent Band Mapping

// Medium

Description

The setPRICE_BAND_WIDTH function in Automation contracts allows arbitrary updates to the PRICE_BAND_WIDTH value.

Changing this value dynamically can lead to inconsistent band indexing for existing positions because:

  • Old positions remain mapped to bands calculated with the previous width, while new positions are mapped based on the updated width.

  • Positions that should be in separate bands may get merged into the same band, resulting in missed liquidations, incorrect executions, or skipped processing during upkeep.

Example:

  • Initial PRICE_BAND_WIDTH = 200, placing a position at index 2 (400).

  • Updated PRICE_BAND_WIDTH = 100, causing positions at 400 to map to index 4 (400/100).

  • Result: Old and new positions incorrectly share the same band, leading to unstable system behavior.

BVSS
Recommendation
  1. Remove setPRICE_BAND_WIDTH: Prevent runtime modifications to PRICE_BAND_WIDTH entirely, enforcing immutability for consistent indexing.

  2. Add Migration Function (Optional): If updates are required, introduce a migration process to recalculate and remap all positions to new bands. However, this approach may be gas-intensive and should be carefully considered.

  3. Off-Chain Band Management (Alternative): Consider managing band calculations off-chain and only submitting validated liquidation data on-chain, reducing complexity and gas usage.

Restricting dynamic changes or implementing migrations ensures system stability, data consistency, and reliable liquidations.

Remediation

SOLVED: The setPRICE_BAND_WIDTH function was removed.

Remediation Hash
2a9423c22a20ceddb37bd7c4a166e686817373a6

7.3 Missing Position Validation for Orders and Potential DOS via Invalid Orders

// Medium

Description

The order function does not validate the existence of a position for decrease orders (orderType != 0). This allows creating orders without a valid position, leading to:

  1. Underflow Errors: Invalid size or collateral can cause reverts during modifyPosition execution.

  2. DOS Vulnerability: Attackers can spam invalid orders, overloading the system and blocking valid ones.

BVSS
Recommendation

Add a position validation check for orderType != 0 to ensure the position exists and has sufficient collateral and size. This prevents invalid orders from being processed and reduces the risk of DOS attacks and underflows. Adding spam protection or rate limits can further secure the system.

Remediation

SOLVED: The Automation contracts are performing an existence check via the position key and its size.

Remediation Hash
2a9423c22a20ceddb37bd7c4a166e686817373a6

7.4 Unhandled Underflow in Modify Position During Size Validation

// Low

Description

The modifyPosition function does not validate whether the specified size parameter is greater than the actual position size, especially after calculating netPnL.

If a size larger than the current position size is used, an underflow can occur during the calculation of position.size and related operations. This could result in unexpected behavior, including contract reverts or inconsistencies in position states.

The issue is particularly concerning after the if (!liq) block, where netPnL, positivePnL, and takerFee adjustments are applied without validating that the remaining size is sufficient for the operation.

BVSS
Recommendation

Add a validation check to ensure the requested size does not exceed the current position size, accounting for positivePnL and takerFee. For example:

require(size <= position.size - takerFee - positivePnL, "Invalid size: exceeds position size");

This check should be placed after the netPnL calculation and before any updates to position.size. This approach prevents unhandled underflows and ensures that position modifications are executed safely.

Remediation

SOLVED: The check was added

Remediation Hash
2a9423c22a20ceddb37bd7c4a166e686817373a6

7.5 Missed Liquidations Due to Limited Price Band Window

// Low

Description

The checkCallback function in AutomationLiquidationsV2, AutomationOrdersV2, and similar contracts only checks for liquidations within a fixed 3-band window (current band and ±1 adjacent bands).

If the price moves rapidly and skips more than 2 bands, positions in skipped bands are not checked for liquidation, leading to:

  1. Missed Liquidations: Positions in skipped bands remain open even if they should be liquidated.

  2. Unstable System State: Unliquidated positions can lead to unrealistic profit/loss calculations, affecting system stability and user balances.

  3. Security Risks: Malicious actors could exploit rapid price changes to bypass liquidation checks and maintain unsafe positions.

BVSS
Recommendation
  1. Implement Dynamic Band Range Checking:
    Track the last checked price band and dynamically calculate the range of bands to check based on the current price band. Iterate over all bands between the last checked band and the current band.

  2. Cache Last Checked Band:
    Maintain a state variable to cache the last checked band, enabling dynamic band traversal even after rapid price changes.

This approach prevents missed liquidations, ensures system stability, and protects against edge-case exploits caused by rapid price movements.

Remediation

RISK ACCEPTED: Recommendations will be implemented in the near future. For now, missed liquidations and order executions caused by rapid price movements will be executed by the off-chain operators.

7.6 Missing Basis Points Validation in Affiliate and Referral Settings

// Low

Description

The updateUserAffiliate and setReferralsSettings functions do not validate that the affiliateReceives, referrerReceives, and hasReferrerDiscount values are within the expected basis points limit of 1e10.

This lack of validation can lead to:

  1. Overflow of Basis Points - Values greater than 1e10 may be incorrectly interpreted, leading to unrealistic percentages, miscalculations, or logical errors.

  2. Unintended Rewards or Discounts - Excessive rewards or discounts may be assigned, resulting in financial losses or unfair advantages for specific users.

BVSS
Recommendation

Add validation checks to enforce the upper limit for the provided values. For example:

require(affiliateReceives[i] <= 1e10, "Invalid affiliateReceives value");
require(_hasReferrerDiscount <= 1e10, "Invalid hasReferrerDiscount value");
require(_referrerReceives <= 1e10, "Invalid referrerReceives value");

These checks should be added at the beginning of the affected functions to prevent setting invalid values. This ensures consistency with the expected basis points format and avoids errors in calculations.

Remediation

SOLVED: The checks have been added.

Remediation Hash
2a9423c22a20ceddb37bd7c4a166e686817373a6

7.7 Potential Fee Calculation Underflow in Discount and Referral Settings

// Low

Description

The getVipLevelDiscount function returns a discount based on the user’s VIP level, and the hasReferrerDiscount applies an additional discount. However, there is no validation to ensure that their combined value does not exceed REFERRAL_BASIS_POINTS (1e10).

If the total discount surpasses this limit, it may lead to underflows during fee calculations in the _applyFeeDistribution function of the FuturesCore contract, potentially resulting in errors or unexpected behavior.

BVSS
Recommendation

Add checks to ensure that the sum of discounts, including VIP level and referrer discounts, does not exceed REFERRAL_BASIS_POINTS. It is also recommended to validate input values when setting discounts to prevent invalid configurations that may cause calculation errors.

Remediation

SOLVED: A check has been added to verify the finalFee.

Remediation Hash
2a9423c22a20ceddb37bd7c4a166e686817373a6

7.8 Empty Referral Name Allowed in Custom Name Creation

// Low

Description

The createReferralCustomName function allows an empty string as a referral name. This leads to the referrerNameToId mapping being set with the user’s referral ID under an empty key. Consequently, the empty string could be used as a valid name, potentially allowing conflicts, incorrect lookups, or bypassing name uniqueness checks.

BVSS
Recommendation

Add a validation check to ensure that the name parameter is not an empty string before processing the request. It is also advisable to enforce additional constraints, such as minimum length or valid character checks, to prevent invalid entries.

Remediation

SOLVED: A zero length check was added.

Remediation Hash
2a9423c22a20ceddb37bd7c4a166e686817373a6

7.9 Excessive Fee Configuration During Market Initialization

// Low

Description

When initializing a market in the initializeMarket function, the sum of tradingFeeToLPs and tradingFeeToTreasury is not validated to ensure it equals 100% (in basis points). If their combined value exceeds 100%, users may be charged more fees than expected when performing actions such as modifyPosition or causing overflows.

Additionally, if the sum is less than 100%, it could result in incorrect fee distribution, potentially leading to accounting discrepancies between the protocol and users.

BVSS
Recommendation

Add a validation check in the initializeMarket function to ensure that the sum of tradingFeeToLPs and tradingFeeToTreasury equals BASIS_POINTS (10,000). For example:

require(tradingFeeToLPs + tradingFeeToTreasury == BASIS_POINTS, "Fees must add up to 100%");

This enforces consistency in fee distribution and prevents overcharging or undercharging users during position modifications and other actions.

Remediation

SOLVED: The initializeMarket function is now verifying that the fees add up 100%.

Remediation Hash
2a9423c22a20ceddb37bd7c4a166e686817373a6

7.10 Inconsistent Array Length Validation in Update and Set Functions

// Low

Description

The updateUserVipLevel, updateUserAffiliate, and setVipLevelsDiscount functions fail to validate that the input arrays _users and vipLevel, _users and affiliateReceives, and vipLevel and discount have matching lengths. This can lead to the following issues:

  1. Incomplete Updates - If one array is shorter than the other, updates or assignments may be skipped, leaving some values uninitialized or unchanged.

  2. Reverts - If the first array (used for iteration) is longer, attempts to access out-of-bound indices in the second array can trigger runtime reverts, resulting in a denial of service.

Such behavior could lead to incomplete or inconsistent data states, which may be exploited to create discrepancies in user permissions or rewards.

BVSS
Recommendation

Add a validation step at the beginning of each function to ensure that all input arrays have the same length. For example:

require(_users.length == vipLevel.length, "Input arrays length mismatch");

Similar checks should be implemented for all affected functions. This ensures data consistency and prevents runtime errors during execution.

Remediation

SOLVED: Array length validation is not performed

Remediation Hash
2a9423c22a20ceddb37bd7c4a166e686817373a6

7.11 Missing Parameter Validation in Public Modify Position Function

// Informational

Description

The public modifyPosition function does not validate its input parameters, such as ensuring slippage is less than 10000 or checking other parameter ranges for validity.

Without proper validation in the public function, invalid parameters may pass through and require Chainlink automation to handle them. This results in unnecessary automation triggers, only for the transaction to eventually revert due to invalid inputs, wasting gas and resources.

Additionally, the lack of validation in the public function means that automations rely on redundant checks inside internal functions, which could be skipped if parameters were pre-validated.

BVSS
Recommendation

Validate all parameters in the public modifyPosition function to ensure they are within acceptable ranges before any further processing.

By enforcing these checks early, invalid transactions are filtered out without triggering automation, improving efficiency and reducing gas costs. Once validations are implemented in the public function, redundant checks inside automation handler functions can be safely removed.

Remediation

ACKNOWLEDGED: The Dexodus team acknowledged this finding.

7.12 Inconsistent Fee Calculation for Decrease Position with Referrer Rewards

// Informational

Description

In the modifyPosition function, within the !isIncrease && !liq block, the calculation of fees distributed to the Liquidity Provider (LP) and Treasury includes the usdcRewardAmount rewarded to the referrer.

This behavior differs from the isIncrease && !liq case, where only the takerFee is used to calculate LP and Treasury fees, excluding the usdcRewardAmount.

As a result, LP and Treasury fees are effectively applied to the referrer rewards, leading to inconsistent fee distribution logic and potentially higher fees than intended for users closing or reducing positions.

BVSS
Recommendation

Align the fee distribution logic in the !isIncrease && !liq case with the isIncrease && !liq case. Only use the takerFee (excluding usdcRewardAmount) when calculating fees for the LP and Treasury.

For example, update the logic to:

uint256 lpFee = takerFee * marketsState[key].tradingFeeToLPs / BASIS_POINTS;
uint256 treasuryFee = takerFee * marketsState[key].tradingFeeToTreasury / BASIS_POINTS;

USDC.safeTransfer(liquidityPool, lpFee);
USDC.safeTransfer(treasury, treasuryFee);

Ensure that fees are calculated consistently for both position increases and decreases, preventing excess charges on referrer rewards and maintaining logical consistency across the contract.

Remediation

ACKNOWLEDGED: The Dexodus team acknowledged this finding.

7.13 Inconsistent PRICE_BAND_WIDTH Across Contracts May Cause Data Mismatches

// Informational

Description

The AutomationPositionsV2 and AutomationOrdersV2 contracts allow setting different PRICE_BAND_WIDTH values independently.

This inconsistency can lead to:

  1. Mismatched Band Calculations: Orders and positions may be mapped to different bands, causing issues when deleting related orders tied to positions.

  2. Missed Deletions: Orders may not be correctly identified and removed, potentially leading to orphaned entries or stale data.

BVSS
Recommendation

Ensure synchronization of PRICE_BAND_WIDTH across related contracts.

  1. Factory Contract: Deploy AutomationPositionsV2 and AutomationOrdersV2 through a factory contract that initializes them with the same PRICE_BAND_WIDTH value.

  2. Immutable Band Width: Consider making PRICE_BAND_WIDTH immutable to prevent runtime changes that could lead to mismatches.

  3. Shared Storage (Optional): Store the PRICE_BAND_WIDTH in a shared storage contract or config module to enforce consistency without duplicating values.

This approach guarantees synchronized band calculations, reduces the risk of mismatched deletions, and improves data consistency across contracts.

Remediation

ACKNOWLEDGED: The Dexodus team acknowledged this finding.

7.14 Duplicate Logic for Perpetual Price Calculation

// Informational

Description

The getPerpetualPriceForPosition and getPerpetualPrice functions contain duplicate logic for calculating the perpetual price based on open interest (longOI and shortOI). This redundancy increases maintenance complexity and introduces a risk of inconsistent behavior if future changes are applied to only one of the functions.

Any adjustments to the perpetual price calculation logic would require updates in both functions, increasing the chance of errors or omissions.

BVSS
Recommendation

Refactor the code to introduce a shared internal function that accepts longOI and shortOI as parameters. Both getPerpetualPriceForPosition and getPerpetualPrice should delegate their calculations to this new function.

This approach centralizes the perpetual price logic, simplifying future updates and ensuring consistent behavior across the contract.

Remediation

ACKNOWLEDGED: The Dexodus team acknowledged this finding.

7.15 Redundant Calls to Perpetual Price Calculation in Modify Position

// Informational

Description

The modifyPosition function calls getPerpetualPriceForPosition multiple times with the same input parameters, leading to redundant computations. These repeated calls increase gas costs and reduce efficiency.

Since the parameters remain unchanged during execution, the same result is recalculated unnecessarily instead of being reused.

BVSS
Recommendation

Store the result of getPerpetualPriceForPosition in a temporary variable and reuse it throughout the function. For example:

uint256 perpetualPrice = getPerpetualPriceForPosition(key, indexPrice, int256(size), isLong);

Ensure that no parameter changes occur during execution that could affect the result. This optimization reduces gas consumption and improves performance without altering the logic.

Remediation

ACKNOWLEDGED: The Dexodus team acknowledged this finding.

7.16 Inefficient Position Management in Price Bands

// Informational

Description

The AutomationLiquidationsV2 contract uses a for loop to find and remove or update positions within priceBands during deletePosition and updatePosition. This approach is inefficient (O(n)) and can lead to higher gas costs and scalability issues as the number of positions grows.

BVSS
Recommendation

Add an index field to the PositionA struct to track the position’s index within its price band. This allows O(1) operations instead of looping through all positions.

Update the logic to:

  • Store the index when inserting a position.

  • Directly access and replace positions by index when deleting or updating them.

This approach improves efficiency, reduces gas costs, and scales better with larger datasets.

Remediation

ACKNOWLEDGED: The Dexodus team acknowledged this finding.

7.17 Duplicate Logic in Position Deletion and Liquidation Execution

// Informational

Description

The AutomationLiquidationsV2 contract contains duplicated logic between the following functions:

  1. Position Deletion: _deletePosition and deletePosition perform almost identical tasks for removing positions from storage.

  2. Liquidation Execution: _executePosLiquidations and executePosLiquidations duplicate the logic for processing liquidations.

This duplication increases code size, maintenance effort, and the risk of inconsistencies if updates are applied to one function but not the other.

BVSS
Recommendation

Refactor the contract to centralize shared logic into internal helper functions. This approach reduces redundancy, improves maintainability, and ensures consistent behavior across similar operations.

Remediation

ACKNOWLEDGED: The Dexodus team acknowledged this finding.

7.18 Duplicated Verification and Logic Across Automation Contracts

// Informational

Description

The checkCallback function in Automation contracts contains repetitive code for verification and data processing. This duplication:

  1. Increases Code Complexity: Makes it harder to read, maintain, and debug.

  2. Slows Updates and Fixes: Any fixes or enhancements require changes in multiple places, increasing the risk of inconsistencies.

  3. Logic Duplication Across Contracts: Contracts such as AutomationLiquidationsV2 and AutomationPositionsV2 share similar functionality but replicate logic instead of reusing code.

BVSS
Recommendation
  1. Extract Shared Logic into a Library or Internal Function:
    Move common logic (e.g., verification, price scaling, and report parsing) into a library or an internal utility function that can be reused across all contracts.

  2. Unify Shared Components Across Contracts:
    Refactor shared logic between Automation contracts into base contracts or shared modules to avoid duplication and improve maintainability.

  3. Enhance Readability and Testing:
    Simplify function implementation and improve test coverage by focusing on reusable components instead of repeatedly testing similar code blocks in different contracts.

This approach reduces redundancy, simplifies maintenance, and allows for faster updates and fixes while ensuring consistency across Automation contracts.

Remediation

ACKNOWLEDGED: The Dexodus team acknowledged this finding.

7.19 Oversized Array Allocation for Positions

// Informational

Description

In the checkCallback function, the line:

uint256[] memory positions = new uint256[](maxLength);

Allocates an array with a size of maxLength, which is calculated based on the number of positions across multiple bands. However, subsequent loops limit the j index to a maximum of 100 elements:

for (uint256 i; i < length && j < 100; ++i) {
    ...
}

This results in oversized memory allocation, wasting gas and memory space since only up to 100 positions are ever used.

BVSS
Recommendation

Replace the array size initialization with a fixed value of 100:

uint256[] memory positions = new uint256[](100);

This ensures memory allocation matches the maximum allowed elements, improving gas efficiency without affecting functionality.

Remediation

ACKNOWLEDGED: The Dexodus team acknowledged this finding.

7.20 Inconsistent Basis Points Across Contracts

// Informational

Description

Different contracts use inconsistent basis point scales:

  • FeeManager: Uses BASIS_POINTS_X10 = 1e6 and BASIS_POINTS = 1e5.

  • User: Uses REFERRAL_BASIS_POINTS = 1e10.

This inconsistency can:

  1. Confuse Integrators and Frontend Developers: Requires explicit awareness of the scale used in each contract.

  2. Cause Invalid Configurations: Incorrectly set values due to misunderstandings can lead to unexpected fee calculations and misconfigured discounts.

  3. Increase Maintenance Overhead: Adding or updating logic requires additional checks to ensure compatibility across contracts.

BVSS
Recommendation

Standardize all basis point definitions across contracts to use a single scale (e.g., 1e6). This simplifies integration, reduces configuration errors, and improves code consistency across the system.

Remediation

ACKNOWLEDGED: The Dexodus team acknowledged this finding.

7.21 TxType Could Be Using Enums

// Informational

Description

The txType variable in the modifyPosition function of the FuturesCore contract is defined as a uint256 to represent transaction types (e.g., 0 for open, 1 for increase, 2 for decrease, and 3 for liquidation). Using plain integers can lead to ambiguity, misinterpretation, and errors during development and debugging, as the code does not inherently enforce valid values or provide meaningful context for each type.

BVSS
Recommendation

Replace the txType variable with a typed enum to make the code more readable and type-safe. Enums enforce valid values and provide clear context, reducing the risk of passing invalid transaction types. Update all occurrences of txType assignments and comparisons accordingly to use the defined enum values.

Remediation

ACKNOWLEDGED: The Dexodus team acknowledged this finding.

Halborn strongly recommends conducting a follow-up assessment of the project either within six months or immediately following any material changes to the codebase, whichever comes first. This approach is crucial for maintaining the project’s integrity and addressing potential vulnerabilities introduced by code modifications.

// Download the full report

* Use Google Chrome for best results

** Check "Background Graphics" in the print settings if needed

© Halborn 2024. All rights reserved.