Introduction
We express our gratitude to the Unizen team for the collaborative engagement that enabled the execution of this Smart Contract Security Assessment.
Unizen is a next-generation DEX aggregator, offering developers, traders, and businesses the ability to unlock unparalleled token swap capabilities.
Document | |
---|---|
Name | Smart Contract Code Review and Security Analysis Report for Unizen |
Audited By | Ivan Bondar; Viktor Raboshchuk |
Approved By | Grzegorz Trawinski, Ataberk Yavuzer |
Website | https://www.unizen.io/→ |
Changelog | 11/11/2024 - Preliminary Report |
27/11/2024 - Final Report | |
Platform | Ethereum; Polygon; BNB Chain; Avalanche; Fantom; Arbitrum; Optimism; Base |
Language | Solidity |
Tags | DEX; Bridge; Upgradable |
Methodology | https://hackenio.cc/sc_methodology→ |
Document
- Name
- Smart Contract Code Review and Security Analysis Report for Unizen
- Audited By
- Ivan Bondar; Viktor Raboshchuk
- Approved By
- Grzegorz Trawinski, Ataberk Yavuzer
- Website
- https://www.unizen.io/→
- Changelog
- 11/11/2024 - Preliminary Report
- 27/11/2024 - Final Report
- Platform
- Ethereum; Polygon; BNB Chain; Avalanche; Fantom; Arbitrum; Optimism; Base
- Language
- Solidity
- Tags
- DEX; Bridge; Upgradable
- Methodology
- https://hackenio.cc/sc_methodology→
Review Scope | |
---|---|
Repository | https://github.com/unizen-io/unizen-trade-aggregator/→ |
Commit | 40a94f4 |
Retest Commit | 91503c2 |
Review Scope
- Commit
- 40a94f4
- Retest Commit
- 91503c2
Audit Summary
The system users should acknowledge all the risks summed up in the risks section of the report
Documentation quality
Functional requirements are detailed.
Project overview is detailed
All roles in the system are described.
Use cases are described and detailed.
For each contract, all futures are described.
All interactions are described.
Technical description is clear and concise.
Run instructions are provided.
Technical specification is provided.
The NatSpec documentation is sufficient.
Code quality
The development environment is configured.
Test coverage
Code coverage of the project is approximately 60.00% (branch coverage).
Deployment and basic user interactions are covered with tests.
Negative cases coverage is partially missed.
Interactions involving multiple users are tested.
Fork tests are present.
Not all branches are covered by tests.
System Overview
The Unizen DEX Aggregator leverages a set of specialized smart contracts, optimizing decentralized token swaps with a focus on cross-chain interoperability and cost efficiency. Unizen enables seamless, omni-chain swaps across various blockchain networks through a modular contract architecture, aggregating liquidity from multiple interoperability providers, including LayerZero, Debridge, Meson, Stargate, and Thorchain. This architecture supports single-chain and cross-chain trades, utilizing optimized routes to minimize costs and execution time. Key roles include the Router, which orchestrates trade execution, Executors for specific swap tasks, and specialized contracts for cross-chain compatibility.
The files in the scope:
SingleChainExecutor.sol: Manages single-chain token swaps using multiple methods on a single blockchain.
GasLessExecutor.sol: Facilitates gasless transactions by validating signed user orders for trade execution without requiring user gas.
UnizenRouter.sol: Central router that initiates all trade executions, coordinates executors, and manages protocol fees.
UnizenBridgeExecutor.sol: Handles asset bridging across chains within Unizen’s ecosystem using LayerZero interoperability.
DebridgeMesonWormholeExecutor.sol: Executes cross-chain swaps via Debridge, Meson, and Wormhole protocols.
BaseExecutor.sol: Provides foundational swap, fee handling, and asset recovery functions for executors.
DexHelpers.sol: Maintains a whitelist of approved DEXes and functions that executors can access.
StargateThorchainExecutor.sol: Manages cross-chain swaps using Stargate and Thorchain protocols.
EthReceiver.sol: Simple contract to receive and hold ETH.
Controller.sol: Governs the protocol, managing contract pausing, whitelisting DEXes, and handling dev permissions.
CCTPBase.sol: Base contract for cross-chain messaging and asset transfers via Wormhole and Circle's CCTP.
IGaslessExecutor.sol: Defines the structure for gasless order execution, including
UnizenGasLessOrder
struct to handle gasless swap details and an event for gasless swap completion.IStargateThorchainExecutor.sol: Provides structures and functions for cross-chain swaps via Stargate and Thorchain, detailing swap parameters and events for tracking cross-chain swap status and UTXO operations.
IRouter.sol: Interface for UnizenRouter, managing token transfers and program execution, with specific structures for handling executors, token transfer permits, and executable programs.
IDebridgeMesonWormholeExecutor.sol: Specifies structures and functions for executing cross-chain swaps using Debridge, Meson, and Wormhole protocols, detailing swap events for each protocol’s operations.
ISingleChainExecutor.sol: Interface for single-chain swaps, defining structures for exact-in and exact-out swap types and functions for initiating these swaps with related events.
IWETH.sol: Interface for Wrapped ETH (WETH) functions, including
deposit
,transfer
, andwithdraw
methods to interact with the WETH token contract.IDexHelpers.sol: Interface for DexHelpers, providing a function to verify if a given DEX and function selector is whitelisted for trading.
Types.sol: Defines key data structures (
Integrator
,SwapCall
,ContractBalance
,Permit
) used across contracts to manage trade parameters, contract balances, and integrator fees.IUniswapV2Pair.sol: Interface for Uniswap V2 pair interactions, specifically for executing token swaps within a Uniswap V2 liquidity pool.
ILayerZeroUserApplicationConfig.sol: Interface for configuring LayerZero messaging, allowing applications to set messaging versions, manage configurations, and resume message flow.
ILayerZeroReceiver.sol: Interface for receiving LayerZero cross-chain messages, enabling the contract to handle payloads from other chains through the
lzReceive
function.IStargateReceiver.sol: Interface for receiving tokens and executing swaps via Stargate protocol, handling messages with specific chain IDs, tokens, and payload data for cross-chain swaps.
Privileged roles
UnizenRouter.sol
Owner
:Can set executor and gasless executor status with
setExecutor
.Can set the
Permit2
contract address withsetPermit2
.Can withdraw earned fees from executors using
unizenWithdrawFee
.
Executor
:Authorized to execute transactions through the
execute
function.Can call
routerTransferTokens
to transfer tokens on behalf of users.Can use
routerTransferTokensPermit2
for token transfers under gasless conditions if it has been permitted byPermit2
.
Controller.sol
Owner
:Assigns a developer management address via
setDevMultiSign
.Pauses and unpauses contract execution using
adminPause
andadminUnPause
.
Dev
:Can whitelist or remove DEX addresses via
whiteListDex
andwhiteListDexes
.This role is given permissions to manage DEX verification lists and ensure only approved addresses can execute trades.
DexHelper.sol
Owner
:Can set a developer for managing DEX whitelisting with
setDev
.
Developer (Dev)
:Can whitelist DEX addresses and their associated function selectors using
whitelistDexes
.Can remove DEX addresses from the whitelist through
removeDexes
.Can add or remove specific function selectors for any DEX with
addOrRemoveFuncSign
.
BaseExecutor (Inherits in all Executors)
Owner
:Updates the router and DexHelper contract addresses.
Manages token allowances by revoking approvals.
Withdraws earned fees from integrators with
unizenWithdrawEarnedFee
.Recovers assets mistakenly left in the contract via
recoverAsset
.
UnizenRouter
:Only Unizen Router can call
unizenWithdrawEarnedFee
to withdraw earned fees across multiple tokens.
DebridgeMesonWormholeExecutor
Owner
:Configures cross-chain parameters (DLN, Meson, Wormhole), including setting stable token statuses, relayer contracts, and integrators.
Updates contract configurations for Wormhole, including address mappings for supported tokens and integrators.
GasLessExecutor
Owner
:Configures senders for gasless transactions with
setSender
.Can initialize contract settings including WETH and DexHelper.
UnizenRouter
:Authorized to execute the
swapGasLess
function for executing gasless swaps on behalf of users.
SingleChainExecutor
Owner
:Configures contract parameters such as WETH and router address.
UnizenRouter
:Authorized to execute
swap
andswapExactOut
, allowing swaps and managing liquidity on a single chain.
StargateThorchainExecutor
Owner
:Configures Stargate and Thorchain routers and manages valid destination addresses.
Sets destination addresses for supported chain IDs.
Manages stable token mappings and approvals across Stargate pools.
UnizenRouter
:Authorized to execute
swapSTG
andswapTC
for cross-chain swaps, allowing source and destination swaps across supported chains.
UnizenBridgeExecutor
Owner
:Manages LayerZero endpoint configurations and cross-chain destination mappings.
Configures bridge tokens, their supported destinations, and corresponding mappings.
Sets fee rate and fee recipient addresses.
UnizenRouter
:Authorizes deposits and token transfers via
deposit
, enabling cross-chain bridging functionalities.
Potential Risks
Scope Definition and Security Guarantees: The audit does not cover all code in the repository. Contracts outside the audit scope may introduce vulnerabilities, potentially impacting the overall security due to the interconnected nature of smart contracts.
System Reliance on External Contracts: The functioning of the system significantly relies on specific external contracts. Any flaws or vulnerabilities in these contracts adversely affect the audited project, potentially leading to security breaches or loss of funds.
Interactions with External DeFi Protocols: Dependence on external DeFi protocols inherits their risks and vulnerabilities. This might lead to direct financial losses if these protocols are exploited, indirectly affecting the audited project.
Owner's Unrestricted State Modification: The absence of restrictions on state variable modifications by the owner leads to arbitrary changes, affecting contract integrity and user trust, especially during critical operations.
Absence of Time-lock Mechanisms for Critical Operations: Without time-locks on critical operations, there is no buffer to review or revert potentially harmful actions, increasing the risk of rapid exploitation and irreversible changes.
Single Points of Failure and Control: The project is fully or partially centralized, introducing single points of failure and control. This centralization can lead to vulnerabilities in decision-making and operational processes, making the system more susceptible to targeted attacks or manipulation.
Administrative Key Control Risks: The digital contract architecture relies on administrative keys for critical operations. Centralized control over these keys presents a significant security risk, as compromise or misuse can lead to unauthorized actions or loss of funds.
Flexibility and Risk in Contract Upgrades: The project's contracts are upgradable, allowing the administrator to update the contract logic at any time. While this provides flexibility in addressing issues and evolving the project, it also introduces risks if upgrade processes are not properly managed or secured, potentially allowing for unauthorized changes that could compromise the project's integrity and security.
Absence of Upgrade Window Constraints: The contract suite allows for immediate upgrades without a mandatory review or waiting period, increasing the risk of rapid deployment of malicious or flawed code, potentially compromising the system's integrity and user assets.
Cross-Chain Execution Failures: Dependencies on external cross-chain protocols (e.g., Stargate, Thorchain) pose risks of execution failures, delays, or inconsistencies across chains.
Dex Whitelisting Risks: Dex whitelisting relies on trusted addresses and function selectors; any error in whitelisting or verification can lead to interactions with malicious DEX contracts.
User-Bypassed Fee Enforcement: Users are intended to interact with the Executor Router contract through the provided frontend, which ensures accurate fee inputs for both integrators and Unizen. However, since fees are part of the user-provided transaction parameters, there is a potential risk that users could bypass the frontend, craft custom transactions, and execute swaps without including the required fees. While Unizen's backend fetches the best swap opportunities to incentivize frontend usage, this bypass risk exists when users interact directly with the protocol using custom inputs.
Malicious or Misconfigured Executors: If the isExecutor
mappings are compromised or misconfigured, unauthorized executors could gain the ability to perform transactions using user permits or direct approvals. This risk enables malicious entities to execute unauthorized transfers of user funds directly to themselves without performing intended swaps, leading to potential financial loss and compromising contract integrity. Users should be cautious about the amounts they permit to the router, and strict controls must be maintained over executor authorization to mitigate this risk.
Findings
Code ― | Title | Status | Severity | |
---|---|---|---|---|
F-2024-7080 | Missing Validation of Permit User in _routerTransferTokens Function | fixed | Critical | |
F-2024-6897 | Missing Initialization for Critical Variables Results in Contract Non-Functionality | fixed | High | |
F-2024-6896 | Lack of Access Control in LayerZero Configuration Functions Allows Unauthorized Alteration of Protocol Settings | fixed | High | |
F-2024-7099 | Exposed API Keys in Hardhat Configuration File | mitigated | Medium | |
F-2024-7050 | Missing isETHTrade Logic in swapSimple Function | fixed | Medium | |
F-2024-7004 | Lack of Nonce or Order Tracking in GasLessExecutor Allows Replay Attacks by Authorized Relayers | fixed | Medium | |
F-2024-6910 | Lack of Call Success Check in Native Asset Withdrawal | fixed | Medium | |
F-2024-6901 | Incorrect Parameter Usage and Ordering in setConfig and getConfig Functions | fixed | Medium | |
F-2024-7103 | Hardcoded Gas Subtraction Leading to Potential Reverts and Unreliable Execution | mitigated | Low | |
F-2024-7101 | Potential Fee Collection Failure Due to Discrepancies in Fee Tracking | accepted | Low |
Identify vulnerabilities in your smart contracts.
Appendix 1. Definitions
Severities
When auditing smart contracts, Hacken is using a risk-based approach that considers Likelihood, Impact, Exploitability and Complexity metrics to evaluate findings and score severities.
Reference on how risk scoring is done is available through the repository in our Github organization:
Severity | Description |
---|---|
Critical | Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation. |
High | High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation. |
Medium | Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category. |
Low | Major deviations from best practices or major Gas inefficiency. These issues will not have a significant impact on code execution. |
Severity
- Critical
Description
- Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation.
Severity
- High
Description
- High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation.
Severity
- Medium
Description
- Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category.
Severity
- Low
Description
- Major deviations from best practices or major Gas inefficiency. These issues will not have a significant impact on code execution.
Potential Risks
The "Potential Risks" section identifies issues that are not direct security vulnerabilities but could still affect the project’s performance, reliability, or user trust. These risks arise from design choices, architectural decisions, or operational practices that, while not immediately exploitable, may lead to problems under certain conditions. Additionally, potential risks can impact the quality of the audit itself, as they may involve external factors or components beyond the scope of the audit, leading to incomplete assessments or oversight of key areas. This section aims to provide a broader perspective on factors that could affect the project's long-term security, functionality, and the comprehensiveness of the audit findings.
Appendix 2. Scope
The scope of the project includes the following smart contracts from the provided repository:
Scope Details | |
---|---|
Repository | https://github.com/unizen-io/unizen-trade-aggregator/→ |
Commit | 40a94f42adbf19601fc798a0ab5dac3c09572c02 |
Retest Commit | 91503c2b84e561b08128412f49837a90bb8976f1 |
Whitepaper | NA |
Requirements | https://docs.unizen.io/→ |
Technical Requirements | https://docs.unizen.io/;→ |
Scope Details
- Commit
- 40a94f42adbf19601fc798a0ab5dac3c09572c02
- Retest Commit
- 91503c2b84e561b08128412f49837a90bb8976f1
- Whitepaper
- NA
- Requirements
- https://docs.unizen.io/→
- Technical Requirements
- https://docs.unizen.io/;→
Assets in Scope
dependencies/Controller.sol
helpers/EthReceiver.sol
interfaces/IController.sol
interfaces/IDlnSource.sol
interfaces/ILayerZeroEndpoint.sol
interfaces/ILayerZeroReceiver.sol
interfaces/ILayerZeroUserApplicationConfig.sol
interfaces/IMeson.sol
interfaces/IStargateReceiver.sol
interfaces/IStargateRouter.sol
interfaces/ITcRouter.sol
libraries/DlnOrderLib.sol
libraries/wormhole/CCTPBase.sol
libraries/wormhole/interfaces/CCTPInterfaces/IMessageTransmitter.sol
libraries/wormhole/interfaces/CCTPInterfaces/IReceiver.sol
Appendix 3. Additional Valuables
Verification of System Invariants
During the audit of Unizen, Hacken followed its methodology by performing fuzz-testing on the project's main functions. Due to the complex and dynamic interactions within the protocol, unexpected edge cases might arise. Therefore, it was important to use fuzz-testing to ensure that several system invariants hold true in all situations.
Fuzz-testing allows the input of many random data points into the system, helping to identify issues that regular testing might miss. A specific Echidna fuzzing suite was prepared for this task, and throughout the assessment, 45 invariants were tested over 30000 runs. This thorough testing ensured that the system works correctly even with unexpected or unusual inputs.
Invariant | Test Result | Run Count |
---|---|---|
testFuzzSetDLNConfigution(): Only the contract owner can set the DLN adapter address; when the owner sets a valid (non-zero) _dlnAdapter , the contract's dlnAdapter state variable is updated accordingly. | Passed | 30K+ |
testFuzzSetDlnStableRevertsOnLengthMismatch(): The setDlnStable function must revert when the lengths of stableTokens and isActive arrays do not match, ensuring array length consistency. | Failed | 30K+ |
estFuzzSetDlnStableRevertsOnZeroToken(): The setDlnStable function must revert if any address in the stableTokens array is the zero address, ensuring only valid token addresses are registered. | Failed | 30K+ |
testFuzzSetMesonConfigution(): Only the contract owner can set the Meson contract address; when the owner sets a valid (non-zero) _meson , the contract's meson state variable is updated accordingly. | Passed | 30K+ |
testFuzzSetMesonConfigutionAccessControl(): Only the contract owner can set the Meson contract address; attempts by non-owner accounts to set the Meson address should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testFuzzSetMesonStableRevertsOnLengthMismatch(): The setMesonStable function must revert when the lengths of stableTokens and isActive arrays do not match, ensuring array length consistency. | Failed | 30K+ |
testFuzzSetMesonStableRevertsOnZeroToken(): The setMesonStable function must revert if any address in the stableTokens array is the zero address, ensuring only valid token addresses are registered. | Failed | 30K+ |
testFuzzSetDLNConfigutionAccessControl(): Only the contract owner can set DLN configurations; attempts by non-owner accounts to set _dlnSource and _dlnAdapter should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testFuzzSetWormholeConfig(): Only the contract owner can set Wormhole configurations; when the owner sets valid (non-zero) _wormholeRelayer , _wormhole , _circleMessageTransmitter , _circleTokenMessenger , and _WormholeUSDC , the contract's corresponding state variables are updated accordingly. | Passed | 30K+ |
testFuzzSetWormholeConfigRevertsOnZeroAddress(): The setWormholeConfig function must revert if any of the provided addresses are the zero address, ensuring only valid Wormhole configuration addresses are set. | Failed | 30K+ |
testFuzzSetWormholeConfigAccessControl(): Only the contract owner can set Wormhole configurations; attempts by non-owner accounts to set Wormhole configurations should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testFuzzSetDexHelper(): Only the contract owner can set the DexHelper address; when the owner sets a valid _dexHelper , the contract's dexHelper state variable is updated accordingly. | Passed | 30K+ |
testFuzzSetDexHelperAccessControl(): Only the contract owner can set the DexHelper address; attempts by non-owner accounts to set the DexHelper should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testFuzzSetDexHelperRevertsOnZeroAddress(): The setDexHelper function must revert if any of the provided addresses are the zero address, ensuring only valid dexHelper addresses are set. | Failed | 30K+ |
testFuzzSetRouter(): Only the contract owner can set the Unizen Router address; when the owner sets a valid _unizenRouter , the contract's unizenRouter state variable is updated accordingly. | Passed | 30K+ |
testFuzzSetRouterAccessControl(): Only the contract owner can set the Unizen Router address; attempts by non-owner accounts to set the Unizen Router should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testFuzzSetRouterRevertsOnZeroAddress(): The setRouter function must revert if any of the provided addresses are the zero address, ensuring only valid dexHelper addresses are set. | Failed | 30K+ |
testFuzzSetDev(): Only the contract owner can set the developer address; when the owner sets a valid (non-zero) newDev address, the dexHelper 's dev state variable is updated accordingly. | Passed | 30K+ |
testFuzzSetDevRevertsOnZeroAddress(): Setting the developer address to the zero address should revert with "DexHelper: newDev addresses cannot be zero", ensuring that dev cannot be set to the zero address. | Failed | 30K+ |
testFuzzSetDevAccessControl(): Only the contract owner can set the developer address; attempts by non-owner accounts to set the developer should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testFuzzRecoverAssetETH(): Only the contract owner can recover ETH by calling recoverAsset with address(0) ; ensures that the owner's ETH balance increases by the recovered amount and the executor's ETH balance is zero after recovery. | Passed | 30K+ |
testFuzzRecoverAssetAccessControl(): Only the contract owner can call recoverAsset ; attempts by non-owner accounts to recover assets should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testFuzzSetSender(): Only the contract owner can set a sender's enabled status; when the owner sets a valid (non-zero) _sender with _isEnabled , the contract's isValidSender mapping is updated accordingly and unizenRouter remains unchanged. | Passed | 30K+ |
testFuzzSetSenderRevertsOnZeroAddress(): Setting the sender address to the zero address should revert with "GasLessExecutor: Sender address cannot be zero", ensuring that zero address cannot be set as a sender. | Failed | 30K+ |
testFuzzSetSenderAccessControl(): Only the contract owner can set a sender's enabled status; attempts by non-owner accounts to set a sender should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testSetLZEndPointRevertsOnZeroAddress(): Setting the LayerZero Endpoint to the zero address should revert with "Unizen: Invalid LayerZero endpoint", ensuring that only valid LayerZero endpoints are set. | Failed | 30K+ |
testSetRouterRevertsOnZeroAddress(): Setting the Router to the zero address should revert with "Unizen: Invalid Router address", ensuring that only valid Router addresses are set. | Failed | 30K+ |
testSetDestAddrRevertsOnZeroBridgeExecutor(): Setting the Destination Address with a bridgeExecutor as the zero address should revert with "Unizen: Invalid bridge executor address", ensuring that only valid bridge executors are set. | Failed | 30K+ |
testAddTokenRevertsOnZeroAddress() : Adding a zero address as a token should revert with "Unizen: Invalid token address", ensuring that only valid token addresses are registered. | Failed | 30K+ |
testSetFeeRevertsOnInvalidFee(): Setting the fee beyond the maximum allowed (e.g., >10000 basis points) should revert with "Unizen: Fee exceeds maximum", ensuring that fees are within acceptable limits. | Failed | 30K+ |
testOnlyOwnerCanSetLZEndPoint(): Only the contract owner can set the LayerZero Endpoint; attempts by non-owner accounts to set the LayerZero Endpoint should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testOnlyOwnerCanAddToken(): Only the contract owner can add tokens; attempts by non-owner accounts to add tokens should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testOnlyOwnerCanRemoveToken(): Only the contract owner can remove tokens; attempts by non-owner accounts to remove tokens should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testOnlyOwnerCanSetFee(): Only the contract owner can set the fee; attempts by non-owner accounts to set the fee should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testOnlyOwnerCanSetDestAddr(): Only the contract owner can set the destination address; attempts by non-owner accounts to set the destination address should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testFuzzSetExecutor(): Only the contract owner can set an executor's validity and gasless status; when the owner sets a valid _executor address with isValid and _isGaslessExecutor flags, the contract's isExecutor and isGaslessExecutor mappings are updated accordingly. | Passed | 30K+ |
testFuzzSetExecutorAccessControl(): Only the contract owner can set an executor's validity and gasless status; attempts by non-owner accounts to set an executor should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testFuzzSetPermit2(): Only the contract owner can set the Permit2 contract address; when the owner sets a valid _permit2 address, the contract's PERMIT2 variable is updated accordingly. | Passed | 30K+ |
testFuzzSetPermit2AccessControl(): Only the contract owner can set the Permit2 contract address; attempts by non-owner accounts to set Permit2 should revert with "Ownable: caller is not the owner". | Passed | 30K+ |
testFuzzSetExecutorWithZeroAddress(): The contract allows setting an executor with the zero address; when the owner sets a zero address executor with isValid and _isGaslessExecutor flags, the contract's isExecutor and isGaslessExecutor mappings are updated accordingly. | Passed | 30K+ |
testFuzzSetPermit2WithZeroAddress(): Only the contract owner can set the Permit2 contract address; when the owner sets Permit2 to the zero address, the contract's PERMIT2 variable is updated accordingly. | Passed | 30K+ |
User swapExactOut Trade USDC to USDT - This test verifies that when a user attempts to swap a maximum amount of USDC for an exact amount of USDT, the contract correctly increases the user's USDT balance by at least the specified expectedAmountOut while ensuring the USDC spent does not exceed amountInMax . It also checks that the transaction either succeeds under valid conditions or reverts gracefully when constraints like insufficient funds or invalid deadlines are met. | Passed | 30K+ |
User swapExactIn Trade Native to USDT - This test ensures that when a user swaps a specific amount of native tokens (e.g., ETH) for USDT, the user's native balance decreases appropriately and their USDT balance increases by at least the desired minimum amount. The test also verifies that transactions either execute successfully when all conditions are favorable or revert as expected due to issues like insufficient native balance or expired deadlines. | Passed | 30K+ |
Permit2 User swapSimple Trade USDT to Native - This test assesses the swapSimple function's ability to handle USDT to native token swaps using Permit2 for approvals. It ensures that the contract correctly processes Permit2 signatures to authorize token spending, updates user balances accurately upon successful swaps, and appropriately reverts transactions when encountering scenarios like excessive minimum output amounts, insufficient USDT balances, or invalid deadlines | Passed | 30K+ |
Invariant
testFuzzSetDLNConfigution():
Only the contract owner can set the DLN adapter address; when the owner sets a valid (non-zero)_dlnAdapter
, the contract'sdlnAdapter
state variable is updated accordingly.Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetDlnStableRevertsOnLengthMismatch():
ThesetDlnStable
function must revert when the lengths ofstableTokens
andisActive
arrays do not match, ensuring array length consistency.Test Result
- Failed
Run Count
- 30K+
Invariant
estFuzzSetDlnStableRevertsOnZeroToken():
ThesetDlnStable
function must revert if any address in thestableTokens
array is the zero address, ensuring only valid token addresses are registered.Test Result
- Failed
Run Count
- 30K+
Invariant
testFuzzSetMesonConfigution():
Only the contract owner can set the Meson contract address; when the owner sets a valid (non-zero)_meson
, the contract'smeson
state variable is updated accordingly.Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetMesonConfigutionAccessControl():
Only the contract owner can set the Meson contract address; attempts by non-owner accounts to set the Meson address should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetMesonStableRevertsOnLengthMismatch():
ThesetMesonStable
function must revert when the lengths ofstableTokens
andisActive
arrays do not match, ensuring array length consistency.Test Result
- Failed
Run Count
- 30K+
Invariant
testFuzzSetMesonStableRevertsOnZeroToken():
ThesetMesonStable
function must revert if any address in thestableTokens
array is the zero address, ensuring only valid token addresses are registered.Test Result
- Failed
Run Count
- 30K+
Invariant
testFuzzSetDLNConfigutionAccessControl():
Only the contract owner can set DLN configurations; attempts by non-owner accounts to set_dlnSource
and_dlnAdapter
should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetWormholeConfig():
Only the contract owner can set Wormhole configurations; when the owner sets valid (non-zero)_wormholeRelayer
,_wormhole
,_circleMessageTransmitter
,_circleTokenMessenger
, and_WormholeUSDC
, the contract's corresponding state variables are updated accordingly.Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetWormholeConfigRevertsOnZeroAddress():
ThesetWormholeConfig
function must revert if any of the provided addresses are the zero address, ensuring only valid Wormhole configuration addresses are set.Test Result
- Failed
Run Count
- 30K+
Invariant
testFuzzSetWormholeConfigAccessControl():
Only the contract owner can set Wormhole configurations; attempts by non-owner accounts to set Wormhole configurations should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetDexHelper():
Only the contract owner can set the DexHelper address; when the owner sets a valid_dexHelper
, the contract'sdexHelper
state variable is updated accordingly.Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetDexHelperAccessControl():
Only the contract owner can set the DexHelper address; attempts by non-owner accounts to set the DexHelper should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetDexHelperRevertsOnZeroAddress():
ThesetDexHelper
function must revert if any of the provided addresses are the zero address, ensuring only valid dexHelper addresses are set.Test Result
- Failed
Run Count
- 30K+
Invariant
testFuzzSetRouter():
Only the contract owner can set the Unizen Router address; when the owner sets a valid_unizenRouter
, the contract'sunizenRouter
state variable is updated accordingly.Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetRouterAccessControl():
Only the contract owner can set the Unizen Router address; attempts by non-owner accounts to set the Unizen Router should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetRouterRevertsOnZeroAddress():
ThesetRouter
function must revert if any of the provided addresses are the zero address, ensuring only valid dexHelper addresses are set.Test Result
- Failed
Run Count
- 30K+
Invariant
testFuzzSetDev():
Only the contract owner can set the developer address; when the owner sets a valid (non-zero)newDev
address, thedexHelper
'sdev
state variable is updated accordingly.Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetDevRevertsOnZeroAddress():
Setting the developer address to the zero address should revert with "DexHelper: newDev addresses cannot be zero", ensuring thatdev
cannot be set to the zero address.Test Result
- Failed
Run Count
- 30K+
Invariant
testFuzzSetDevAccessControl():
Only the contract owner can set the developer address; attempts by non-owner accounts to set the developer should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzRecoverAssetETH():
Only the contract owner can recover ETH by callingrecoverAsset
withaddress(0)
; ensures that the owner's ETH balance increases by the recovered amount and the executor's ETH balance is zero after recovery.Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzRecoverAssetAccessControl():
Only the contract owner can callrecoverAsset
; attempts by non-owner accounts to recover assets should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetSender():
Only the contract owner can set a sender's enabled status; when the owner sets a valid (non-zero)_sender
with_isEnabled
, the contract'sisValidSender
mapping is updated accordingly andunizenRouter
remains unchanged.Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetSenderRevertsOnZeroAddress():
Setting the sender address to the zero address should revert with "GasLessExecutor: Sender address cannot be zero", ensuring that zero address cannot be set as a sender.Test Result
- Failed
Run Count
- 30K+
Invariant
testFuzzSetSenderAccessControl():
Only the contract owner can set a sender's enabled status; attempts by non-owner accounts to set a sender should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testSetLZEndPointRevertsOnZeroAddress():
Setting the LayerZero Endpoint to the zero address should revert with "Unizen: Invalid LayerZero endpoint", ensuring that only valid LayerZero endpoints are set.Test Result
- Failed
Run Count
- 30K+
Invariant
testSetRouterRevertsOnZeroAddress():
Setting the Router to the zero address should revert with "Unizen: Invalid Router address", ensuring that only valid Router addresses are set.Test Result
- Failed
Run Count
- 30K+
Invariant
testSetDestAddrRevertsOnZeroBridgeExecutor():
Setting the Destination Address with a bridgeExecutor as the zero address should revert with "Unizen: Invalid bridge executor address", ensuring that only valid bridge executors are set.Test Result
- Failed
Run Count
- 30K+
Invariant
testAddTokenRevertsOnZeroAddress()
: Adding a zero address as a token should revert with "Unizen: Invalid token address", ensuring that only valid token addresses are registered.Test Result
- Failed
Run Count
- 30K+
Invariant
testSetFeeRevertsOnInvalidFee():
Setting the fee beyond the maximum allowed (e.g., >10000 basis points) should revert with "Unizen: Fee exceeds maximum", ensuring that fees are within acceptable limits.Test Result
- Failed
Run Count
- 30K+
Invariant
testOnlyOwnerCanSetLZEndPoint():
Only the contract owner can set the LayerZero Endpoint; attempts by non-owner accounts to set the LayerZero Endpoint should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testOnlyOwnerCanAddToken():
Only the contract owner can add tokens; attempts by non-owner accounts to add tokens should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testOnlyOwnerCanRemoveToken():
Only the contract owner can remove tokens; attempts by non-owner accounts to remove tokens should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testOnlyOwnerCanSetFee():
Only the contract owner can set the fee; attempts by non-owner accounts to set the fee should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testOnlyOwnerCanSetDestAddr():
Only the contract owner can set the destination address; attempts by non-owner accounts to set the destination address should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetExecutor():
Only the contract owner can set an executor's validity and gasless status; when the owner sets a valid_executor
address withisValid
and_isGaslessExecutor
flags, the contract'sisExecutor
andisGaslessExecutor
mappings are updated accordingly.Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetExecutorAccessControl():
Only the contract owner can set an executor's validity and gasless status; attempts by non-owner accounts to set an executor should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetPermit2():
Only the contract owner can set the Permit2 contract address; when the owner sets a valid_permit2
address, the contract'sPERMIT2
variable is updated accordingly.Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetPermit2AccessControl():
Only the contract owner can set the Permit2 contract address; attempts by non-owner accounts to set Permit2 should revert with "Ownable: caller is not the owner".Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetExecutorWithZeroAddress():
The contract allows setting an executor with the zero address; when the owner sets a zero address executor withisValid
and_isGaslessExecutor
flags, the contract'sisExecutor
andisGaslessExecutor
mappings are updated accordingly.Test Result
- Passed
Run Count
- 30K+
Invariant
testFuzzSetPermit2WithZeroAddress():
Only the contract owner can set the Permit2 contract address; when the owner sets Permit2 to the zero address, the contract'sPERMIT2
variable is updated accordingly.Test Result
- Passed
Run Count
- 30K+
Invariant
User swapExactOut Trade USDC to USDT
- This test verifies that when a user attempts to swap a maximum amount of USDC for an exact amount of USDT, the contract correctly increases the user's USDT balance by at least the specifiedexpectedAmountOut
while ensuring the USDC spent does not exceedamountInMax
. It also checks that the transaction either succeeds under valid conditions or reverts gracefully when constraints like insufficient funds or invalid deadlines are met.Test Result
- Passed
Run Count
- 30K+
Invariant
User swapExactIn Trade Native to USDT
- This test ensures that when a user swaps a specific amount of native tokens (e.g., ETH) for USDT, the user's native balance decreases appropriately and their USDT balance increases by at least the desired minimum amount. The test also verifies that transactions either execute successfully when all conditions are favorable or revert as expected due to issues like insufficient native balance or expired deadlines.Test Result
- Passed
Run Count
- 30K+
Invariant
Permit2 User swapSimple Trade USDT to Native
- This test assesses the swapSimple function's ability to handle USDT to native token swaps using Permit2 for approvals. It ensures that the contract correctly processes Permit2 signatures to authorize token spending, updates user balances accurately upon successful swaps, and appropriately reverts transactions when encountering scenarios like excessive minimum output amounts, insufficient USDT balances, or invalid deadlinesTest Result
- Passed
Run Count
- 30K+
Additional Recommendations
The smart contracts in the scope of this audit could benefit from the introduction of automatic emergency actions for critical activities, such as unauthorized operations like ownership changes or proxy upgrades, as well as unexpected fund manipulations, including large withdrawals or minting events. Adding such mechanisms would enable the protocol to react automatically to unusual activity, ensuring that the contract remains secure and functions as intended.
To improve functionality, these emergency actions could be designed to trigger under specific conditions, such as:
Detecting changes to ownership or critical permissions.
Monitoring large or unexpected transactions and minting events.
Pausing operations when irregularities are identified.
These enhancements would provide an added layer of security, making the contract more robust and better equipped to handle unexpected situations while maintaining smooth operations.