diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index 9f478a598..1cf606501 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -40,6 +40,9 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { /// @dev The address of the WETH token on L1. address public immutable override L1_WETH_TOKEN; + /// @dev The assetId of the base token. + bytes32 public immutable ETH_TOKEN_ASSET_ID; + /// @dev The address of ZKsync Era diamond proxy contract. address internal immutable ERA_DIAMOND_PROXY; @@ -97,6 +100,7 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { L1_WETH_TOKEN = _l1WethAddress; ERA_DIAMOND_PROXY = _eraDiamondProxy; L1_NULLIFIER = IL1Nullifier(_l1Nullifier); + ETH_TOKEN_ASSET_ID = DataEncoding.encodeNTVAssetId(block.chainid, ETH_TOKEN_ADDRESS); } /// @dev Initializes a contract bridge for later use. Expected to be used in the proxy. @@ -374,6 +378,16 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { (address, uint256, address) ); bytes32 assetId = _ensureTokenRegisteredWithNTV(_l1Token); + + if (assetId == ETH_TOKEN_ASSET_ID) { + // In the old SDK/contracts the user had to always provide `0` as the deposit amount for ETH token, while + // ultimately the provided `msg.value` was used as the deposit amount. This check is needed for backwards compatibility. + + if (_depositAmount == 0) { + _depositAmount = msg.value; + } + } + return (assetId, abi.encode(_depositAmount, _l2Receiver)); } diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 12427d9ed..1c6cffe7c 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -254,36 +254,28 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 ) internal virtual returns (bytes memory _bridgeMintData) { (uint256 _depositAmount, address _receiver) = abi.decode(_data, (uint256, address)); - uint256 amount; address nativeToken = tokenAddress[_assetId]; if (_assetId == BASE_TOKEN_ASSET_ID) { - amount = msg.value; - - // In the old SDK/contracts the user had to always provide `0` as the deposit amount for ETH token, while - // ultimately the provided `msg.value` was used as the deposit amount. This check is needed for backwards compatibility. - if (_depositAmount == 0) { - _depositAmount = amount; - } - _handleChainBalanceIncrease(_chainId, _assetId, amount, true); - if (_depositAmount != amount) { - revert ValueMismatch(_depositAmount, amount); + if (_depositAmount != msg.value) { + revert ValueMismatch(_depositAmount, msg.value); } + + _handleChainBalanceIncrease(_chainId, _assetId, _depositAmount, true); } else { // The Bridgehub also checks this, but we want to be sure if (msg.value != 0) { revert NonEmptyMsgValue(); } - amount = _depositAmount; - _handleChainBalanceIncrease(_chainId, _assetId, amount, true); + _handleChainBalanceIncrease(_chainId, _assetId, _depositAmount, true); if (!_depositChecked) { uint256 expectedDepositAmount = _depositFunds(_originalCaller, IERC20(nativeToken), _depositAmount); // note if _originalCaller is this contract, this will return 0. This does not happen. // The token has non-standard transfer logic - if (amount != expectedDepositAmount) { + if (_depositAmount != expectedDepositAmount) { revert TokensWithFeesNotSupported(); } } } - if (amount == 0) { + if (_depositAmount == 0) { // empty deposit amount revert EmptyDeposit(); } @@ -296,7 +288,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 _originalCaller: _originalCaller, _l2Receiver: _receiver, _l1Token: nativeToken, - _amount: amount, + _amount: _depositAmount, _erc20Metadata: erc20Metadata }); @@ -305,7 +297,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 assetId: _assetId, sender: _originalCaller, receiver: _receiver, - amount: amount + amount: _depositAmount }); } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol index f74c190fd..3c99f366d 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol @@ -6,11 +6,11 @@ import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; import {L1AssetRouterTest} from "./_L1SharedBridge_Shared.t.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; -import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; +import {IBridgehub, L2TransactionRequestTwoBridgesInner} from "contracts/bridgehub/IBridgehub.sol"; import {L2Message, TxStatus} from "contracts/common/Messaging.sol"; import {IMailbox} from "contracts/state-transition/chain-interfaces/IMailbox.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; -import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; +import {IAssetRouterBase, LEGACY_ENCODING_VERSION} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; import {IL1AssetHandler} from "contracts/bridge/interfaces/IL1AssetHandler.sol"; import {IL1BaseTokenAssetHandler} from "contracts/bridge/interfaces/IL1BaseTokenAssetHandler.sol"; import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR, L2_ASSET_ROUTER_ADDR} from "contracts/common/L2ContractAddresses.sol"; @@ -479,4 +479,31 @@ contract L1AssetRouterTestBase is L1AssetRouterTest { assertEq(endBalanceNtv - startBalanceNtv, amount); assertEq(endEthBalanceNtv - startEthBalanceNtv, amount); } + + function test_bridgehubDeposit_Eth_storesCorrectTxHash() public { + _setBaseTokenAssetId(tokenAssetId); + vm.prank(bridgehubAddress); + vm.mockCall( + bridgehubAddress, + abi.encodeWithSelector(IBridgehub.baseTokenAssetId.selector), + abi.encode(tokenAssetId) + ); + // solhint-disable-next-line func-named-parameters + L2TransactionRequestTwoBridgesInner memory request = sharedBridge.bridgehubDeposit{value: amount}( + chainId, + alice, + 0, + abi.encode(ETH_TOKEN_ADDRESS, 0, bob) + ); + + bytes32 expectedTxHash = DataEncoding.encodeTxDataHash({ + _nativeTokenVault: address(nativeTokenVault), + _encodingVersion: LEGACY_ENCODING_VERSION, + _originalCaller: alice, + _assetId: nativeTokenVault.BASE_TOKEN_ASSET_ID(), + _transferData: abi.encode(amount, bob) + }); + + assertEq(request.txDataHash, expectedTxHash); + } }