LayerZero engaged Guardian to perform three rounds of security review on the Console codebase. From January 19th to March 25th, a team of four auditors conducted a comprehensive assessment of the source code. All findings identified throughout the three rounds have been recorded in the following report.
- Published
- Rounds
- Round One, Round Two, Round Three
- Language
- Solidity
- Chains
- Ethereum, Solana, Stellar, Canton
- Sector
- Cross-chain
- 0 Critical
- 0 High
- 2 Medium
- 2 Low
- 72 Informational
Scope
Overview
LayerZero engaged Guardian to perform three rounds of security review on the Console codebase. From January 19th to March 25th, a team of four auditors conducted a comprehensive assessment of the source code. All findings identified throughout the three rounds have been recorded in the following report.
Findings 76
Round One
68 findings-
M-01 Medium Nexus composeFrom Is Not Original Sender Logical Error Resolved
Description
In Nexus, both compose identity fields resolve to the
NexusOFTwrapper instead of the original user. Standard OFT behavior preserves the OFT contract as_fromand the end user ascomposeFrom, but the Nexus call flow causesOFTMsgCodec.encode()to capture the wrapper as the compose initiator. As a result, receiving contracts cannot reliably identify or authenticate the original sender. Any downstream logic that depends on user-specific authorization or accounting sees every compose as coming from the same wrapper address.Recommendation
Use Nexus-specific message encoding that writes the original user into
composeFrominstead of relying on the currentmsg.sendercontext.Resolution
LayerZero: Resolved.
-
M-02 Medium Missing Overflow Validation Validation Resolved
Description
Proof of concept: PoC
In the
DecimalUtilsfile the_toSDdoes not perform any validation to ensure that the result of the decimal conversion does not overflow auint64. ``` function _toSD(uint256 _amountLD) internal view virtual returns (uint64 amountSD) { return uint64(_amountLD / DECIMAL_CONVERSION_RATE); } ``` As a result, large crosschain transfers for tokens with a high supply can result in the sender burning a huge amount of tokens and receiving a very small amount of tokens out, representing a complete loss of assets.Recommendation
Add validation on the result of
_amountLD / DECIMAL_CONVERSION_RATEto ensure that it does not overflow auint64before casting it as such.Resolution
LayerZero: Resolved.
-
L-01 Low quoteOFT Doesn't Account For Fees Warning Resolved
Description
The
quoteOFTfunction returnsmaxAmountLDdirectly from the rate limiter's available outbound capacity. However, the rate limiter enforces limits onamountReceivedLD(post-fee amount), whilemaxAmountLDis meant to represent the maximumamountSentLD(pre-fee amount) a user can submit. When fees are configured, users can actually send more than the reported maximum. For example, with a rate limit of 1000 and a 10% fee, users can send approximately 1111 tokens (resulting in 1000 received), butquoteOFTreports a maximum of 1000. This causes UIs and integrations relying on this value to understate available capacity.Recommendation
Consider accounting for the fee in
quoteOFTfor themaxAmountLDcalculation.Resolution
LayerZero: Resolved.
-
L-02 Low quoteOFT Misses outboundEnabled Logical Error Resolved
Description
The
nexusQuoteOFTandquoteOfton the Portal uses themaxAmountLDas presented by thegetRateLimitUsagesfunction. However this function does not account for when the config bitmap of the rate limit indicates that the outbound rate limit is disabled. As a result thenexusQuoteOFTfunction will present inaccuracies in themaxAmountLDwhen theoutboundEnabledconfig value for a rate limit isfalse.Recommendation
Take into consideration whether the outbound pathway is enabled or not for the rate limit in the quote functions.
Resolution
LayerZero: Resolved.
-
I-01 Informational Some Tokens Cannot Bridge Back Full Supply Warning Acknowledged
Description
Due to the way the fee logic works, in some cases it is impossible for a networks supply to go entirely to zero while a fee is configured. Consider the following example:
- A token uses a lockbox OFT on Network A, and a a MintBurn OFT on Network B
- Network B has an outgoing fee of 50 BIPs and a token supply of X, backed by X tokens on Network
A in the lockbox
- Token on Network B has 18 decimals and 6 shared decimals
- Even if all X tokens are transferred back to Network A, a fee of 50X/10000 will be taken and kept on
Network B
- Since the dust removal logic favors counting dust removed as fees, a remaining amount that is
below the conversion rate will just simply not be transferrable back
- Such a dust amount is guaranteed to always exist as long as the fee does not get truncated to zero
Recommendation
Be aware that a dust supply of a token will be trapped on a network unless the fee is removed.
Resolution
LayerZero: Acknowledged.
-
I-02 Informational IMintableBurnable Return Values Ignored Warning Acknowledged
Description
The IMintableBurnable interface shows that both
burn()andmint()returnbool success. However, in Nexus.sol, these return values are ignored. If the underlying token implementation returns false instead of reverting, the operations will fail silently, leading to:- Tokens not actually burned but message sent (unbacked mint on destination)
- Tokens not actually minted but message processed (user doesn't receive funds)
Recommendation
Check return values or document that tokens that revert on failure should be used.
Resolution
LayerZero: Acknowledged.
-
I-03 Informational Pausing Asymmetry Between Debit And Credit Documentation Acknowledged
Description
The
whenNotPaused(_nexusId)modifier appears only on_debit, which is the send path. The_creditfunction used for receiving tokens has no pause check. This means that pausing stops outbound transfers but allows inbound which may be unexpected.Recommendation
Document this asymmetry clearly.
Resolution
LayerZero: Acknowledged.
-
I-04 Informational Misleading Fee Balance View Function Warning Acknowledged
Description
The Nexus's fees accumulated is simply calculated as
IERC20(_token).balanceOf(address(this));within functiongetFeeBalance. It should be clearly noted to users that if tokens are directly sent to the Nexus, they'll be counted as withdrawable fees.Recommendation
Clearly document this behavior.
Resolution
LayerZero: Acknowledged.
-
I-05 Informational Allowlist Not Enforced On Approvals And Permit Warning Acknowledged
Description
The allowlist checks are only applied to token movement functions (transfer/transferFrom/burn) but not to approval state changes. Blacklisted or non-whitelisted users can set or increase allowances through functions
approve/increaseAllowance/decreaseAllowanceandpermitwhile disallowed by the allowlist. These allowances can be executed immediately once the owner/spender becomes allowlisted.Recommendation
Clearly document that approval changes do not verify allowlist.
Resolution
LayerZero: Acknowledged.
-
I-06 Informational Approvals And Permit Usable During Global Pause Warning Acknowledged
Description
Functions such as
approveandpermitare not gated bywhenNotGloballyPaused. While transfers are paused, actors can set allowances and signatures that can be executed immediately after unpause, which should be documented.Recommendation
Clearly document this behavior.
Resolution
LayerZero: Acknowledged.
-
I-07 Informational Owner Must Pass Downscaled Value Documentation Acknowledged
Description
Rate‑limit configs/states expect downscaled units when SCALE_DECIMALS > 0, but only
getRateLimitUsagesupscales for display, so an admin passing base decimal limits will silently set much larger caps and lead to a misconfigured limit.Recommendation
Add explicit documentation on IRateLimiterCore setters and RateLimiterUpgradeable admin functions stating inputs are downscaled units.
Resolution
LayerZero: Acknowledged.
-
I-08 Informational Closed Rate Limit Allows Zero-Amount Messaging Warning Acknowledged
Description
Setting the rate limit to closed (
limit = 0) blocks non‑zero transfers, but zero‑amount sends still pass the rate‑limit checks. This allows messages (including optional compose payloads) to be emitted even when operators expect the rate limiter to fully halt activity. If downstream logic assumes that "closed" means no cross‑chain messages, this creates an unexpected bypass for message‑only actions.Recommendation
If the intended behavior is to block all messaging when closed, add an explicit
amount > 0check in the send path. Otherwise, document that "closed" only blocks value transfer and that pause must be used to halt messaging.Resolution
LayerZero: Acknowledged.
-
I-09 Informational Withdraw Fees May Drain Balance On Override Logical Error Acknowledged
Description
FeeCoreUpgradeablecomments suggest overrides may track fees viabalanceOf(this)instead of storage. That pattern is safe for mint/burn OFTs, but not for lock/unlock OFTs where the contract holds both user deposits and accrued fees. If an override followed the documentation literally in a lock/unlock setup,withdrawFees()could treat the full contract balance as fees and drain user funds along with the real fee balance. Current implementations are safe, but the guidance is too broad.Recommendation
Clarify in the
getFeeBalance()and_resetFeeBalance()documentation that balance-based fee tracking is only safe when contract balances do not also include user-held liquidity.Resolution
LayerZero: Acknowledged.
-
I-10 Informational Incorrect Registry Storage Location Documentation Resolved
Description
In the
OFTRegistryCoreUpgradeablecontract theOFT_REGISTRY_CORE_STORAGE_LOCATIONis stored as0x5e17fe86d24c27c5744e7e62d5c0e4b546c352da7d7ccf57d4d9d4da22a06900with a comment indicating it is computed askeccak256(abi.encode(uint256(keccak256("layerzerov2.storage.oftregistrycore")) - 1)) &~bytes32(uint256(0xff)). Howeverkeccak256(abi.encode(uint256(keccak256("layerzerov2.storage.oftregistrycore")) - 1)) &~bytes32(uint256(0xff))evaluates to0x3061291cb11960a6131e0dc3984d775ee9ea1d64c1334ba80fc584e9cc0bf400which does not match the storage location used.Recommendation
Correct the comment or the storage location bytes.
Resolution
LayerZero: Resolved.
-
I-11 Informational Allowlist Mode Switching Preserves Inactive Set Warning Acknowledged
Description
When switching between Blacklist and Whitelist modes via
setAllowlistMode(), the contract only updates the active mode but does not clear the inactive set. BothblacklistedSetandwhitelistedSetpersist in storage regardless of the current mode. If the owner toggles modes and later switch back, previously configured entries immediately become active again. For example, blacklisting users, switching to Whitelist mode, then returning to Blacklist mode will re-activate all previous blacklist entries without additional transactions.Recommendation
Clearly document this behavior.
Resolution
LayerZero: Acknowledged.
-
I-12 Informational Fee Withdrawal Blocked By Allowlist Settings Documentation Acknowledged
Description
In whitelist or blacklist modes,
PortalERC20.transferandNexusERC20.transferrequires the caller and recipient to be allowlisted. Nexus uses transfer when withdrawing fee balances to a recipient. Ifaddress(Nexus)or the recipient is not allowlisted (or is blacklisted),withdrawFeesreverts, leaving fee balances stuck in the Nexus contract.Recommendation
Clearly document that the Nexus must be allowlisted as well as the recipient.
Resolution
LayerZero: Acknowledged.
-
I-13 Informational Allowlist Can Block Fund Recovery Warning Acknowledged
Description
PortalERC20.recoverFundsandNexusERC20.recoverFundsexplicitly revert when the_fromaddress is allowlisted. Fee withdrawal from Nexus uses token transfers, which requireaddress(Nexus)to be allowlisted in whitelist mode. As a result, if the protocol allowlists Nexus to enable fee withdrawals, fund recovery from the Nexus address is always blocked by design.Recommendation
Clearly document this behavior
Resolution
LayerZero: Acknowledged.
-
I-14 Informational Upgradeable Immutables Can Change Behavior Warning Acknowledged
Description
Several upgradeable contracts rely on immutable values (e.g., SCALE_DECIMALS, decimal conversion rates). Upgrading to an implementation compiled with different immutables silently changes behavior without storage migration, potentially altering rate‑limit math and token decimal handling.
Recommendation
Document this and ensure deployments are done without changing immutables.
Resolution
LayerZero: Acknowledged.
-
I-15 Informational Exempt Users Can Reduce Net Accounting Warning Acknowledged
Description
In the
_applyRateLimitfunction users who are marked as exempt can avoid the rate limits, as expected. However, when a rate limit has net accounting enabled, an exempt user can abuse this. Consider the following scenario:- Chain A is the current chain
- Chain A has the inbound and outbound rate limits enabled
- Chain A has net accounting enabled
- Chain A currently has a 5e18 usage on the outbound rate limit, and the maximum is 10e18
- An exempt user bridges out 5e18 tokens and avoids the rate limit
- The exempt user then transfers these tokens to a different address under their control
- The exempt user then bridges them back to Chain A
- Since Chain A uses net accounting, the Chain’s outbound rate limit usage is decreased to 0.
Recommendation
Be aware of this ability from exempt addresses, and consider this carefully for setups that use both exempt addresses and net accounting. All exempt addresses must be trusted entirely.
Resolution
LayerZero: Acknowledged.
-
I-16 Informational Zero Amounts Cause Rate Limit Precision Loss Rounding Acknowledged
Description
Zero value sends are allowed, however they do update the rate limit decay which uses round down division. If a smaller limit is used relative to the length of the window for a rate limit, some precision loss can be forced through zero amount transfers maliciously occurring at a low interval to keep the rate limits down marginally.
Recommendation
Be aware of this rounding and consider disallowing zero value OFT send.
Resolution
LayerZero: Acknowledged.
-
I-17 Informational Rate Limits Consumed DoS Warning Resolved
Description
It is possible for anyone to purposely consume the rate limits when net accounting is not enabled by simply transferring out and then back into the src chain.
Recommendation
Be aware of and document this loophole so that those who would like to avoid it can use net accounting.
Resolution
LayerZero: Resolved.
-
I-18 Informational Lacking Configuration List Duplicate Check Validation Acknowledged
Description
In the
RateLimiterCoreUpgradeablecontract_setRateLimitConfigsand_setRateLimitStatesconfiguration functions there is no validation that theparam.idhas not already been configured in the list before. If a parameter with the same id were to be provided it could lead to an unexpected configuration overwrite.Recommendation
Be aware of this lacking validation and consider adding it to validate against any potential mishaps during configuration. Otherwise be sure to review configurations to ensure that only unique param ids are being configured.
Resolution
LayerZero: Acknowledged.
-
I-19 Informational Nexus And Portal Tokens Should Have Low Supply Warning Acknowledged
Description
The Nexus and Portal implementations use a value of 0 for the
_scaleDecimalshardcoded in their constructor. As a result, to avoid any unexpected conditions these systems should avoid using tokens what may have a total supply greater than ~79 Billion tokens using 18 decimals.Recommendation
Be aware of this limitation and consider it when using tokens with these systems.
Resolution
LayerZero: Acknowledged.
-
I-20 Informational Unlimited Transfers With Net Accounting Logical Error Acknowledged
Description
When one transfer direction is disabled but
netAccountingEnabledremains on, users can send unlimited volume through the disabled direction and still reduce usage in the opposite direction. That lets them repeatedly restore capacity on the enforced side and bypass the intended rate limit. For example, if outbound is disabled, inbound is limited, and net accounting is enabled, a user can send tokens outbound without restriction, reduce inbound usage, then immediately consume the restored inbound capacity. Repeating the cycle turns a configured limit into effectively unlimited throughput.Recommendation
Document this behavior clearly so operators understand that disabling one direction can still affect the opposite direction when net accounting is enabled.
Resolution
LayerZero: Acknowledged.
-
I-21 Informational Non-exempt Users May Bypass Inbound Rate Limits Warning Acknowledged
Description
In the
_applyRateLimitfunction the_useraddress provided is used to check whether the user is an exempt address for the rate limit check. On the sending side, this user is the address that initiated the action and sent the tokens. However on the receiving side, this_useraddress represents the receiving side, and anyone can initiate transfers to this address.Recommendation
Consider if this is the desired way to check whether a crosschain transfer should be exempt from the rate limit, or if there should be some authentication also based on the authorization of the sender address from the sending chain. Though this introduces some complexity when receiving from a non-EVM compatible network. Generally, be aware of this ability for anyone to transfer to the addresses and contracts that are considered exempt from the inbound rate limits, and document it accordingly.
Resolution
LayerZero: Acknowledged.
-
I-22 Informational De-registration Risk Warning Acknowledged
Description
If a token is de-registered while there are in-flight messages, then those messages will get stuck in the channel. If the token is re-registered or another token is registered using the same tokenId, then those stuck messages will be executable again unexpectedly.
Recommendation
To avoid any unexpected behavior, consider blacklisting a tokenId after it is de-registered and requiring that a new one is used. Furthermore, handle token registration across all chains with extreme care, as it must be done in a timely manner and with exact accuracy every time.
Resolution
LayerZero: Acknowledged.
-
I-23 Informational Minting Bypasses Global Pause Warning Acknowledged
Description
The
mintfunction in both PortalERC20 and NexusERC20 lacks thewhenNotGloballyPausedmodifier, allowing addresses with theMINTER_ROLEto mint tokens even when the system is globally paused. Whiletransfer,transferFrom, andburnare all gated by pause checks, minting remains unrestricted. Consequently token supply can increase during emergency pause conditions when all other token movement is frozen.Recommendation
Clearly document this behavior.
Resolution
LayerZero: Acknowledged.
-
I-24 Informational Recover Bypasses Validations Warning Acknowledged
Description
The
recoverFundsfunction in PortalERC20 and NexusERC20 validates that the source address is not allowlisted but performs no validation on the destination address. By usingsuper._transfer()instead of the standard transfer function, it bypasses both pause checks and allowlist enforcement on the recipient. This allows aFUND_RECOVERY_ROLEholder to transfer recovered tokens to any address, including those that are blacklisted.Recommendation
Clearly document this behavior.
Resolution
LayerZero: Acknowledged.
-
I-25 Informational Missing InvalidLocalDecimals Error Validation Resolved
Description
In the
DecimalUtilsutility contract there is no logic to explicitly validate the local decimals are greater than or equal to the shared decimals. As a result, in the event that the shared decimals is specified to be larger than the local decimals the deployment will revert with a non-descriptive panic revert.Recommendation
Consider implementing an explicit validation that the shares decimals is not greater than the local decimals and reverting with a
InvalidLocalDecimalserror similarly to the canonical OFT implementation.Resolution
LayerZero: Resolved.
-
I-26 Informational Incorrect Max Amount Reported Unexpected Behavior Acknowledged
Description
When the rate limiter is globally disabled,
getRateLimitUsages()reportstype(uint256).maxas the maximum transferable amount. That is misleading because Nexus amounts are still bounded by OFT shared-decimal constraints, so values anywhere nearuint256.maxare not actually transferable. The canonical OFT implementation reportstype(uint64).max, and the most accurate bound here istype(uint64).max * DECIMAL_CONVERSION_RATE. Returninguint256.maxtherefore overstates what callers can really send.Recommendation
Match the canonical OFT behavior by reporting
type(uint64).max, or return the stricter bound oftype(uint64).max * DECIMAL_CONVERSION_RATE.Resolution
LayerZero: Acknowledged.
-
I-27 Informational Enforced Options Cannot Be Applied Individually Compatibility Acknowledged
Description
A Nexus instance may support multiple NexusOFTs with different underlying token implementations. One underlying token implementation may have special logic or callback hooks on the destination chain and thus may require more gas than other underlying tokens that are supported by the Nexus. However the combineOptions function does not allow options to be enforced per nexus id, only per Eid. This leaves the Nexus with rigid option enforcement that cannot fully adapt to the various tokens that may be a part of the Nexus.
Recommendation
Consider implementing a version of
combineOptionsthat allowsenforcedOptionsto be specified on a nexus id level instead of an Eid level.Resolution
LayerZero: Acknowledged.
-
I-28 Informational Some Tokens Incompatible Compatibility Acknowledged
Description
The Nexus contract uses an
IMintableBurnableinterface that expects a boolean return value from themintandburnfunctions, however not all tokens who expose these functions return a boolean. For example, the GMX token on Arbitrum implements a MintableBaseToken where the mint and burn functions do not return any return data. These tokens are incompatible out of the box with the system as the invocations of mint and burn will revert due to unexpected lack of returndata. These tokens would need adapters to be correctly supported.Recommendation
Consider removing the return values from the
IMintableBurnableinterface as they are not currently used and instead limit the compatibility of the system.Resolution
LayerZero: Acknowledged.
-
I-29 Informational Lacking ERC7802 Compliance Compatibility Acknowledged
Description
The Nexus system implements minting and burning calls on a
minterBurnercontract meant for crosschain mint/burn actions. There is an EIP7802 which is intended to support exactly this, and is used by the most popular cross-chain stablecoin on OFT rails, USDT0, however the Nexus system does not adhere to this EIP and instead defines it’s ownIMintableBurnableinterface.Recommendation
Consider adopting the EIP7802 interface for the Nexus system.
Resolution
LayerZero: Acknowledged.
-
I-30 Informational Trapped ETH Through lzReceive Warning Acknowledged
Description
The lzReceive function does not implement any validation that ensures that the
msg.valueprovided is zero and there are no ETH rescue functions, therefore any message that is executed with nonzeromsg.valuetraps that ETH in the Nexus contract.Recommendation
Consider validating that the
msg.valueis zero in_lzRecieveor adding a rescue native function.Resolution
LayerZero: Acknowledged.
-
I-31 Informational Redundant Sources Of Truth Best Practices Resolved
Description
The NexusOFT contract stores a
TOKEN_IDwhich is immutable and stored at construction time, however the Nexus stores each OFT by a tokenId that is provided by the owner who registers it, with no validation that this id matches the one returned from theNexustOFT.tokenIdfunction. This leaves room for incorrect configurations and creates parallel tracking for the same information in theNexusandNexusOFTcontracts.Recommendation
Consider either having the
NexusOFTcontract fetch theNEXUS.getTokenId(address(this))result as the implementation for thetokenIdfunction (less gas efficient, but deduplicates state), or validating that the reported tokenId() on theNexusOFTinstance matches thetokenIdit’s being configured for in theregisterTokenfunction.Resolution
LayerZero: Resolved.
-
I-32 Informational quoteSend Ignores Pausability Or Rate Limits Warning Acknowledged
Description
quoteSendreturns a validMessagingFeewithout checking pause state or rate limits. It calls_debitViewwhich performs pure calculations. However, the actualsendcalls_debitwhich haswhenNotPausedmodifier and enforces rate limits with_outflow. Users can receive valid-looking quotes for sends that will revert.Recommendation
Clearly document this behavior to integrators.
Resolution
LayerZero: Acknowledged.
-
I-33 Informational Breaking MsgInspector Interface Update Compatibility Acknowledged
Description
The old
IOAppMsgInspectorinterface defined an inspect function with the signatureinspect(bytescalldata _message, bytes calldata _options), however the newIMsgInspectorinterface defined forNexusandPortalexposes only ainspect(address _sender, bytes calldata _message, bytes calldata_options)signature. As a result old message inspector implementations will not be compatible with the Nexus and Portal.Recommendation
Be aware of this breaking change and clearly document it for those building on Nexus and Portal.
Resolution
LayerZero: Acknowledged.
-
I-34 Informational Mints Can Occur While Token Is Paused Documentation Acknowledged
Description
The
mintfunction states thatIt does not revert if the recipient is not allowlisted, as funds cannot bedebited in that state, however as a byproduct of ignoring the checkTransfer validation themintfunction also ignores the pause status of the underlying token. This could be intentional to not hold uplzReceiveactions and allow them to be executed, however it is important to note that mints can still occur, through receiving crosschain messages or other arbitrary mints by theMINTER_ROLEholders.Recommendation
Be aware of this gap in pause validation and consider if it is expected for all cases in which
mintcan be called. Consider if theisPausedfunction on thePausecontract should be used to particularly check if the pause status should be consulted before allowing certain mint actions.Resolution
LayerZero: Acknowledged.
-
I-35 Informational Payouts Spend Fees From Same Pool Warning Acknowledged
Description
PortalOFTNativeandPortalOFTLockUnlockaccrue fees infeeBalances, but recipient credits are paid from the same native token pool. Because fees are not reserved separately, bridge payouts can consume balance that accounting still treats as withdrawable fees. In unsupported configurations where remote supply is not fully backed by locked assets, tokens can be bridged back and drain the pool below the tracked fee balance.withdrawFees()may then revert even though fees were previously accrued.Recommendation
Use only configurations where remote token supply remains properly backed and cannot dilute the balances relied on to pay out accrued fees.
Resolution
LayerZero: Acknowledged.
-
I-36 Informational Scale Decimals Are Not Exposed Compatibility Partially resolved
Description
In both the Nexus and Portal implementations that use the
RateLimiterUpgradeable, thescaleDecimalsare unused and not able to be configured in the constructor. SinceSCALE_DECIMALSis an immutable value it’s not possible to configure a nonzero scale decimals for any token using Portal or Nexus out of the box. Rate limit amounts are applied on a local decimals basis, so any tokens which may reasonably have overtype(uint96).max(notably tokens with a totalSupply over 79 Billion tokens and 18 decimals) cannot employ the necessary scaling out of the box.Recommendation
Consider exposing the
scaleDecimalsthrough the constructor to support such tokens out of the box.Resolution
LayerZero: Partially Resolved.
-
I-37 Informational quoteOFT Does Not Consult Guard Unexpected Behavior Acknowledged
Description
The
quoteOFTfunction in Nexus does not consult the token guard to check if the caller is allowlisted for the token in the first place.Recommendation
Consider if
quoteOFTshould take into account the status of the caller in the guard.Resolution
LayerZero: Acknowledged.
-
I-38 Informational Initialize Must Be Invoked During Deployment Best Practices Acknowledged
Description
All upgradeable contracts in this repo rely on initialize(...) for ownership and critical configuration. If a proxy instance is deployed without calling initialize in the same transaction, any external account can front‑run and call initialize first, seizing ownership/admin roles and permanently compromising the instance.
Recommendation
Be sure to initialize every deployment in the same transaction to avoid any front-running initialization risks.
Resolution
LayerZero: Acknowledged.
-
I-39 Informational Nexus Doesn’t Support Native Inner Tokens Suggestion Acknowledged
Description
Portal contracts can support native tokens as the underlying token for the OFT, however Nexus contracts do not expose a variant to support any native tokens as they only contain the min/burn variant.
Recommendation
Consider if this is intended and whether Nexus should be able to support a native token (ERC20 form or canonical native) as an inner token similarly to how the
PortalOFTLockUnlockandPortalOFTNativeversions of the Portal OFT can.Resolution
LayerZero: Acknowledged.
-
I-40 Informational New Network Supports Allows Circumvention Warning Acknowledged
Description
In
Nexusthe fees, rate limits, and pause state are based upon thenexusIdwhich depends on the destination EID in addition to thetokenId. As a result, when a new peer is configured for the Nexus for a new Eid there is an opportunity to bypass all of the existing network exit pathway fees, pause state, and rate limits by sending to this new destination EID before it’s fee, pause state, and rate limits are configured for each of the tokenIds.Recommendation
Be sure to configure the pause state, fees, and rate limits for every token as necessary on a
Nexusbefore configuring a new EID for theNexus.Resolution
LayerZero: Acknowledged.
-
I-41 Informational Lacking Pause Idempotency Check Best Practices Acknowledged
Description
In the
PauseCoreandPauseCoreUpgradeablecontracts, the_setDefaultPausedfunction includes an idempotency check to validate that the configuration being made actually changes state, however no such validation exists for the configurations that are made in the_setPausedfunction for the individual pause configurations.Recommendation
Consider introducing an idempotency check in the
_setPausedfunction to match the validation performed in the_setDefaultPausedfunction.Resolution
LayerZero: Acknowledged.
-
I-42 Informational Event Log Manipulation With Reentrancy Events Acknowledged
Description
PortalOFTNative._lzReceive()calls_credit()before emittingOFTReceived. Because_credit()can transfer native value to a recipient that re-enters, anotherlzReceiveorsendcan execute before the original receive path emits its completion events. This does not appear to break core accounting, but it can cause event consumers and indexers to observeOFTReceivedandPacketDeliveredin an order that does not match the order in which operations actually completed.Recommendation
Document this ordering caveat so integrators consuming
OFTReceivedandPacketDelivereddo not assume strict completion order from event sequence alone.Resolution
LayerZero: Acknowledged.
-
I-43 Informational NexusAlt Lacks Sanity Check Validation Resolved
Description
The other Alt contracts verify that this is indeed a network which uses an ERC20 representation of the native token by validating that the
nativeTokenresult from the endpoint is nonzero. However theNexusAltcontract, which does not need this token directly but is still intended only for these networks, does not implement such a validation.Recommendation
Consider implementing sanity validation in the
NexusAltconstructor that theENDPOINT.nativeToken()result is nonzero to verify that this is indeed an Alt network.Resolution
LayerZero: Resolved.
-
I-44 Informational Missing MinterBurnerCallFailed Implementation Superfluous Code Resolved
Description
The
PortalOFTMintBurncontract defines aMinterBurnerCallFailederror and stipulates that it isThrown when a low-level call to MINTER_BURNER fails. However this error is not thrown when a low-level call to theMINTER_BURNERfails and is not used at all.Recommendation
Consider removing the
MinterBurnerCallFailederror.Resolution
LayerZero: Resolved.
-
I-45 Informational Mint/Burn Result Ignored By PortalOFTMintBurn Compatibility Acknowledged
Description
The
PortalOFTMintBurnimplementation intends to support arbitrary mint and burn functions so long as they accept anaddressfollowed by auint256as parameters. However thePortalOFTMintBurncontract is not compatible with mint/burn functions that fit this signature and return false on failure rather than reverting. This is because the_callMinterBurnerfunction simply delegates to the OpenZeppelinfunctionCallfunction which verifies the success of the call but does not assume or assert anything about the returndata contents.Recommendation
Either explicitly document this or support asserting a returned boolean when there is nonzero returndata present.
Resolution
LayerZero: Acknowledged.
-
I-46 Informational Credit Can Consume An Unexpected Amount Of Gas DoS Acknowledged
Description
Because the
PortalOFTNativeautomatically loads in thereturnDatainto memory during_credit, this function is susceptible to gas griefing or an OOG when a large amount of bytes are returned:(boolsuccess, bytes memory data) = payable(_to).call{ value: _amountLD }("");The arbitrary call is also forwarded the maximum available 63/64 amount of gas in the current transaction. Which gives thetoaddress the ability to expend more gas than desired.Recommendation
Consider implementing a low level assembly call and specifying 0 as the length of return data to copy into memory to avoid any returndata bombs. Furthermore consider capping the gasLimit that is forwarded to the
toaddress to avoid any unexpected expenditures.Resolution
LayerZero: Acknowledged.
-
I-47 Informational Unsynchronized Limits Can Lead To Stuck Funds Warning Acknowledged
Description
The configured rate limits can be different between chains where the Oapp contracts are deployed. If the source chain allows sending more than the destination allows for receiving, transactions with an amount between these two limits can be sent but cannot be received on the destination chain because the corresponding transactions to
lzReceive()will revert.Recommendation
Clearly document this risk to users.
Resolution
LayerZero: Acknowledged.
-
I-48 Informational Rate Limit Bypass Through Exempt Address Warning Acknowledged
Description
The address-exemption feature checks
msg.sender, not the beneficial token owner. If an exempt router, aggregator, or similar address is allowed to callsend(), non-exempt users can route through it and bypass rate limits for that destination. The same issue can extend to exempt EOAs using mechanisms like EIP-7702. Once an exempt caller executes the transfer path, all users behind that caller inherit the exemption in practice.Recommendation
Treat exempt addresses as high trust, monitor how they are used, and remove exemptions if they allow third parties to bypass intended rate-limit controls.
Resolution
LayerZero: Acknowledged.
-
I-49 Informational Fee-on-Transfer/Rebasing Tokens Not Supported Warning Acknowledged
Description
On credit, the contract transfers
_amountLDto the recipient without accounting for tokens that charge a transfer fee/deflate on transfer. The recipient will receive less than_amountLDwhile the protocol still considers the message fully delivered, creating an unaccounted shortfall between the escrowed amount and the actual tokens received.Recommendation
Disallow fee-on-transfer and rebasing tokens.
Resolution
LayerZero: Acknowledged.
-
I-50 Informational Tokens With Inconsistent Decimals Incompatible Compatibility Acknowledged
Description
Some tokens have different decimals depending on the network being used such as USDT which has 6 decimals on most chains but 18 decimals on BNB. These tokens are incompatible with the Nexus because there is a requirement that all tokens in the nexus have the same local decimals on all networks the Nexus is deployed on.
Recommendation
Be aware of this constraint when allowing tokens to be a part of a Nexus. Alternatively, consider supporting solutions for differences in local decimals across tokens on various networks since they may not always be aligned on every network.
Resolution
LayerZero: Acknowledged.
-
I-51 Informational PortalOFTNative Stuck Message Risk Warning Acknowledged
Description
The
PortalOFTNativehas a risk of crosschain messages being stuck if they were to target atoaddress which is a contract that does not accept ether. In this case the message would be stuck in the message channel and there would be no way to rescue that Ether.Recommendation
Document this risk and consider performing some initial offchain validations in the UI to protect users from such a risk.
Resolution
LayerZero: Acknowledged.
-
I-52 Informational Missing Events Events Resolved
Description
Events are missing in the following functions that could benefit from them:
- RateLimiterCoreUpgradeable._setRateLimitGlobalConfig
- FeeCoreUpgradeable._accrueFee
Recommendation
Consider implementing the appropriate events for these cases.
Resolution
LayerZero: Resolved.
-
I-53 Informational Rate Limit Quotes Can Be Misleading Unexpected Behavior Acknowledged
Description
The quote functions only focus on the rate limits for the sending chain and cannot include any information about the rate limit on the receiving chain, which may be misleading for large transfers. There may be a scenario where a transfer is wholly larger than the limit on a receiving chain and can in fact never fit within the limit, in which case the crosschain message would be stuck until the rate limit is raised just for this message.
Recommendation
Document this gap in the current quoting functions.
Resolution
LayerZero: Acknowledged.
-
I-54 Informational Outside Tokens Do Not Follow The Guard Compatibility Acknowledged
Description
In the Nexus setup when an outside token is used which does not consult the
NexusERC20Guardbefore actions, there can be a divergence between the allowlist/blacklist status of the normalNexusERC20tokens and this outside token. For example, if a Nexus were to be created where one inner token was USDT while the rest were allNexusERC20tokens, User A could be blacklisted by the NexusERC20 tokens in the guard but USDT may not have this address blacklisted. Because of this, User A may still interact with the Nexus contract through USDT usage.Recommendation
Consider if this setup where an outside token is valid, or if only
NexusERC20tokens can be used with a Nexus hub and document this accordingly.Resolution
LayerZero: Acknowledged.
-
I-55 Informational Fee Rounds Down Rounding Acknowledged
Description
In the getFee function, the resulting fee is rounded down in favor of the user and against the favor of the protocol. This is typically against best practices of rounding in protocols, though the material affect is minimal in practice especially due to the fact that the subsequent de-dusting is counted as a fee.
Recommendation
Confirm if it is acceptable to LayerZero to round the fee amount down in favor of the user in this non-material way.
Resolution
LayerZero: Acknowledged.
-
I-56 Informational Quote Misses Underlying Token Pause State Unexpected Behavior Acknowledged
Description
The
quoteOFTfunction on theNexusOFTrelies entirely on thenexusQuoteOFTfunction from the Nexus contract. ThenexusQuoteOFTfunction accounts for the if a particular pathway corresponding to the nexusId or if the Nexus as a whole is paused when computing theoftLimit. However thenexusQuoteOFTfunction does not account for if the underlying NexusERC20 token is paused. In theNexusERC20Guardcontract thecheckTransferfunction asserts an_assertNotPausedvalidation for the token, however this is not reflected in the quote result.Recommendation
Consider if the pause status of the underlying token should be taken into account when quoting and providing the
oftLimitsimilarly to the individual pathway pauses that may occur in theNexuscontract.Resolution
LayerZero: Acknowledged.
-
I-57 Informational Fees Inflated For Small Sends Rounding Acknowledged
Description
_debitView()applies_removeDust()after subtracting fees and does not refund value rounded away by dust removal. For very small sends, that can make the effective fee far larger than the configured fee rate. For example, after fee subtraction a send may round down to zero received tokens, effectively charging a 100% fee, or lose half its value when the remaining amount is rounded down to the nearest shared-decimal unit. The issue is most noticeable on low-value transfers.Recommendation
If this behavior is undesirable, distinguish true fee from dust removed for rounding and refund the de-dusted amount instead of treating it as part of the sent value.
Resolution
LayerZero: Acknowledged.
-
I-58 Informational Rate Limits Retroactively Affected Unexpected Behavior Acknowledged
Description
In the
_setRateLimitConfigsfunction the protocol is careful to checkpoint the rate limit states before updating the details of the decay slope. However this checkpointing is only done for rate limits when their specific config is being updated. Commonly, configs will actually rely on the default config, which uses id 0, for the window and limit of their rate limit. If the default config is updated then all of the rate limit states for those rate limits relying on it will be affected in a stepwise manner retroactively.Recommendation
Be aware of this behavior when updating the global rate limit using id 0. Consider manually processing all of the rate limits that rely on it before the global rate limit when passing the list of
SetRateLimitConfigParam’s.Resolution
LayerZero: Acknowledged.
-
I-59 Informational Net Accounting Abused For One Sided Rate Rounding Acknowledged
Description
When
forwardEnabledis false,backwardEnabledis true, andnetAccountingEnabledis true, the system allows back-and-forth transfers to net usage. Because amounts are rounded up even when reducing usage, an actor can lower current usage more than intended and regain additional capacity. By splitting return transfers into smaller chunks, the actor can repeatedly round reductions in their favor and end up with less recorded usage than before the cycle. That weakens one-sided rate limits beyond the intended netting behavior.Recommendation
Round up when increasing
forwardUsage, but round down when decreasing the opposite-direction usage through net accounting.Resolution
LayerZero: Acknowledged.
-
I-60 Informational Rate Limit Configurations Sandwiched Frontrunning Acknowledged
Description
Rate-limit configuration changes take effect immediately. When
useGlobalStateFlagis enabled, per-path limits are replaced in one transaction by the shared global limiter, and_setRateLimitStatescan also set exact usage values directly. That creates a sandwich opportunity around the admin transaction: an actor can consume allowance before the update, then back-run after the new configuration lands and consume additional capacity that operators may not have intended to expose within the same block window.Recommendation
Make sensitive rate-limit changes through a private mempool or another protected execution path, and account for sandwich exposure when changing limiter configuration.
Resolution
LayerZero: Acknowledged.
-
I-61 Informational Incorrect To Address Used Logical Error Acknowledged
Description
In the
Nexuscontract, when a credit is made to the zero address the address is re-assigned to the dead address in the_creditfunction to avoid reverts with some underlying ERC20 implementations. However theNexusOFT.nexusReceivefunction receives the originaltoaddress as the_toparameter, which has not been remapped to the dead address. In this case theOFTReceivedevent and thesendComposecall will use the incorrect receiver address, as it will present asaddress(0)but actually be the dead address.Recommendation
Re-map
address(0)to the dead address in thenexusReceivefunction of theNexusOFTcontract.Resolution
LayerZero: Acknowledged.
-
I-62 Informational Default Rate Limit Cannot Apply Across Tokens Compatibility Acknowledged
Description
The
RateLimiterCoreUpgradeablecontract includes a default rate limit that is intended to be shared across tokens supported by the Nexus. The Nexus does require that these tokens all use the same decimals and shared decimals, however it does not make sense for this default rate limit to apply across assets that have different prices. For example an asset with a price of $100,000 per 1e18 tokens cannot abide by the same rate limit as an asset with a price of $1 per 1e18 tokens.Recommendation
Consider optionally allowing a default rate limit to be defined by a USD value of tokens being moved via an arbitrary supported oracle pricing solution. Otherwise, be aware of this deficiency in the ability to enforce a default rate limit in a valuable way, and clearly document this for those who use Nexus to rate limit multiple tokens.
Resolution
LayerZero: Acknowledged.
-
I-63 Informational nexusQuoteOFT Misses Exemptions Warning Acknowledged
Description
The
nexusQuoteOFTfunction uses themaxAmountLDas presented by thegetRateLimitUsagesfunction. However this function does not account for when the sender who would initiate an OFT send is exempt when presenting the available amount. ThenexusQuoteOFTfunction provides no extra logic to raise the available amount to thetype(uint256).maxif the ultimate caller of the quoteOft function on theNexusOFTcontract is exempt, and therefore portrays an inaccurate quote result for that caller.Recommendation
Consider taking into consideration whether the caller of the
quoteOFTfunction on theNexusOFTcontract is exempt or not to portray an accurate quote response in thenexusQuoteOFTfunction. Note that this vector also applies to Portal, hencequoteOFTcan check for exemptions there as well.Resolution
LayerZero: Acknowledged.
-
I-64 Informational nexusQuoteOFT Does Not Consult The MsgInspector Unexpected Behavior Acknowledged
Description
In the
nexusQuoteOFTfunction, the msg inspector is not consulted to verify if the send may be invoked or not based upon the sender, sendParams, and options. However, thenexusQuoteSendfunction does consult the msg inspector by way of using the_buildMsgAndOptionsfunction.Recommendation
Consider if the
nexusQuoteOFTfunction ought to consult the msg inspector to more accurately mimic the result of a send operation. Or consider standardizing both thenexusQuoteOFTandnexusQuoteSendfunctions to both not consult the msg inspector if it should not be considered. Otherwise clearly document this difference between the two quote functions.Resolution
LayerZero: Acknowledged.
Round Two
6 findings-
I-01 Informational Stale Checkpointing Documentation Documentation Acknowledged
Description
IRateLimiter.checkpointRateLimitsdocs still state it should be used "when not using setRateLimitConfigs", but current behavior is the opposite:setRateLimitConfigsdoes not checkpoint state. This creates contradictory guidance for operators updating limits/windows.Recommendation
Update the
checkpointRateLimitsdocstring to reflect current behavior, e.g. "call before changing limits/windows..." so it is clear that rate limiter admin must trigger checkpoints, otherwise changes can apply retroactively.Resolution
LayerZero: Acknowledged.
-
I-02 Informational Potential Overflow In maxAmountLD Warning Acknowledged
Description
In
nexusQuoteOFT,maxAmountLDis rescaled by fee basis points: maxAmountLD = (maxAmountLD * BPS_DENOMINATOR) / (BPS_DENOMINATOR - feeBps); This is currently safe because available amounts are tightly bounded, the unlimitedtype(uint256).maxsentinel is excluded before the multiplication, and the full-fee case is handled separately. The concern is that this safety depends on those invariants remaining true. If future changes introduce scaled availability or broader bounds, this multiplication path could become an overflow or revert surface.Recommendation
Document the assumptions that keep this safe today, and revisit the calculation if future changes expand the range of
maxAmountLD.Resolution
LayerZero: Acknowledged.
-
I-03 Informational AccessControl2StepUpgradeable Lacks Timelock Warning Acknowledged
Description
AccessControl2StepUpgradeableimplements a two-stepDEFAULT_ADMIN_ROLEtransfer modeled after OpenZeppelin'sAccessControlDefaultAdminRules. However, it omits a time delay betweenbeginDefaultAdminTransfer()andacceptDefaultAdminTransfer(). As implemented, the Acknowledged admin can accept the transfer in the very same block as the transfer is initiated without delay.Recommendation
Clearly document this behavior or consider adding a delay.
Resolution
LayerZero: Acknowledged.
-
I-04 Informational PauseByIDBase._setPaused Lacks Idempotent Check Warning Acknowledged
Description
PauseByIDBaseUpgradeable._setPauseddoes not check whether the new config matches the existing config before writing. Every other pause setter in the codebase reverts on same-state writes:PauseBaseUpgradeable._setPaused(bool)— reverts withPauseStateIdempotentPauseByIDBaseUpgradeable._setDefaultPaused(bool)— reverts withPauseStateIdempotent
But
PauseByIDBaseUpgradeable._setPaused(SetPausedParam[])unconditionally overwrites and emits, which allows the same config to be written repeatedly, emitting duplicate PauseSet events each time.Recommendation
Consider adding an idempotent check per entry,
Resolution
LayerZero: Acknowledged.
-
I-05 Informational Missing AccessControl Init Locks Admin Functions Logical Error Acknowledged
Description
OAppCoreRBACUpgradeableprotects management functions such assetPeerandsetDelegatewithonlyRole(OAPP_ADMIN_ROLE), but its initializer does not initializeAccessControl2Step. If an integrating contract calls only__OAppCoreRBAC_init(...)and skips__AccessControl2Step_init(...), no account receivesDEFAULT_ADMIN_ROLE,grantRolecannot be used, and the role-gated admin paths can be locked permanently.Recommendation
Update the docs and initialization guidance to state explicitly that inheritors must call
__AccessControl2Step_init(initialAdmin), and that omitting this step can lock admin functionality.Resolution
LayerZero: Acknowledged.
-
I-06 Informational PreCrime Simulation Layer Entirely Removed Warning Acknowledged
Description
The Console implementation completely removes LayerZero's PreCrime simulation subsystem from the OFT stack. In
OFTCoreBaseUpgradeable, the inheritance chain isOAppUpgradeable ->OAppOptionsType3BaseUpgradeable -> OAppMsgInspectionBaseUpgradeable—OAppPreCrimeSimulatorUpgradeableis absent.Recommendation
Consider if this intended. If so, clearly document this behavior.
Resolution
LayerZero: Acknowledged.
Round Three
2 findings-
I-01 Informational Shared Nonce Channel Enables Token-level DoS DoS Acknowledged
Description
Proof of concept: PoC
setPeer(dstEid, peer)creates a single shared LayerZero lane between Nexus contracts. At the protocol level, LayerZero identifies channels by (sender, srcEid, dstEid, receiver) and maintains a single sequential nonce per lane, with no awareness of token IDs. Although each token has its ownNexusOFT, all tokens route through the sameNexushub, so transfers for USDC, WBTC, and all other tokens share one nonce stream between chains.On the source side, a send is accepted as long as the OFT is registered locally and a peer exists for the destination chain, but it does not verify that the token is registered on the destination Nexus. This allows a token that exists on chain A but not on chain B to still be sent. The failure only occurs on B inside
_lzReceive(),whereInvalidTokenId(tokenId)is triggered. By that point, the message has already been verified and stored in the LayerZero endpoint, but not cleared.Because LayerZero uses a lazy inbound nonce, clearing later messages requires iterating through all prior nonces in the lane. If invalid packets accumulate ahead of valid ones, this linear scan becomes increasingly expensive and can run out of gas, preventing legitimate transfers from being processed.
An attacker can exploit this by repeatedly sending a token supported on A but not B
(even with amountLD = 0). These messages are accepted on A, revert on B, and remain uncleared, creating a backlog. Since all tokens share the same Nexus lane, this backlog impacts unrelated tokens, eventually causing legitimate transfers to fail due to gas exhaustionduring _clearPayload.This results in a global DoS across all tokens on the affected chain pair. The attack is cheap to execute (only messaging fees), and recovery is operational, requiring manual clearing of stuck packets while user funds remain blocked until intervention.
https://gist.github.com/GuardianAudits/6cf23d0d7c36bf1bd299aa4b22da15b8
Recommendation
setPeer(B) opens the A -> B road for the whole Nexus registerToken(X) decides which tokens exist on each chain The bug needs token X exists on A, does not exist on B, but the A -> B road is still open for X A mitigation for this can be to pause any affected route, by blocking any exact pair, tokenId X -> destination EID B, before
nexusSend()burns and sends By computing the route ID with nexus.getNexusId(tokenId, dstEid), then calling setPaused(routeId) on the pause moduleResolution
LayerZero: Acknowledged.
-
I-02 Informational Immutable Decimals Can Change On Upgrade Upgradeability Acknowledged
Description
ERC20Plus stores DECIMALS as an immutable set in the implementation constructor. In a proxy setup, immutables live in the implementation bytecode (not proxy storage). If the proxy is upgraded to an implementation deployed with a different constructor _decimals, decimals() will silently change while balances/allowances/totalSupply remain in the old units.
Recommendation
Do not use an immutable for decimals in an upgradeable token. Store decimals in proxy storage (set once in initialize()), and enforce that it cannot change across upgrades.
Resolution
LayerZero: Acknowledged.
No findings match.
More from LayerZero
All 7 reports-
Canton VER Updates
169 findings2 critical · 12 high 169 findings: 2 critical, 12 high, 36 medium, 54 low, 65 informational -
Console EVM Updates
4 findings 4 findings: 1 low, 3 informational -
Solana Console
42 findings 42 findings: 6 low, 36 informational -
Solana OApp
54 findings 54 findings: 1 medium, 9 low, 44 informational
Put your code through the same review.
This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.