Skip to main content
Blazing Trading Suite - July 20, 2024

Blazing Trading Suite

Smart Contract Security Assessment

July 20, 2024

Blazing_Trading_Suite

SUMMARY


ABSTRACT

Dedaub was commissioned to perform a security audit of the** Blazing Trading Suite** protocol. The auditors reported 3 high-severity issues, 1 medium-severity issue, 6 low-severity issues, and 3 advisory issues. The auditors have also identified 4 protocol-level considerations that do not currently result in security issues but would harden the protocol further if addressed.


BACKGROUND

The Blazing Trading Suite protocol provides a wrapper around Uniswap V2 and Uniswap V3 pools, providing many useful convenience functions for interacting with these pools.

The codebase is divided into three parts, a minimal version of Blazing Trading Suite called helper, and more comprehensive versions of Blazing Trading Suite called V2 and V3, which interact with Uniswap V2 and V3 respectively.

The functionality provided by Blazing Trading Suite includes dispersing native and ERC20 tokens to different addresses, providing bribes for miners, checking the liquidity of pools, checking whether pools are honeypots and checking the taxes levied by the pools. Blazing Trading Suite also allows orchestrating multi-hop swaps, by specifying the amountIn or amountOut expected from the transaction. Blazing Trading Suite also supports the transfer of funds through the PERMIT2 scheme. Fees are collected by the protocol in return for using Blazing Trading Suite.

The protocol is highly optimised and uses low-level code to promote gas savings. The auditors found the code to be well-structured and well-documented.


SETTING & CAVEATS

This audit report mainly covers the contracts of the blazing-contract repository of the Blazing Trading Suite Protocol at commit 89970eea6047d9f9eeb9998543a118fde5a69ab6.

Two auditors worked on the codebase for 12 days on the following contracts:

Blazing-contract/src/
  • helper/
    • BlazingHelperV1.sol
    • BlazingHelperV2.sol
    • base/
      • Dispatcher.sol
      • HelperParameters.sol
      • LockAndMsgSender.sol
    • interfaces/
      • IBlazingHelper.sol
    • libraries/
      • BytesLib.sol
      • Commands.sol
      • Constants.sol
    • modules/
      • payments/
        • Payments.sol
        • PaymentsImmutables.sol
  • v2/
    • BlazingRouterV21.sol
    • BlazingRouterV22.sol
    • base/
      • Dispatcher.sol
      • LockAndMsgSender.sol
      • ProtocolTaxesManager.sol
      • RouterParameters.sol
    • interfaces/
      • IBlazingRouter.sol
      • IDexPairMinimal.sol
      • IWETH9.sol
    • libraries/
      • BytesLib.sol
      • Commands.sol
      • Constants.sol
      • Multichain.sol
    • modules/
      • dex/
        • DexHelper.sol
        • SwapRouter.sol
      • payments/
        • Payments.sol
        • PaymentsImmutables.sol
        • Permit2Payments.sol
      • protection/
        • Protection.sol
  • v3/
    • BlazingRouterV31.sol
    • BlazingRouterV32.sol
    • base/
      • Dispatcher.sol
      • LockAndMsgSender.sol
      • ProtocolTaxesManager.sol
      • RouterParameters.sol
    • interfaces/
      • IBlazingRouter.sol
      • IDexPairMinimal.sol
      • IWETH9.sol
    • libraries/
      • BytesLib.sol
      • Commands.sol
      • Constants.sol
      • Multichain.sol
    • modules/
      • dex/
        • DexHelper.sol
        • DexPath.sol
        • SwapRouter.sol
      • payments/
        • Payments.sol
        • PaymentsImmutables.sol
        • Permit2Payments.sol
      • protection/
        • Protection.sol

After the code was audited, the auditors examined fixes to the issues in this report contained in the following commit numbers:

  • fed07499c4d88e1293bee4e99abbec374d847755
  • b54ee152d73624ec8ff7a30e27a251fbb952b82a
  • 5b0ca96edab2abe9e63705ca2d811c65f80791411

