ABSTRACT
BACKGROUND
The iLayer protocol enables intent-based token swapping across chains using existing infrastructure, currently integrating with Axelar and LayerZero. It also supports same-chain intents.
The protocol consists of three components: the OrderHub
, where users submit orders; the OrderSpoke
, which receives and manages those orders on the destination chain; and Routers
, which coordinate cross-chain communication and fund transfers between the two.
Users submit orders to the OrderHub
on the source chain, specifying input and output tokens. Permit2
is used for token approvals, allowing third parties to submit orders on behalf of users. Once submitted, the OrderHub
escrows the input tokens and sends a message to the corresponding OrderSpoke
, which creates a pending order.
On the destination chain, anyone can call the fillOrder
method to fulfill the order by transferring the output tokens to the designated recipient. After this transfer is completed, the OrderSpoke
notifies the source chain OrderHub
to release the escrowed input tokens to an address specified by the filler.
SETTINGS & CAVEATS
Audit Period
July 22 – July 28, 2025 (7 days)
Auditor Allocation
2 auditors
This audit report mainly covers the contracts of the iLayer core-v1 repository at commit 2ba39ba2124376140a04d858319e6cf14e978989
. Audit fixes were also reviewed up to commit 8c6d114b826aca927a2f651371fbd0e0e0f6d0b0
.
Contracts in Scope:
├── Executor.sol
├── interfaces
│ └── IRouterCallable.sol
├── libraries
│ ├── BytesUtils.sol
│ ├── OrderHelper.sol
│ └── PermitHelper.sol
├── OrderHub.sol
├── OrderSpoke.sol
├── Root.sol
├── RouterEnabled.sol
├── routers
│ ├── AxLzRouter.sol
│ ├── AxRouter.sol
│ ├── BaseRouter.sol
│ ├── LzRouter.sol
│ └── NullRouter.sol
└── Validator.sol
PROTOCOL-LEVEL CONSIDERATIONS
The OrderHub::withdrawOrder
function allows users to withdraw their locked tokens if their order has not been executed on the destination chain. The function uses a timeBuffer
value to enforce a grace period after the order’s deadline
:
function withdrawOrder(
Order memory order, uint64 orderNonce
) external nonReentrant {
address user = BytesUtils.bytes32ToAddress(order.user);
bytes32 orderId = getOrderId(order, orderNonce);
if (
user != _msgSender() ||
// Dedaub: grace period enforcement
order.deadline + timeBuffer > block.timestamp ||
orders[orderId] != Status.ACTIVE
) {
revert OrderCannotBeWithdrawn();
}
orders[orderId] = Status.WITHDRAWN;
// transfer input assets back to the user
// Dedaub: Code omitted for brevity
// ..
}
This timeBuffer
is intended to accommodate delays in message passing between chains, giving time for the order’s status to be updated on the source chain after being filled on the destination. However, if timeBuffer
is set too short relative to actual cross-chain message delays, users may be able to withdraw their tokens before the order’s fill status is reported back. This would mean the order filler would not receive the order inputs, despite having filled the order on the destination chain.
This is a real risk as cross-chain messaging protocols such as Axelar and LayerZero, which iLayer uses to relay messages cross-chain, can experience substantial delays due to relayer performance, chain finality, chain congestion, and other factors. According to Axelar documentation and recent Axelar GMP empirical data, the median time to relay a message on EVM chains is currently 20–30 minutes (e.g., Base 27m 28s, Ethereum 18m 46s), with some chains experiencing even longer times (e.g., Scroll 3h 42m 41s).
We recommend regularly reviewing and updating the timeBuffer
parameter based on empirical data from the slowest supported cross-chain path, taking into account observed relay times under load. Additionally, a conservative safety margin should be added to account for outliers and exceptional delays.
VULNERABILITIES & FUNCTIONAL ISSUES
This section details issues affecting the functionality of the contract. We generally categorize 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:
|
LOW | Examples:
|
Each issue has an identifier of the form [Letter][Number] (e.g., A1). The letter corresponds to the issue's severity, while the number is arbitrary.
CRITICAL SEVERITY:
[No critical severity issues]
HIGH SEVERITY:
[No high severity issues]
MEDIUM SEVERITY:
[No medium severity issues]
LOW SEVERITY:
[No low severity issues]
CENTRALIZATION ISSUES
It is often desirable for DeFi protocols to assume no trust in a central authority, including the protocol's owner. Even if the owner is reputable, users are more likely to engage with a protocol that guarantees no catastrophic failure even in the case the owner gets hacked/compromised. We list issues of this kind below. (These issues should be considered in the context of usage/deployment, as they are not uncommon. Several high-profile, high-value protocols have significant centralization threats.)
OrderHub
inherits the setRouter
from RouterEnabled
which allows the owner
to swap out the router address. A malicious owner could swap out the router to an implementation which calls onMessageReceived
to “fill” all the orders and steal all the escrowed funds.OrderSpoke::setFee
does not define a maximum fee, i.e., the fee can be set to 100%. This means that a malicious admin could exploit this function to set the fee to 100% and effectively steal all funds that pass through the protocol.Ownable::renounceOwnership
should be overridden with an empty implementation if protocol owners do not intend to use it.OTHER / ADVISORY ISSUES
This section details issues that are not thought to directly affect the functionality of the project, but we recommend considering them.
Client Response: The protocol assumes the trustworthiness of the underlying bridges.
In OrderSpoke::onMessageReceived
, the contract sets the order status to PENDING
without verifying its current status. While this is safe under the assumption that the underlying bridges never re-transmit (successfully executed) messages, it introduces risk if future versions of the bridge infrastructure contain bugs.
Specifically, a retransmission could cause an already filled order to revert to PENDING
, enabling multiple fillers to fulfill the same order. Adding a check before setting the status would alleviate this potential issue and lower the trust assumption on the underlying bridge.
The Axelar router currently does not provide a way to estimate interchain transaction gas fees on-chain. As confirmed by the Axelar documentation, on-chain estimation is not accurate and is also unavailable outside of testnet environments. Most dApps using Axelar rely on the off-chain gas estimation API to calculate and present gas estimates to users.
Whether this is a significant limitation depends on the protocol’s use case. If integrating customers are end-users interacting through the protocol’s dApp, the lack of on-chain estimation is generally not an issue, as the dApp can handle estimation offchain. However, if the protocol is intended to be used entirely on-chain or by other smart contracts, the absence of a reliable on-chain estimation method could cause operational issues or failed transactions due to gas miscalculations.
OrderHub::createOrder
, if order.callData.length > 0
, it should be verified that order.callRecipient
is not the zero address (address(0)
).OrderHub
’s onERC721Received
, onERC1155Received
, and onERC1155BatchReceived
should not be trusted by any off-chain system, as these functions can be freely called by anyone.OrderSpoke::fillOrder
should check that fundingWallet != hubs[order.sourceChainId]
and fundingWallet != address(0)
. Without these checks, it is possible for the protocol’s own hub address or the zero address to be set as the fundingWallet
. This could result in funds being locked or irretrievably lost. Adding these checks would help prevent accidental misconfiguration.OrderHub::_checkOrderValidity
could perform zero checks on the user
and recipient
to avoid simple misconfiguration errors. Additionally, the input and output token addresses could be validated (excluding the native token, if needed) to prevent invalid (zero) addresses from being used. Since filler
and callRecipient
are optional, checks are not required for those fields. Without these validations, there is a risk that invalid or unintended addresses are provided, which could result in funds being lost or orders failing unexpectedly.OrderHub
implements onERC1155BatchReceived
even though the protocol does not support batch transfers of ERC1155 tokens.In OrderHub::withdrawOrder
, the if
condition can be split into three separate conditions for better error reporting:
if (user != _msgSender()) {
revert OrderCannotBeWithdrawnByOtherUser();
}
if (order.deadline + timeBuffer > block.timestamp) {
revert OrderCannotBeWithdrawnTooEarly();
}
if (orders[orderId] != Status.ACTIVE) {
revert OrderCannotBeWithdrawnNotActive();
}
Root
defines the enum Status
, which is used by OrderHub
, while OrderSpoke
defines a separate enum OrderStatus
. Since Root::Status
is not reused by OrderSpoke
, it should be moved to OrderHub
.Client Response: This information is consumed by the off-chain network of solver bots.
The boolean field sponsored
in the Root::Order
struct is not read on-chain. This suggests it is currently unused and could be removed to simplify the code and reduce storage costs.
There exist the following unnecessary imports:
OrderHelper.sol
MessagingReceipt
NullRouter.sol
BytesUtils
IRouterCallable
Ownable
When using enums in Solidity, the compiler automatically inserts range checks for values passed into the contract. While this enhances correctness, it introduces challenges for upgradeability.
A key example is the Bridge
enum. Although the router implementation can be upgraded/changed to support additional bridges, the OrderHub will reject any bridge value not already defined in the Bridge enum.
_execute
function of both AxRouter
and AxLzRouter
could revert with UnsupportedAxChain
if srcChainId == 0
, assuming that the zero chain ID is generally unused.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.