33pragma solidity 0.8.28 ;
44
55import {IL2AssetRouter} from "./IL2AssetRouter.sol " ;
6+ import {IL2CrossChainSender} from "../interfaces/IL2CrossChainSender.sol " ;
67import {IAssetRouterBase} from "./IAssetRouterBase.sol " ;
78import {AssetRouterBase} from "./AssetRouterBase.sol " ;
89
@@ -20,15 +21,16 @@ import {InteropCallStarter} from "../../common/Messaging.sol";
2021import {L2_BRIDGEHUB_ADDR, L2_INTEROP_CENTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "../../common/l2-helpers/L2ContractAddresses.sol " ;
2122import {L2ContractHelper} from "../../common/l2-helpers/L2ContractHelper.sol " ;
2223import {DataEncoding} from "../../common/libraries/DataEncoding.sol " ;
23- import {AmountMustBeGreaterThanZero, AssetIdNotSupported, EmptyAddress, InvalidCaller, Unauthorized, TokenNotLegacy, InvalidSelector, PayloadTooShort, ExecuteMessageFailed} from "../../common/L1ContractErrors.sol " ;
24- import {IERC7786Receiver } from "../../interop/IERC7786Receiver .sol " ;
24+ import {AmountMustBeGreaterThanZero, AssetIdNotSupported, EmptyAddress, Unauthorized, TokenNotLegacy, InvalidSelector, PayloadTooShort, ExecuteMessageFailed} from "../../common/L1ContractErrors.sol " ;
25+ import {IERC7786Recipient } from "../../interop/IERC7786Recipient .sol " ;
2526import {IERC7786Attributes } from "../../interop/IERC7786Attributes.sol " ;
27+ import {InteroperableAddress} from "@openzeppelin/contracts-master/utils/draft-InteroperableAddress.sol " ;
2628
2729/// @author Matter Labs
2830/// @custom:security-contact [email protected] 2931/// @notice The "default" bridge implementation for the ERC20 tokens. Note, that it does not
3032/// support any custom token logic, i.e. rebase tokens' functionality is not supported.
31- contract L2AssetRouter is AssetRouterBase , IL2AssetRouter , ReentrancyGuard , IERC7786Receiver {
33+ contract L2AssetRouter is AssetRouterBase , IL2AssetRouter , ReentrancyGuard , IERC7786Recipient {
3234 /// @dev The address of the L2 legacy shared bridge.
3335 address public immutable L2_LEGACY_SHARED_BRIDGE;
3436
@@ -42,9 +44,9 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter, ReentrancyGuard, IERC
4244 modifier onlyAssetRouterCounterpart (uint256 _originChainId ) {
4345 if (_originChainId == L1_CHAIN_ID) {
4446 // Only the L1 Asset Router counterpart can initiate and finalize the deposit.
45- require (AddressAliasHelper.undoL1ToL2Alias (msg .sender ) == L1_ASSET_ROUTER, InvalidCaller (msg .sender ));
47+ require (AddressAliasHelper.undoL1ToL2Alias (msg .sender ) == L1_ASSET_ROUTER, Unauthorized (msg .sender ));
4648 } else {
47- revert InvalidCaller (msg .sender ); // xL2 messaging not supported for now
49+ revert Unauthorized (msg .sender );
4850 }
4951 _;
5052 }
@@ -53,27 +55,25 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter, ReentrancyGuard, IERC
5355 modifier onlyAssetRouterCounterpartOrSelf (uint256 _chainId ) {
5456 if (_chainId == L1_CHAIN_ID) {
5557 // Only the L1 Asset Router counterpart can initiate and finalize the deposit.
56- if (
57- (AddressAliasHelper.undoL1ToL2Alias (msg .sender ) != L1_ASSET_ROUTER) &&
58- msg .sender != address (this ) &&
59- (AddressAliasHelper.undoL1ToL2Alias (msg .sender ) != address (this ))
60- ) {
61- revert InvalidCaller (msg .sender );
58+ if ((AddressAliasHelper.undoL1ToL2Alias (msg .sender ) != L1_ASSET_ROUTER) && msg .sender != address (this )) {
59+ revert Unauthorized (msg .sender );
6260 }
6361 } else {
64- revert InvalidCaller (msg .sender ); // xL2 messaging not supported for now
62+ if (msg .sender != address (this )) {
63+ revert Unauthorized (msg .sender );
64+ }
6565 }
6666 _;
6767 }
6868
6969 /// @notice Checks that the message sender is the legacy L2 bridge.
7070 modifier onlyLegacyBridge () {
71- require (msg .sender == L2_LEGACY_SHARED_BRIDGE, InvalidCaller (msg .sender ));
71+ require (msg .sender == L2_LEGACY_SHARED_BRIDGE, Unauthorized (msg .sender ));
7272 _;
7373 }
7474
7575 modifier onlyNTV () {
76- require (msg .sender == L2_NATIVE_TOKEN_VAULT_ADDR, InvalidCaller (msg .sender ));
76+ require (msg .sender == L2_NATIVE_TOKEN_VAULT_ADDR, Unauthorized (msg .sender ));
7777 _;
7878 }
7979
@@ -130,42 +130,32 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter, ReentrancyGuard, IERC
130130 }
131131
132132 /// @notice Executes cross-chain interop messages following ERC-7786 standard
133- /// @param messageId Gateway-specific message identifier (currently unused)
134- /// @param sourceChain CAIP-2 chain identifier where the message originated
135- /// @param sender CAIP-10 account address that initiated the cross-chain message
133+ /// @param sender ERC-7930 Address of the message sender
136134 /// @param payload Encoded function call data (must be finalizeDeposit)
137- /// @param attributes ERC-7786 message attributes (currently unused)
138135 /// @return Function selector confirming successful execution per ERC-7786
139- function executeMessage (
140- // kl todo: change back to strings
141- // solhint-disable-next-line no-unused-vars
142- bytes32 messageId , // Gateway-specific message identifier
143- uint256 sourceChain , // [CAIP-2] chain identifier
144- address sender , // [CAIP-10] account address
145- bytes calldata payload ,
146- // solhint-disable-next-line no-unused-vars
147- bytes [] calldata attributes
136+ function receiveMessage (
137+ bytes32 /* receiveId */ , // Unique identifier
138+ bytes calldata sender , // ERC-7930 address
139+ bytes calldata payload
148140 ) external payable returns (bytes4 ) {
149141 // This function serves as the L2AssetRouter's entry point for processing cross-chain bridge operations
150142 // initiated through the InteropCenter system. It implements critical security validations:
151- // - L1->L2 calls: Only L1_ASSET_ROUTER can send messages from L1_CHAIN_ID
143+ // - L1->L2 calls: Currently Interop can only be initiated on L2, so this case shouldn't be covered.
152144 // - L2->L2 calls: Only this contract (L2AssetRouter) can send messages from other L2 chains
153145 //
154146 // This dual validation prevents attackers from spoofing cross-chain messages by requiring
155147 // both correct source chain ID and authorized sender address.
156148 //
157149 // INDIRECT CALL PATTERN (L2->L2 interop flow):
158150 // 1. User calls InteropCenter on source L2
159- // 2. InteropCenter calls interopCenterInitiateBridge () on source chain's L2AssetRouter
151+ // 2. InteropCenter calls initiateBridging () on source chain's L2AssetRouter
160152 // 3. Source L2AssetRouter becomes the "sender" for the destination L2 call
161- // 4. Destination L2 validates sender == address(this) for non-L1 sources
153+ // 4. Destination L2 validates senderAddress == address(this) for non-L1 sources
162154 // (L2AssetRouter address is equal for all ZKsync chains)
163155
164- require (
165- (sourceChain == L1_CHAIN_ID && sender == L1_ASSET_ROUTER) ||
166- (sourceChain != L1_CHAIN_ID && sender == address (this )),
167- InvalidCaller (sender)
168- );
156+ (uint256 senderChainId , address senderAddress ) = InteroperableAddress.parseEvmV1Calldata (sender);
157+
158+ require ((senderChainId != L1_CHAIN_ID && senderAddress == address (this )), Unauthorized (senderAddress));
169159
170160 // The payload must contain a valid finalizeDeposit selector to ensure only legitimate
171161 // bridge operations are executed. This prevents arbitrary function calls through the interop system.
@@ -177,7 +167,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter, ReentrancyGuard, IERC
177167
178168 (bool success , ) = address (this ).call (payload);
179169 require (success, ExecuteMessageFailed ());
180- return IERC7786Receiver .executeMessage .selector ;
170+ return IERC7786Recipient .receiveMessage .selector ;
181171 }
182172
183173 /*//////////////////////////////////////////////////////////////
@@ -219,13 +209,13 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter, ReentrancyGuard, IERC
219209 emit DepositFinalizedAssetRouter (_originChainId, _assetId, _transferData);
220210 }
221211
222- /// @inheritdoc IL2AssetRouter
223- function interopCenterInitiateBridge (
212+ /// @inheritdoc IL2CrossChainSender
213+ function initiateBridging (
224214 uint256 _chainId ,
225215 address _originalCaller ,
226216 uint256 _value ,
227217 bytes calldata _data
228- ) external payable returns (InteropCallStarter memory interopCallStarter ) {
218+ ) external payable onlyL2InteropCenter returns (InteropCallStarter memory interopCallStarter ) {
229219 // This function is called by the InteropCenter when processing indirect interop calls.
230220 // It prepares the bridge operation for cross-chain execution through these steps:
231221 // 1. Processing the deposit through the standard bridgehub flow
@@ -252,7 +242,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter, ReentrancyGuard, IERC
252242 bytes [] memory attributes = new bytes [](1 );
253243 attributes[0 ] = abi.encode (IERC7786Attributes .interopCallValue.selector , _value);
254244 interopCallStarter = InteropCallStarter ({
255- nextContract : request.l2Contract,
245+ to : request.l2Contract,
256246 data: request.l2Calldata,
257247 callAttributes: attributes
258248 });
0 commit comments