Era

ZKsync
FoundryLayer 2
500,000 USDC
View results
Submission Details
Severity: medium
Valid

Inability to Withdraw Tokens from Layer 2 Due to Flawed Token Registration Logic

Vulnerability Summary

The implementation of the _ensureTokenRegisteredWithNTV function in the L2AssetRouter contract is flawed, causing it to always return bytes32(0) for tokens. This results in users being unable to withdraw their tokens from Layer 2, as the asset ID cannot be resolved to a valid asset handler address in the _withdrawSender function.

Vulnerability Details

When users attempt to withdraw tokens from Layer 2 using the L2AssetRouter::withdrawToken function, the process involves several steps:

  1. Calling withdrawToken: Users call the withdrawToken function, which is intended to facilitate the withdrawal of tokens from Layer 2 to Layer 1.

  2. Token Registration Check: The function calls _ensureTokenRegisteredWithNTV to verify if the token is registered with the native token vault and to retrieve its asset ID.

  3. Flawed Implementation: The implementation of _ensureTokenRegisteredWithNTV is as follows:

function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) {
IL2NativeTokenVault nativeTokenVault = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR);
nativeTokenVault.ensureTokenIsRegistered(_token);
}

The function declares assetId in its return statement, but fails to set the variable in the function body leading to the function returning the default bytes32 value, bytes32(0).

  1. Withdrawal Process Failure: When _withdrawSender is called, it relies on the asset ID returned by _ensureTokenRegisteredWithNTV. Since the asset ID is bytes32(0), the function cannot resolve the asset handler address, leading to a failure in processing the withdrawal.

Impact

Users are unable to withdraw their tokens from Layer 2, effectively locking their assets in the system. A core functionality of the protocol is effectively shutdown.

Tools Used

Manual review

Recommendation

To resolve this issue, the following recommendations should be implemented:

  • Ensure that the assetId is correctly set after calling nativeTokenVault.ensureTokenIsRegistered(_token). The function should be modified as follows:

function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) {
IL2NativeTokenVault nativeTokenVault = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR);
assetId = nativeTokenVault.ensureTokenIsRegistered(_token); // Ensure assetId is set after registration
}
  • Before the above can work, we have to also update the NativeTokenVault::ensureTokenIsRegistered to return the appropriate value.

Updates

Lead Judging Commences

inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

`L2AssetRouter._ensureTokenRegisteredWithNTV` `assetId` return value is never assigned, which will cause `withdrawToken` to fail

Appeal created

inallhonesty Lead Judge
5 months ago
inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

`L2AssetRouter._ensureTokenRegisteredWithNTV` `assetId` return value is never assigned, which will cause `withdrawToken` to fail

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.