Introduction
We express our gratitude to the Dione Protocol team for the collaborative engagement that enabled the execution of this Smart Contract Security Assessment.
The Dione Protocol is a bridge solution between Ethereum and Odyssey blockchains. Oddysey chain is a highly performant Layer-1 protocol offering swift transaction finality, robust security, and seamless scalability. The bridge aims to transfer Dione tokens from Odyssey to Ethereum and USDC tokens from Ethereum to Odyssey primarily.
Document | |
---|---|
Name | Smart Contract Code Review and Security Analysis Report for Dione Protocol |
Audited By | Grzegorz Trawinski, Viktor Raboshchuk |
Approved By | Przemyslaw Swiatowiec |
Website | https://www.dioneprotocol.com/→ |
Changelog | 30/09/2024 - Preliminary Report, 21/10/2024 - Final Report |
Platform | Ethereum, Odyssey |
Language | Solidity |
Tags | Bridge |
Methodology | https://hackenio.cc/sc_methodology→ |
Document
- Name
- Smart Contract Code Review and Security Analysis Report for Dione Protocol
- Audited By
- Grzegorz Trawinski, Viktor Raboshchuk
- Approved By
- Przemyslaw Swiatowiec
- Changelog
- 30/09/2024 - Preliminary Report, 21/10/2024 - Final Report
- Platform
- Ethereum, Odyssey
- Language
- Solidity
- Tags
- Bridge
- Methodology
- https://hackenio.cc/sc_methodology→
Review Scope | |
---|---|
Repository | https://github.com/DioneProtocol/sfxdx__odyssey-bridge-sc→ |
Commit | a89f414 |
Retest Commit | 8697ea1 |
Review Scope
- Commit
- a89f414
- Retest Commit
- 8697ea1
Audit Summary
The system users should acknowledge all the risks summed up in the risks section of the report
Documentation quality
Functional requirements are missed.
Technical description is partially provided.
Code quality
The code mostly follows style guides and best practices.
See observations for more details.
The development environment is configured.
Test coverage
Code coverage of the project is 15% (branch coverage).
Not all branches are covered with tests.
System Overview
The Dione Protocol is a bridge solution between Ethereum and Odyssey blockchains.
WDIONE - is a smart contract that wraps Dione token, allowing users to deposit and withdraw native Dione assets.
WDIONEBridged - is a smart contract for wrapped Dione token on the Ethereum side.
DioneBridge - is a main bridge contract. It is using the Wanchain Message Bridge (WMB) behind the scenes. It allows to deposit WDione tokens. It allows to redeem WDioneBridged tokens. Additionally it allows to bridge native tokens and any other ERC20 token. It supports both withdrawing tokens and minting wrapped tokens.
DioneBridgeERC20 - is a smart contract for bridge transfers of ERC20 token.
DioneBridgeERC20DeterministicFactory - is a smart contract that deploys ERC20 tokens for cross-chain bridges.
DioneBridgeERC20Factory - is a smart contract that allows the deployment of new ERC20 tokens by cloning a pre-existing token contract.
DioneBridgeERC677 - is a smart contract allowing tokens to be transferred with additional data, and if the recipient is a contract, it triggers a callback function on the recipient.
WmbAppUpgradeable - is an abstract base that enables applications to send and receive cross-chain messages via the Wanchain Message Bridge (WMB).
Privileged roles
The
owner
of the WDIONE can configure key parameters such as fees, blacklist management, and fee receiver updates.The
DEFAULT_ADMIN_ROLE
of DioneBridge contract can set token limits, supported tokens, cross-chain parameters, and fees.The
GOVERNANCE_ROLE
of DioneBridge contract can pause or unpause the contract operations.The
RESCUER_ROLE
of DioneBridge contract allows the recovery of tokens that are accidentally sent to the contract.The
owner
of the WDIONEBridged contract can set the transfer fee, the fee receiver, the fee threshold and manage the whitelist.The
bridge
role WDIONEBridged contract.The
MINTER_ROLE
of the DioneBridgeERC20 contract allows accounts to mint new tokens by calling themint()
function.The
owner
of the DioneBridgeERC20DeterministicFactory contract can deploy deterministic token contracts using thedeployDeterministic()
function and transfer ownership.In the WDIONEBridged contract, the
onlyBridge
modifier restricts access to mint() and burn(), functions, allowing only the DioneBridge contract to execute them.
Potential Risks
Risk of Stuck Transactions from Incorrect messageGasLimit: The admin role of contract DioneBridge
capable to specify a custom messageGasLimit
for the deposit and redeem functions. However, if the gasLimit
is set incorrectly, it poses a risk that transactions may become indefinitely stuck in pending status or be reverted.
System Reliance on External Contracts: The functioning of the system significantly relies on Wanchain Message Bridge external contracts. Any flaws or vulnerabilities in these contracts adversely affect the audited project, potentially leading to security breaches or loss of funds.
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.
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.
Findings
Code ― | Title | Status | Severity | |
---|---|---|---|---|
F-2024-6287 | The wmbReceive's Mint Does Not Update _balances Array | fixed | Critical | |
F-2024-6290 | The rescueTokens Function Can Break Bridge Accounting | fixed | High | |
F-2024-6291 | Inconsistent and Ambiguous Application of Fees | fixed | High | |
F-2024-6337 | The transferFrom Does Not Check Allowance For Applied Fee | fixed | Medium | |
F-2024-6338 | Fee-On-Transfer Is Applied On Top Of The Transferred Amount | fixed | Low | |
F-2024-6322 | Incorrect Fee Validation in setFee() Function Allows Excessive Fees and Prevents Reduction | fixed | Low | |
F-2024-6308 | The wmbReceive Function Can Revert Due To Gas Overconsumption | fixed | Low | |
F-2024-6307 | Excessive Amount of Native Tokens Are Collected By The Protocol | fixed | Low | |
F-2024-6292 | The dispatchMessage Can Revert Due To Too Large Fee Provided | fixed | Low | |
F-2024-6289 | USDC Blacklisting Feature Can Prevent Token Transfer | fixed | Low |
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/DioneProtocol/sfxdx__odyssey-bridge-sc→ |
Commit | a89f4141f732c1ac7d178154bff65649a9f30841 |
Retest Commit | 8697ea1c9c2f3c6873ba267d6cb759b1ad395c45 |
Whitepaper | https://dione-protocol.gitbook.io/dione-whitepaper→ |
Requirements | Bridge and Wrapped D-2.pdf |
Technical Requirements | - |
Scope Details
- Commit
- a89f4141f732c1ac7d178154bff65649a9f30841
- Retest Commit
- 8697ea1c9c2f3c6873ba267d6cb759b1ad395c45
- Requirements
- Bridge and Wrapped D-2.pdf
- Technical Requirements
- -
Assets in Scope
Appendix 3. Additional Valuables
Verification of System Invariants
During the audit of Dione Protocol, Hacken followed its methodology by performing fuzz-testing on the project's main functions. Echidna →, a tool used for fuzz-testing, was employed to check how the protocol behaves under various inputs. 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, 30 invariants were tested over 50000 runs. This thorough testing ensured that the system works correctly even with unexpected or unusual inputs.
Invariant | Test Result | Run Count |
---|---|---|
WDIONE::setFeeBps Only the owner can set feeBps , and the new value must not exceed PCT_DIV . | Failed | 50k |
WDIONE::setFeeReceiver The feeReceiver must be a valid non-zero address, and only the owner can update it. | Failed | 50k |
WDIONE::setBlacklist Only the owner can add or remove an account from the blacklist, and the blacklist state must reflect the changes correctly. | Passed | 50k |
WDIONE::deposit The caller’s balanceOf must increase by msg.value , and the total supply must increase by the same amount. | Passed | 50k |
WDIONE::withdraw The withdrawal must not exceed the caller’s balance, and the withdrawn amount must be transferred to the caller. | Passed | 50k |
WDIONE::approve The allowance for the spender must be set to the provided amount, and the Approval event must be emitted. | Passed | 50k |
WDIONE::transferFrom The src address must have sufficient balance, the allowance must be correctly handled, and the transfer fee must be deducted if applicable. | Passed | 50k |
WDIONEBridged::checkAndSendFees The function sends collected fees to the target chain when the collectedFees exceed feeThreshold, ensuring sufficient ETH is available for the operation. | 50k | |
WDIONEBridged::receive The receive() function allows the contract to accept ETH payments to fund cross-chain fee transfers. | Passed | 50k |
WDIONEBridged::mint() The function, callable only by the bridge, mints tokens to the specified address, increasing the total supply accordingly. | Passed | 50k |
WDIONEBridged::burn The function, callable only by the bridge, burns tokens from the caller's balance if sufficient, decreasing the total supply. | Passed | 50k |
WDIONEBridged::setFee The function, callable only by the owner, sets the transfer fee in basis points, ensuring it does not exceed PCT_DIV. | Failed | 50k |
WDIONEBridged::setFeeReceiver The function, callable only by the owner, updates the fee receiver's address accurately. | Failed | 50k |
WDIONEBridged::setFeeThreshold The function, callable only by the owner, sets the feeThreshold which determines when fees are sent to the target chain. | Passed | 50k |
WDIONEBridged::setWhitelist The function, callable only by the owner, correctly adds or removes a single account from the whitelist. | Passed | 50k |
DioneBridge::setWethAddress Only the admin can set WETH_ADDRESS, and it must be a non-zero address. | Failed | 50k |
DioneBridge::setSupportedBridge Only the admin can set supported bridges, and each chain ID must map to a valid non-zero bridge address. | Failed | 50k |
DioneBridge::setMaxSupply Only the admin can set the max supply for a specific token, it must be a non-zero address, and the new max supply must be stored correctly. | Failed | 50k |
DioneBridge::setMessageGasLimit Only the admin can set the gas limit for messages, ensuring it is stored correctly. | Passed | 50k |
DioneBridge::setTokenForReceive Only the admin can configure which tokens are supported for receiving from another chain, and the token operation must be valid. | Failed | 50k |
DioneBridge::setTokenToSend Only the admin can configure which non-zero address tokens are supported for sending to another chain, and the operation must be valid. | Failed | 50k |
DioneBridge::setWhitelist Only the admin can add or remove an address from the whitelist, and the whitelist state must be updated accordingly. | Passed | 50k |
DioneBridge::deposit The token must be supported, the correct fees must be deducted, and the remaining amount must be deposited into the contract. | Passed | 50k |
DioneBridge::redeem The token must be supported, the correct fees must be deducted, and the remaining amount must be burned, with the cross-chain withdrawal initiated. | Passed | 50k |
DioneBridge::rescueTokens Only the rescuer role can rescue tokens, and the rescued amount must be transferred to the specified address. | Passed | 50k |
DioneBridge::withdrawFees Only the admin can withdraw accumulated fees, ensuring the correct amount is transferred to the recipient, and the fees are updated. | Passed | 50k |
WmbAppUpgradeable::wmbReceive The wmbReceive function can only be called by the wmbGateway contract. The from address and fromChainId must be trusted according to the trustedRemotes mapping. Minting or withdrawing tokens operations must be successfully processed to ensure correct state transitions and token handling. | Passed | 50k |
WmbAppUpgradeable::setTrustedRemotes Only accounts with the DEFAULTADMINROLE can call setTrustedRemotes. The lengths of fromChainIds, froms, and trusted arrays must be equal. Updates the trustedRemotes mapping to reflect the provided trusted statuses for each remote address. | Passed | 50k |
Invariant
WDIONE::setFeeBps
Only the owner can setfeeBps
, and the new value must not exceedPCT_DIV
.Test Result
- Failed
Run Count
- 50k
Invariant
WDIONE::setFeeReceiver
ThefeeReceiver
must be a valid non-zero address, and only the owner can update it.Test Result
- Failed
Run Count
- 50k
Invariant
WDIONE::setBlacklist
Only the owner can add or remove an account from the blacklist, and the blacklist state must reflect the changes correctly.Test Result
- Passed
Run Count
- 50k
Invariant
WDIONE::deposit
The caller’sbalanceOf
must increase bymsg.value
, and the total supply must increase by the same amount.Test Result
- Passed
Run Count
- 50k
Invariant
WDIONE::withdraw
The withdrawal must not exceed the caller’s balance, and the withdrawn amount must be transferred to the caller.Test Result
- Passed
Run Count
- 50k
Invariant
WDIONE::approve
The allowance for the spender must be set to the provided amount, and the Approval event must be emitted.Test Result
- Passed
Run Count
- 50k
Invariant
WDIONE::transferFrom
The src address must have sufficient balance, the allowance must be correctly handled, and the transfer fee must be deducted if applicable.Test Result
- Passed
Run Count
- 50k
Invariant
WDIONEBridged::checkAndSendFees
The function sends collected fees to the target chain when the collectedFees exceed feeThreshold, ensuring sufficient ETH is available for the operation.Test Result
Run Count
- 50k
Invariant
WDIONEBridged::receive
The receive() function allows the contract to accept ETH payments to fund cross-chain fee transfers.Test Result
- Passed
Run Count
- 50k
Invariant
WDIONEBridged::mint()
The function, callable only by the bridge, mints tokens to the specified address, increasing the total supply accordingly.Test Result
- Passed
Run Count
- 50k
Invariant
WDIONEBridged::burn
The function, callable only by the bridge, burns tokens from the caller's balance if sufficient, decreasing the total supply.Test Result
- Passed
Run Count
- 50k
Invariant
WDIONEBridged::setFee
The function, callable only by the owner, sets the transfer fee in basis points, ensuring it does not exceed PCT_DIV.Test Result
- Failed
Run Count
- 50k
Invariant
WDIONEBridged::setFeeReceiver
The function, callable only by the owner, updates the fee receiver's address accurately.Test Result
- Failed
Run Count
- 50k
Invariant
WDIONEBridged::setFeeThreshold
The function, callable only by the owner, sets the feeThreshold which determines when fees are sent to the target chain.Test Result
- Passed
Run Count
- 50k
Invariant
WDIONEBridged::setWhitelist
The function, callable only by the owner, correctly adds or removes a single account from the whitelist.Test Result
- Passed
Run Count
- 50k
Invariant
DioneBridge::setWethAddress
Only the admin can set WETH_ADDRESS, and it must be a non-zero address.Test Result
- Failed
Run Count
- 50k
Invariant
DioneBridge::setSupportedBridge
Only the admin can set supported bridges, and each chain ID must map to a valid non-zero bridge address.Test Result
- Failed
Run Count
- 50k
Invariant
DioneBridge::setMaxSupply
Only the admin can set the max supply for a specific token, it must be a non-zero address, and the new max supply must be stored correctly.Test Result
- Failed
Run Count
- 50k
Invariant
DioneBridge::setMessageGasLimit
Only the admin can set the gas limit for messages, ensuring it is stored correctly.Test Result
- Passed
Run Count
- 50k
Invariant
DioneBridge::setTokenForReceive
Only the admin can configure which tokens are supported for receiving from another chain, and the token operation must be valid.Test Result
- Failed
Run Count
- 50k
Invariant
DioneBridge::setTokenToSend
Only the admin can configure which non-zero address tokens are supported for sending to another chain, and the operation must be valid.Test Result
- Failed
Run Count
- 50k
Invariant
DioneBridge::setWhitelist
Only the admin can add or remove an address from the whitelist, and the whitelist state must be updated accordingly.Test Result
- Passed
Run Count
- 50k
Invariant
DioneBridge::deposit
The token must be supported, the correct fees must be deducted, and the remaining amount must be deposited into the contract.Test Result
- Passed
Run Count
- 50k
Invariant
DioneBridge::redeem
The token must be supported, the correct fees must be deducted, and the remaining amount must be burned, with the cross-chain withdrawal initiated.Test Result
- Passed
Run Count
- 50k
Invariant
DioneBridge::rescueTokens
Only the rescuer role can rescue tokens, and the rescued amount must be transferred to the specified address.Test Result
- Passed
Run Count
- 50k
Invariant
DioneBridge::withdrawFees
Only the admin can withdraw accumulated fees, ensuring the correct amount is transferred to the recipient, and the fees are updated.Test Result
- Passed
Run Count
- 50k
Invariant
WmbAppUpgradeable::wmbReceive
The wmbReceive function can only be called by the wmbGateway contract. The from address and fromChainId must be trusted according to the trustedRemotes mapping. Minting or withdrawing tokens operations must be successfully processed to ensure correct state transitions and token handling.Test Result
- Passed
Run Count
- 50k
Invariant
WmbAppUpgradeable::setTrustedRemotes
Only accounts with the DEFAULTADMINROLE can call setTrustedRemotes. The lengths of fromChainIds, froms, and trusted arrays must be equal. Updates the trustedRemotes mapping to reflect the provided trusted statuses for each remote address.Test Result
- Passed
Run Count
- 50k
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.