Following this the auditors updated the report to reflect which issues were resolved and the manner in which they were resolved.

The audit’s main target is security threats, i.e., what the community understanding would likely call "hacking", rather than the regular use of the protocol. Functional correctness (i.e. issues in "regular use") is a secondary consideration. Typically it can only be covered if we are provided with unambiguous (i.e. full-detail) specifications of what is the expected, correct behavior. In terms of functional correctness, we often trusted the code’s calculations and interactions, in the absence of any other specification. Functional correctness relative to low-level calculations (including units, scaling and quantities returned from external protocols) is generally most effectively done through thorough testing rather than human auditing.


PROTOCOL-LEVEL CONSIDERATIONS

The following protocol-level considerations represent some general observations about the protocol. While the auditors did not link these to an immediate threat to the protocol, we suggest that the team consider the following best practice to further harden the protocol and reduce its possible attack surface.

P1

No whitelisting functionality for pools which Blazing Trading Suite interacts with

PROTOCOL-LEVEL-CONSIDERATION
dismissed
P1
No whitelisting functionality for pools which Blazing Trading Suite interacts with

Dismissed

The team has indicated that it already has control over which pools will be used in the router because all of the transaction calldata will be created from the Blazing Trading Suite backend.

In general, the Blazing Trading Suite protocol allows users to interact with arbitrary pools. Some of these pools can have malicious behaviours and transferring control to them can expose the protocol to reentrancy attacks. Although no specific attack was identified, we suggest whitelisting the pools which the protocol is allowed to interact with.

P2

PERMIT2 acceptance policy is too permissive

PROTOCOL-LEVEL-CONSIDERATION
partially resolved
P2
PERMIT2 acceptance policy is too permissive

Partially resolved

The team has added a check to ensure that only InvalidNonce() errors are accepted. The fix was applied only to BlazingRouterV21 as this will be the production version.

Dispatcher::permit2PermitDispatch accepts a failed call to PERMIT2.permit if a valid allowance is already set, in order to prevent DoS scenarios in which an adversary intercepts the signature and calls permit himself in order to make the "duplicate" permit fail. Although we understand the motivation, and this behaviour does not by itself create an immediate threat, this is still not a safe practice and could potentially facilitate an attack scenario in combination with other vulnerabilities. While a valid permit included in a transaction shows the intention to spend the corresponding funds, if the permit is invalid, the user's intention is much less clear. The contract will even accept permits with wrong signatures (not just duplicates), and tolerating wrong signatures makes it easier to exploit vulnerabilities.

We recommend limiting the accepted errors as much as possible. For instance, the contract could accept only InvalidNonce() errors from Permit2, which indicate a possible replay attack, but _not_ incorrect signatures. Moreover, the contract could check that the expiration of the existing allowance is exactly that of the submitted permit, not just greater or equal, again trying to accept duplicate permits while minimising other accepted errors.

P3

No separation of fees and user funds

PROTOCOL-LEVEL-CONSIDERATION
acknowledged
P3
No separation of fees and user funds

Acknowledged

The team has indicated that this issue is not a priority at the moment, but that it could be revisited in the future.

The contract's balance is used to store two unrelated things: taxes and user funds for the currently executed swaps. This mix could potentially lead to a loss of funds if an accounting bug is found, either in the current code or in future iterations. Although we did not identify accounting issues, the risk would be substantially lowered by keeping taxes in a separate contract that never receives user funds.

P4

Funds which are not fees are assumed to belong to the user

PROTOCOL-LEVEL-CONSIDERATION
resolved
P4
Funds which are not fees are assumed to belong to the user

Resolved

The fix was applied only to BlazingRouterV21 as this will be the production version.

BlazingRouter::execute considers any funds which are owned by the contract and which are not fees as belonging to the user, even if the funds were not obtained in the current transaction.

This can allow an adversary to artificially inflate the contract's balance at any moment, by transferring an arbitrary amount of native token to the contract (via selfdestruct), knowing that he can recover these funds in the next transaction.

