Skip to content

Commit 832edb3

Browse files
fix: some fixes for medium-interop review + ERC7786 compatibility changes (#1556)
Co-authored-by: nikitastupin-matterlabs <[email protected]>
1 parent 39c61b7 commit 832edb3

35 files changed

+819
-409
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@
1515
[submodule "lib/@matterlabs/zksync-contracts"]
1616
path = lib/@matterlabs/zksync-contracts
1717
url = https://github.com/matter-labs/v2-testnet-contracts
18+
[submodule "l1-contracts/lib/openzeppelin-contracts-master"]
19+
path = l1-contracts/lib/openzeppelin-contracts-master
20+
url = https://github.com/OpenZeppelin/openzeppelin-contracts.git

l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {IL2AssetRouter} from "./asset-router/IL2AssetRouter.sol";
1616
import {IL2NativeTokenVault} from "./ntv/IL2NativeTokenVault.sol";
1717

1818
import {IL2SharedBridgeLegacy} from "./interfaces/IL2SharedBridgeLegacy.sol";
19-
import {AmountMustBeGreaterThanZero, DeployFailed, EmptyBytes32, Unauthorized, ZeroAddress, InvalidCaller} from "../common/L1ContractErrors.sol";
19+
import {AmountMustBeGreaterThanZero, DeployFailed, EmptyBytes32, Unauthorized, ZeroAddress} from "../common/L1ContractErrors.sol";
2020

2121
/// @author Matter Labs
2222
/// @custom:security-contact [email protected]
@@ -113,7 +113,7 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable {
113113
AddressAliasHelper.undoL1ToL2Alias(msg.sender) != l1Bridge &&
114114
AddressAliasHelper.undoL1ToL2Alias(msg.sender) != l1SharedBridge
115115
) {
116-
revert InvalidCaller(msg.sender);
116+
revert Unauthorized(msg.sender);
117117
}
118118

119119
IL2AssetRouter(L2_ASSET_ROUTER_ADDR).finalizeDepositLegacyBridge({

l1-contracts/contracts/bridge/asset-router/AssetRouterBase.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,14 @@ abstract contract AssetRouterBase is IAssetRouterBase, Ownable2StepUpgradeable,
109109
bytes32 _assetId,
110110
address _originalCaller,
111111
uint256 _amount
112-
) public payable virtual;
112+
) external payable virtual;
113113

114114
function _bridgehubDepositBaseToken(
115115
uint256 _chainId,
116116
bytes32 _assetId,
117117
address _originalCaller,
118118
uint256 _amount
119-
) public payable virtual {
119+
) internal virtual {
120120
address assetHandler = assetHandlerAddress[_assetId];
121121
require(assetHandler != address(0), AssetHandlerDoesNotExist(_assetId));
122122

@@ -220,7 +220,7 @@ abstract contract AssetRouterBase is IAssetRouterBase, Ownable2StepUpgradeable,
220220
//////////////////////////////////////////////////////////////*/
221221

222222
/// @inheritdoc IAssetRouterBase
223-
function finalizeDeposit(uint256 _chainId, bytes32 _assetId, bytes calldata _transferData) public payable virtual;
223+
function finalizeDeposit(uint256 _chainId, bytes32 _assetId, bytes calldata _transferData) external payable virtual;
224224

225225
function _finalizeDeposit(
226226
uint256 _chainId,

l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {IAssetRouterBase} from "./IAssetRouterBase.sol";
88
import {L2TransactionRequestTwoBridgesInner} from "../../bridgehub/IBridgehub.sol";
99
import {IL1SharedBridgeLegacy} from "../interfaces/IL1SharedBridgeLegacy.sol";
1010
import {IL1ERC20Bridge} from "../interfaces/IL1ERC20Bridge.sol";
11-
import {IL1CrossChainSender} from "./IL1CrossChainSender.sol";
11+
import {IL1CrossChainSender} from "../interfaces/IL1CrossChainSender.sol";
1212

1313
/// @title L1 Bridge contract interface
1414
/// @author Matter Labs

l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
pragma solidity ^0.8.20;
44

55
import {IAssetRouterBase} from "./IAssetRouterBase.sol";
6-
import {InteropCallStarter} from "../../common/Messaging.sol";
6+
import {IL2CrossChainSender} from "../interfaces/IL2CrossChainSender.sol";
77

88
/// @author Matter Labs
99
/// @custom:security-contact [email protected]
10-
interface IL2AssetRouter is IAssetRouterBase {
10+
interface IL2AssetRouter is IAssetRouterBase, IL2CrossChainSender {
1111
event WithdrawalInitiatedAssetRouter(
1212
uint256 chainId,
1313
address indexed l2Sender,
@@ -37,19 +37,4 @@ interface IL2AssetRouter is IAssetRouterBase {
3737
/// a legacy asset.
3838
/// @param _assetId The assetId of the legacy token.
3939
function setLegacyTokenAssetHandler(bytes32 _assetId) external;
40-
41-
/// @notice Function that returns an InteropCallStarter corresponding to the interop call. Effectively this initiates bridging,
42-
/// BH part is processed within this function via `_bridgehubDeposit` call which also returns the data for an l2 call
43-
/// on the destination chain (which will be processed with the returned InteropCallStarter from this function).
44-
/// @param _chainId Destination chain ID.
45-
/// @param _originalCaller The `msg.sender` address from the external call that initiated current one.
46-
/// @param _value The `msg.value` to be deposited on the target chain.
47-
/// @param _data The calldata for the second bridge deposit.
48-
/// @return interopCallStarter InteropCallStarter corresponding to the second bridge call.
49-
function interopCenterInitiateBridge(
50-
uint256 _chainId,
51-
address _originalCaller,
52-
uint256 _value,
53-
bytes calldata _data
54-
) external payable returns (InteropCallStarter memory interopCallStarter);
5540
}

l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {IL1AssetRouter} from "./IL1AssetRouter.sol";
99
import {IL2AssetRouter} from "./IL2AssetRouter.sol";
1010
import {IAssetRouterBase, LEGACY_ENCODING_VERSION, SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION} from "./IAssetRouterBase.sol";
1111
import {AssetRouterBase} from "./AssetRouterBase.sol";
12+
import {IL1CrossChainSender} from "../interfaces/IL1CrossChainSender.sol";
1213

1314
import {IL1AssetHandler} from "../interfaces/IL1AssetHandler.sol";
1415
import {IL1ERC20Bridge} from "../interfaces/IL1ERC20Bridge.sol";
@@ -195,7 +196,7 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard {
195196
_bridgehubDepositBaseToken(_chainId, _assetId, _originalCaller, _amount);
196197
}
197198

198-
/// @inheritdoc IL1AssetRouter
199+
/// @inheritdoc IL1CrossChainSender
199200
function bridgehubDeposit(
200201
uint256 _chainId,
201202
address _originalCaller,

l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol

Lines changed: 30 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
pragma solidity 0.8.28;
44

55
import {IL2AssetRouter} from "./IL2AssetRouter.sol";
6+
import {IL2CrossChainSender} from "../interfaces/IL2CrossChainSender.sol";
67
import {IAssetRouterBase} from "./IAssetRouterBase.sol";
78
import {AssetRouterBase} from "./AssetRouterBase.sol";
89

@@ -20,15 +21,16 @@ import {InteropCallStarter} from "../../common/Messaging.sol";
2021
import {L2_BRIDGEHUB_ADDR, L2_INTEROP_CENTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "../../common/l2-helpers/L2ContractAddresses.sol";
2122
import {L2ContractHelper} from "../../common/l2-helpers/L2ContractHelper.sol";
2223
import {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";
2526
import {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
});

l1-contracts/contracts/bridge/asset-tracker/AssetTracker.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {L2_ASSET_ROUTER_ADDR, L2_ASSET_TRACKER_ADDR, L2_INTEROP_CENTER_ADDR, L2_
1111
import {DataEncoding} from "../../common/libraries/DataEncoding.sol";
1212
import {IAssetRouterBase} from "../asset-router/IAssetRouterBase.sol";
1313
import {INativeTokenVault} from "../ntv/INativeTokenVault.sol";
14-
import {InsufficientChainBalanceAssetTracker, InvalidInteropCalldata, InvalidMessage, InvalidProof, ReconstructionMismatch, Unauthorized, ChainIdNotRegistered} from "../../common/L1ContractErrors.sol";
14+
import {InsufficientChainBalanceAssetTracker, InvalidInteropCalldata, InvalidMessage, InvalidProof, ReconstructionMismatch, Unauthorized, ChainIdNotRegistered, InvalidChainId} from "../../common/L1ContractErrors.sol";
1515
import {IMessageRoot} from "../../bridgehub/IMessageRoot.sol";
1616
import {ProcessLogsInput} from "../../state-transition/chain-interfaces/IExecutor.sol";
1717
import {DynamicIncrementalMerkleMemory} from "../../common/libraries/DynamicIncrementalMerkleMemory.sol";
@@ -24,7 +24,7 @@ import {FinalizeL1DepositParams} from "../../bridge/interfaces/IL1Nullifier.sol"
2424
import {TransientPrimitivesLib} from "../../common/libraries/TransientPrimitves/TransientPrimitives.sol";
2525
import {AddressAliasHelper} from "../../vendor/AddressAliasHelper.sol";
2626
// import {IChainAssetHandler} from "../../bridgehub/IChainAssetHandler.sol";
27-
import {NotMigratedChain, InvalidAssetId, InvalidAmount, InvalidChainId, InvalidSender} from "./AssetTrackerErrors.sol";
27+
import {NotMigratedChain, InvalidAssetId, InvalidAmount, InvalidSender} from "./AssetTrackerErrors.sol";
2828

2929
contract AssetTracker is IAssetTracker, Ownable2StepUpgradeable, AssetHandlerModifiers {
3030
using DynamicIncrementalMerkleMemory for DynamicIncrementalMerkleMemory.Bytes32PushTree;

l1-contracts/contracts/bridge/asset-tracker/AssetTrackerErrors.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ pragma solidity ^0.8.21;
55
error InvalidAmount();
66
// 0xfafca5a0
77
error InvalidAssetId();
8-
// 0x7a47c9a2
9-
error InvalidChainId();
108
// 0x4ecc0587
119
error InvalidMigrationNumber();
1210
// 0xddb5de5e

0 commit comments

Comments
 (0)