Although we did not identify a specific attack, the possibility of arbitrarily inflating the contract's balance is commonly exploited in combination with other vulnerabilities, hence it would be preferable to avoid it.

An alternative could be to consider that only funds obtained in the current transaction belong to the user, and keep the remaining funds in the contract, making it financially difficult for the adversary to inflate the balance.



VULNERABILITIES & FUNCTIONAL ISSUES

This section details issues affecting the functionality of the contract. Dedaub generally categorises issues according to the following severities, but may also take other considerations into account such as impact or difficulty in exploitation:

Category
Description
CRITICAL
Can be profitably exploited by any knowledgeable third-party attacker to drain a portion of the system’s or users’ funds OR the contract does not function as intended and severe loss of funds may result.
HIGH
Third-party attackers or faulty functionality may block the system or cause the system or users to lose funds. Important system invariants can be violated.
MEDIUM
Examples:
  • User or system funds can be lost when third-party systems misbehave.
  • DoS, under specific conditions.
  • Part of the functionality becomes unusable due to a programming error.
LOW
Examples:
  • Breaking important system invariants but without apparent consequences.
  • Buggy functionality for trusted users where a workaround exists.
  • Security issues which may manifest when the system evolves.

Issue resolution includes “dismissed” or “acknowledged” but no action taken, by the client, or “resolved”, per the auditors.


CRITICAL SEVERITY

[No critical severity issues]


HIGH SEVERITY

H1

Any current or future accounting bug will brick the execute function

HIGH
resolved
H1
Any current or future accounting bug will brick the execute function

The BlazingRouterV21 execute function reverts if currentBalance < newProtocolTaxesBalance. If there is any bug in the protocol, the execute function will freeze and brick the entire protocol. This also affects the BlazingRouterV22, BlazingRouterV31 and BlazingRouterV32 contracts.

H2

Contracts are non-upgradable with respect to new contract variables

HIGH
resolved
H2
Contracts are non-upgradable with respect to new contract variables

Resolved

Gaps have been introduced in BlazingRouterV21. BlazingHelperV1 and BlazingHelperV2 are no longer UUPSUpgradable. BlazingRouterV22 and BlazingRouterV23 do not contain this fix as they are not production versions.

The contracts use the UUPS pattern but do not use “gaps” to leave space for future state variables. As a consequence, adding a state variable to any base contract will break the state layout after the upgrade.



MEDIUM SEVERITY

M1

Possible out of gas condition when dispersing currency or tokens

MEDIUM
dismissed
M1
Possible out of gas condition when dispersing currency or tokens

Dismissed

The team has indicated that this parameter is handled by the Blazing Trading Suite backend, and that thus no further action is necessary.

The /helper/modules/payments/Payments contract has a disperseChainCurrency function which loops over a recipients array to disperse the chain currency to users. This function is called by the /helper/base/Dispatcher::disperseChainCurrencyDispatch function which indicates that it is supposed to be able to handle a maximum of 838_860 recipients. We advise metering this function to ensure that the disperseChainCurrency function does not run out of gas over such a large number of recipients before deployment. The same issue affects Payments::disperseToken.



LOW SEVERITY

L1

Redundant use of inheritance in BlazingRouter

LOW
resolved
L1
Redundant use of inheritance in BlazingRouter

Resolved

This issue was resolved in BlazingRouterV21. BlazingRouterV22 and BlazingRouterV23 do not contain this fix as they are not production versions.

The BlazingRouterV21 contract inherits from Initializable and OwnableUpgradable. However, its parent /v2/base/LockAndMsgSender already inherits from Initializable, and its parent /v2/base/ProtocolTaxesManager already inherits from Initializable and from OwnableUpgradable. This issue also affects BlazingRouterV22, BlazingRouterV31 and BlazingRouterV32 (the latter two relative to the /v3/base contracts).

L2

Redundant use of inheritance in Dispatcher

LOW
resolved
L2
Redundant use of inheritance in Dispatcher

The /v2/base/Dispatcher inherits from Payments, but its parent /v2/modules/payments/Permit2Payments already inherits from the /v2/modules/payments/Payments contract. Also affects /v3/base/Dispatcher.

L3

checkHoneyPot function fails on special case

LOW
dismissed
L3
checkHoneyPot function fails on special case

The /v2/modules/Protection contract has a checkHoneyPot function which reverts if the interaction with the pool fails with a reason which is not 64 bytes long. However this exposes the users to honeypots with a reason which is 64 bytes long.

L4

checkLiquidity does not consider all stages on a path

LOW
dismissed
L4
checkLiquidity does not consider all stages on a path

The /v2/modules/Protection contract has a function called checkLiquidity which checks that the final pool on a swap path has liquidity between certain lower and upper bounds. However, it does not check that the intermediate pools also have a certain liquidity, which may be misleading to the caller of the function. Also affects /v3/modules/Protection.

L5

Constraint on permit2 expirations too soft

LOW
acknowledged
L5
Constraint on permit2 expirations too soft

Acknowledged

The team has indicated that this will be handled on the backend.

Although not enforced, the comments in Dispatcher::permit2PermitDispatch suggest that expiration = 0 will always be used. However, this does not necessarily increase security; the permit will be valid for just the current block, but it will be accepted in _any_ block. In a hypothetical scenario in which the adversary wants to use the permit in some attack, if the transaction is not executed (so the permit

remains valid), it will stay valid forever and the adversary can submit it himself at any moment, the only constraint being that he will need to execute his attack in the same block (a quite soft constraint).

In some scenarios, for instance transactions with a forced block or known deadline, it might be actually preferable to set the expiration to that specific block, severely limiting the lifespan of the permit.



OTHER / ADVISORY ISSUES

This section details issues that are not thought to directly affect the functionality of the project, but we recommend considering them.

A1

Missing check on length of parameters for disperse* functions

ADVISORY
info
A1
Missing check on length of parameters for disperse* functions

The /helper/modules/payments/Payments contract has two functions disperseChainCurrency and disperseToken which should check whether the recipients and the values array have the same length if the useSameAmount flag is false.

A2

Missing validations on tax setter function

ADVISORY
info
A2
Missing validations on tax setter function

The /v2/base/ProtocolTaxesManager function is an onlyOwner setter function, but does not have any validations on the tax amounts being set. Also affects /v3/base/ProtocolTaxesManager.

A3

Inconsistent use of hex and decimal values in BytesLib

ADVISORY
info
A3
Inconsistent use of hex and decimal values in BytesLib

The /v3/libraries/BytesLib toLastPool function assigns a hex value to the offsetValue variable and a decimal value to the lastPoolOffsetSub variable. We recommend choosing one representation for consistency.

A4

Compiler bugs

ADVISORY
info
A4
Compiler bugs

The code is compiled with Solidity ^0.8.20. For deployment, we recommend no floating pragmas, but a specific version, to be confident about the baseline guarantees offered by the compiler. Version 0.8.20, in particular, has some known bugs, which we do not believe affect the correctness of the contracts.



DISCLAIMER

The audited contracts have been analyzed using automated techniques and extensive human inspection in accordance with state-of-the-art practices as of the date of this report. The audit makes no statements or warranties on the security of the code. On its own, it cannot be considered a sufficient assessment of the correctness of the contract. While we have conducted an analysis to the best of our ability, it is our recommendation for high-value contracts to commission several independent audits, a public bug bounty program, as well as continuous security auditing and monitoring through Dedaub Security Suite.


ABOUT DEDAUB

Dedaub offers significant security expertise combined with cutting-edge program analysis technology to secure some of the most prominent protocols in DeFi. The founders, as well as many of Dedaub's auditors, have a strong academic research background together with a real-world hacker mentality to secure code. Protocol blockchain developers hire us for our foundational analysis tools and deep expertise in program analysis, reverse engineering, DeFi exploits, cryptography and financial mathematics.