diff --git a/l1-contracts/contracts/bridge/BridgedStandardERC20.sol b/l1-contracts/contracts/bridge/BridgedStandardERC20.sol index d848dcbac..bbe0ed8c4 100644 --- a/l1-contracts/contracts/bridge/BridgedStandardERC20.sol +++ b/l1-contracts/contracts/bridge/BridgedStandardERC20.sol @@ -86,11 +86,7 @@ contract BridgedStandardERC20 is ERC20PermitUpgradeable, IBridgedStandardToken, /// @param _originToken Address of the origin token that can be deposited to mint this bridged token /// @param _data The additional data that the L1 bridge provide for initialization. /// In this case, it is packed `name`/`symbol`/`decimals` of the L1 token. - function bridgeInitialize( - bytes32 _assetId, - address _originToken, - bytes calldata _data - ) external initializer returns (uint256) { + function bridgeInitialize(bytes32 _assetId, address _originToken, bytes calldata _data) external initializer { if (_originToken == address(0)) { revert ZeroAddress(); } @@ -100,8 +96,10 @@ contract BridgedStandardERC20 is ERC20PermitUpgradeable, IBridgedStandardToken, nativeTokenVault = msg.sender; // We parse the data exactly as they were created on the L1 bridge - (uint256 chainId, bytes memory nameBytes, bytes memory symbolBytes, bytes memory decimalsBytes) = DataEncoding - .decodeTokenData(_data); + // slither-disable-next-line unused-return + (, bytes memory nameBytes, bytes memory symbolBytes, bytes memory decimalsBytes) = DataEncoding.decodeTokenData( + _data + ); ERC20Getters memory getters; string memory decodedName; @@ -143,7 +141,6 @@ contract BridgedStandardERC20 is ERC20PermitUpgradeable, IBridgedStandardToken, availableGetters = getters; emit BridgeInitialize(_originToken, decodedName, decodedSymbol, decimals_); - return chainId; } /// @notice A method to be called by the governor to update the token's metadata. diff --git a/l1-contracts/contracts/bridge/L1ERC20Bridge.sol b/l1-contracts/contracts/bridge/L1ERC20Bridge.sol index 0cd72ccc7..190c086b4 100644 --- a/l1-contracts/contracts/bridge/L1ERC20Bridge.sol +++ b/l1-contracts/contracts/bridge/L1ERC20Bridge.sol @@ -13,7 +13,7 @@ import {IL1AssetRouter} from "./asset-router/IL1AssetRouter.sol"; import {L2ContractHelper} from "../common/libraries/L2ContractHelper.sol"; import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; -import {EmptyDeposit, WithdrawalAlreadyFinalized, TokensWithFeesNotSupported, ETHDepositNotSupported} from "../common/L1ContractErrors.sol"; +import {EmptyDeposit, WithdrawalAlreadyFinalized, TokensWithFeesNotSupported, ETHDepositNotSupported, ApprovalFailed} from "../common/L1ContractErrors.sol"; import {ETH_TOKEN_ADDRESS} from "../common/Config.sol"; /// @author Matter Labs @@ -188,7 +188,7 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard { if (_l1Token == ETH_TOKEN_ADDRESS) { revert ETHDepositNotSupported(); } - uint256 amount = _depositFundsToAssetRouter(msg.sender, IERC20(_l1Token), _amount); + uint256 amount = _approveFundsToAssetRouter(msg.sender, IERC20(_l1Token), _amount); if (amount != _amount) { // The token has non-standard transfer logic revert TokensWithFeesNotSupported(); @@ -203,6 +203,11 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard { _l2TxGasPerPubdataByte: _l2TxGasPerPubdataByte, _refundRecipient: _refundRecipient }); + // clearing approval + bool success = IERC20(_l1Token).approve(address(L1_ASSET_ROUTER), 0); + if (!success) { + revert ApprovalFailed(); + } depositAmount[msg.sender][_l1Token][l2TxHash] = _amount; emit DepositInitiated({ l2DepositTxHash: l2TxHash, @@ -219,10 +224,14 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard { /// @dev Transfers tokens from the depositor address to the native token vault address. /// @return The difference between the contract balance before and after the transferring of funds. - function _depositFundsToAssetRouter(address _from, IERC20 _token, uint256 _amount) internal returns (uint256) { - uint256 balanceBefore = _token.balanceOf(address(L1_ASSET_ROUTER)); - _token.safeTransferFrom(_from, address(L1_ASSET_ROUTER), _amount); - uint256 balanceAfter = _token.balanceOf(address(L1_ASSET_ROUTER)); + function _approveFundsToAssetRouter(address _from, IERC20 _token, uint256 _amount) internal returns (uint256) { + uint256 balanceBefore = _token.balanceOf(address(this)); + _token.safeTransferFrom(_from, address(this), _amount); + bool success = _token.approve(address(L1_ASSET_ROUTER), _amount); + if (!success) { + revert ApprovalFailed(); + } + uint256 balanceAfter = _token.balanceOf(address(this)); return balanceAfter - balanceBefore; } diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index ff94d0db5..bc97e9bfa 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -404,10 +404,17 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { // Do the transfer if allowance to Shared bridge is bigger than amount // And if there is not enough allowance for the NTV - if ( + bool weCanTransfer = false; + if (l1Token.allowance(address(legacyBridge), address(this)) >= _amount) { + _originalCaller = address(legacyBridge); + weCanTransfer = true; + } else if ( l1Token.allowance(_originalCaller, address(this)) >= _amount && l1Token.allowance(_originalCaller, address(nativeTokenVault)) < _amount ) { + weCanTransfer = true; + } + if (weCanTransfer) { // slither-disable-next-line arbitrary-send-erc20 l1Token.safeTransferFrom(_originalCaller, address(nativeTokenVault), _amount); return true; @@ -542,7 +549,7 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { // Save the deposited amount to claim funds on L1 if the deposit failed on L2 L1_NULLIFIER.bridgehubConfirmL2TransactionForwarded( ERA_CHAIN_ID, - keccak256(abi.encode(_originalCaller, _l1Token, _amount)), + DataEncoding.encodeLegacyTxDataHash(_originalCaller, _l1Token, _amount), txHash ); diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 0a10822f4..2bd0fa748 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -68,6 +68,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { } /// @dev Disable the initialization to prevent Parity hack. + /// @dev this contract is deployed in the L2GenesisUpgrade, and is meant as direct deployment without a proxy. /// @param _l1AssetRouter The address of the L1 Bridge contract. constructor( uint256 _l1ChainId, diff --git a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol index d5b059ae6..414cf4940 100644 --- a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol @@ -273,7 +273,7 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken } } - function _deployBeaconProxy(bytes32 _salt) internal override returns (BeaconProxy proxy) { + function _deployBeaconProxy(bytes32 _salt, uint256) internal override returns (BeaconProxy proxy) { // Use CREATE2 to deploy the BeaconProxy address proxyAddress = Create2.deploy( 0, diff --git a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol index 4b83037a7..b896f11df 100644 --- a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol @@ -37,6 +37,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { bytes32 internal l2TokenProxyBytecodeHash; /// @notice Initializes the bridge contract for later use. + /// @dev this contract is deployed in the L2GenesisUpgrade, and is meant as direct deployment without a proxy. /// @param _l1ChainId The L1 chain id differs between mainnet and testnets. /// @param _l2TokenProxyBytecodeHash The bytecode hash of the proxy for tokens deployed by the bridge. /// @param _aliasedOwner The address of the governor contract. @@ -92,33 +93,32 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { } /// @notice Ensures that the token is deployed. - /// @param _originChainId The chain ID of the origin chain. /// @param _assetId The asset ID. /// @param _originToken The origin token address. /// @param _erc20Data The ERC20 data. /// @return expectedToken The token address. - function _ensureTokenDeployed( - uint256 _originChainId, + function _ensureAndSaveTokenDeployed( bytes32 _assetId, address _originToken, bytes memory _erc20Data ) internal override returns (address expectedToken) { - expectedToken = _assetIdCheck(_originChainId, _assetId, _originToken); + uint256 tokenOriginChainId; + (expectedToken, tokenOriginChainId) = _calculateExpectedTokenAddress(_originToken, _erc20Data); address l1LegacyToken; if (address(L2_LEGACY_SHARED_BRIDGE) != address(0)) { l1LegacyToken = L2_LEGACY_SHARED_BRIDGE.l1TokenAddress(expectedToken); } if (l1LegacyToken != address(0)) { - /// token is a legacy token, no need to deploy - if (l1LegacyToken != _originToken) { - revert AddressMismatch(_originToken, l1LegacyToken); - } - tokenAddress[_assetId] = expectedToken; - assetId[expectedToken] = _assetId; + _ensureAndSaveTokenDeployedInnerLegacyToken({ + _assetId: _assetId, + _originToken: _originToken, + _expectedToken: expectedToken, + _l1LegacyToken: l1LegacyToken + }); } else { - super._ensureTokenDeployedInner({ - _originChainId: _originChainId, + super._ensureAndSaveTokenDeployedInner({ + _tokenOriginChainId: tokenOriginChainId, _assetId: _assetId, _originToken: _originToken, _erc20Data: _erc20Data, @@ -127,13 +127,35 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { } } - /// @notice Deploys the beacon proxy for the L2 token, while using ContractDeployer system contract. + /// @notice Ensures that the token is deployed inner for legacy tokens. + function _ensureAndSaveTokenDeployedInnerLegacyToken( + bytes32 _assetId, + address _originToken, + address _expectedToken, + address _l1LegacyToken + ) internal { + _assetIdCheck(L1_CHAIN_ID, _assetId, _originToken); + + /// token is a legacy token, no need to deploy + if (_l1LegacyToken != _originToken) { + revert AddressMismatch(_originToken, _l1LegacyToken); + } + + tokenAddress[_assetId] = _expectedToken; + assetId[_expectedToken] = _assetId; + } + + /// @notice Deploys the beacon proxy for the L2 token, while using ContractDeployer system contract or the legacy shared bridge. /// @dev This function uses raw call to ContractDeployer to make sure that exactly `l2TokenProxyBytecodeHash` is used /// for the code of the proxy. /// @param _salt The salt used for beacon proxy deployment of L2 bridged token. + /// @param _tokenOriginChainId The origin chain id of the token. /// @return proxy The beacon proxy, i.e. L2 bridged token. - function _deployBeaconProxy(bytes32 _salt) internal virtual override returns (BeaconProxy proxy) { - if (address(L2_LEGACY_SHARED_BRIDGE) == address(0)) { + function _deployBeaconProxy( + bytes32 _salt, + uint256 _tokenOriginChainId + ) internal virtual override returns (BeaconProxy proxy) { + if (address(L2_LEGACY_SHARED_BRIDGE) == address(0) || _tokenOriginChainId != L1_CHAIN_ID) { // Deploy the beacon proxy for the L2 token (bool success, bytes memory returndata) = SystemContractsCaller.systemCallWithReturndata( @@ -172,17 +194,18 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { //////////////////////////////////////////////////////////////*/ /// @notice Calculates L2 wrapped token address given the currently stored beacon proxy bytecode hash and beacon address. + /// @param _tokenOriginChainId The chain id of the origin token. /// @param _l1Token The address of token on L1. /// @return Address of an L2 token counterpart. function calculateCreate2TokenAddress( - uint256 _originChainId, + uint256 _tokenOriginChainId, address _l1Token ) public view virtual override(INativeTokenVault, NativeTokenVault) returns (address) { - bytes32 constructorInputHash = keccak256(abi.encode(address(bridgedTokenBeacon), "")); - bytes32 salt = _getCreate2Salt(_originChainId, _l1Token); - if (address(L2_LEGACY_SHARED_BRIDGE) != address(0)) { + if (address(L2_LEGACY_SHARED_BRIDGE) != address(0) && _tokenOriginChainId == L1_CHAIN_ID) { return L2_LEGACY_SHARED_BRIDGE.l2TokenAddress(_l1Token); } else { + bytes32 constructorInputHash = keccak256(abi.encode(address(bridgedTokenBeacon), "")); + bytes32 salt = _getCreate2Salt(_tokenOriginChainId, _l1Token); return L2ContractHelper.computeCreate2Address( address(this), @@ -194,10 +217,13 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { } /// @notice Calculates the salt for the Create2 deployment of the L2 token. - function _getCreate2Salt(uint256 _originChainId, address _l1Token) internal view override returns (bytes32 salt) { - salt = _originChainId == L1_CHAIN_ID + function _getCreate2Salt( + uint256 _tokenOriginChainId, + address _l1Token + ) internal view override returns (bytes32 salt) { + salt = _tokenOriginChainId == L1_CHAIN_ID ? bytes32(uint256(uint160(_l1Token))) - : keccak256(abi.encode(_originChainId, _l1Token)); + : keccak256(abi.encode(_tokenOriginChainId, _l1Token)); } function _handleChainBalanceIncrease( diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index c4d11e827..805e1e041 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -21,7 +21,7 @@ import {DataEncoding} from "../../common/libraries/DataEncoding.sol"; import {BridgedStandardERC20} from "../BridgedStandardERC20.sol"; import {BridgeHelper} from "../BridgeHelper.sol"; -import {EmptyDeposit, Unauthorized, TokensWithFeesNotSupported, TokenNotSupported, NonEmptyMsgValue, ValueMismatch, AddressMismatch, AssetIdMismatch, AmountMustBeGreaterThanZero, ZeroAddress, L1TokenDeploymentWithZeroChainId, DeployingBridgedTokenForNativeToken} from "../../common/L1ContractErrors.sol"; +import {EmptyDeposit, Unauthorized, TokensWithFeesNotSupported, TokenNotSupported, NonEmptyMsgValue, ValueMismatch, AddressMismatch, AssetIdMismatch, AmountMustBeGreaterThanZero, ZeroAddress, DeployingBridgedTokenForNativeToken} from "../../common/L1ContractErrors.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev @@ -142,7 +142,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 (, receiver, originToken, amount, erc20Data) = DataEncoding.decodeBridgeMintData(_data); if (token == address(0)) { - token = _ensureTokenDeployed(_originChainId, _assetId, originToken, erc20Data); + token = _ensureAndSaveTokenDeployed(_assetId, originToken, erc20Data); } _handleChainBalanceDecrease(_originChainId, _assetId, amount, false); IBridgedStandardToken(token).bridgeMint(receiver, amount); @@ -206,6 +206,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 address bridgedToken = tokenAddress[_assetId]; IBridgedStandardToken(bridgedToken).bridgeBurn(_originalCaller, _amount); + _handleChainBalanceIncrease(_chainId, _assetId, _amount, false); emit BridgeBurn({ chainId: _chainId, @@ -358,15 +359,15 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 TOKEN DEPLOYER FUNCTIONS //////////////////////////////////////////////////////////////*/ - function _ensureTokenDeployed( - uint256 _originChainId, + function _ensureAndSaveTokenDeployed( bytes32 _assetId, address _originToken, bytes memory _erc20Data ) internal virtual returns (address expectedToken) { - expectedToken = _assetIdCheck(_originChainId, _assetId, _originToken); - _ensureTokenDeployedInner({ - _originChainId: _originChainId, + uint256 tokenOriginChainId; + (expectedToken, tokenOriginChainId) = _calculateExpectedTokenAddress(_originToken, _erc20Data); + _ensureAndSaveTokenDeployedInner({ + _tokenOriginChainId: tokenOriginChainId, _assetId: _assetId, _originToken: _originToken, _erc20Data: _erc20Data, @@ -374,27 +375,44 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 }); } - function _assetIdCheck( - uint256 _originChainId, - bytes32 _assetId, - address _originToken - ) internal view returns (address expectedToken) { - expectedToken = calculateCreate2TokenAddress(_originChainId, _originToken); - bytes32 expectedAssetId = DataEncoding.encodeNTVAssetId(_originChainId, _originToken); + /// @notice Calculates the bridged token address corresponding to native token counterpart. + function _calculateExpectedTokenAddress( + address _originToken, + bytes memory _erc20Data + ) internal view returns (address expectedToken, uint256 tokenOriginChainId) { + /// @dev calling externally to convert from memory to calldata + tokenOriginChainId = this.tokenDataOriginChainId(_erc20Data); + expectedToken = calculateCreate2TokenAddress(tokenOriginChainId, _originToken); + } + + /// @notice Returns the origin chain id from the token data. + function tokenDataOriginChainId(bytes calldata _erc20Data) public view returns (uint256 tokenOriginChainId) { + // slither-disable-next-line unused-return + (tokenOriginChainId, , , ) = DataEncoding.decodeTokenData(_erc20Data); + if (tokenOriginChainId == 0) { + tokenOriginChainId = L1_CHAIN_ID; + } + } + + /// @notice Checks that the assetId is correct for the origin token and chain. + function _assetIdCheck(uint256 _tokenOriginChainId, bytes32 _assetId, address _originToken) internal view { + bytes32 expectedAssetId = DataEncoding.encodeNTVAssetId(_tokenOriginChainId, _originToken); if (_assetId != expectedAssetId) { // Make sure that a NativeTokenVault sent the message revert AssetIdMismatch(_assetId, expectedAssetId); } } - function _ensureTokenDeployedInner( - uint256 _originChainId, + function _ensureAndSaveTokenDeployedInner( + uint256 _tokenOriginChainId, bytes32 _assetId, address _originToken, bytes memory _erc20Data, address _expectedToken ) internal { - address deployedToken = _deployBridgedToken(_originChainId, _assetId, _originToken, _erc20Data); + _assetIdCheck(_tokenOriginChainId, _assetId, _originToken); + + address deployedToken = _deployBridgedToken(_tokenOriginChainId, _assetId, _originToken, _erc20Data); if (deployedToken != _expectedToken) { revert AddressMismatch(_expectedToken, deployedToken); } @@ -404,40 +422,34 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 } /// @notice Calculates the bridged token address corresponding to native token counterpart. + /// @param _tokenOriginChainId The chain id of the origin token. /// @param _bridgeToken The address of native token. /// @return The address of bridged token. function calculateCreate2TokenAddress( - uint256 _originChainId, + uint256 _tokenOriginChainId, address _bridgeToken ) public view virtual override returns (address); /// @notice Deploys and initializes the bridged token for the native counterpart. + /// @param _tokenOriginChainId The chain id of the origin token. /// @param _originToken The address of origin token. /// @param _erc20Data The ERC20 metadata of the token deployed. /// @return The address of the beacon proxy (bridged token). function _deployBridgedToken( - uint256 _originChainId, + uint256 _tokenOriginChainId, bytes32 _assetId, address _originToken, bytes memory _erc20Data ) internal returns (address) { - bytes32 salt = _getCreate2Salt(_originChainId, _originToken); - - BeaconProxy l2Token = _deployBeaconProxy(salt); - uint256 tokenOriginChainId = BridgedStandardERC20(address(l2Token)).bridgeInitialize( - _assetId, - _originToken, - _erc20Data - ); - // an extra check for legacy tokens on L1, they might not be registered i.e. - if (block.chainid == L1_CHAIN_ID && tokenOriginChainId == 0) { - revert L1TokenDeploymentWithZeroChainId(_assetId); - } - tokenOriginChainId = tokenOriginChainId == 0 ? L1_CHAIN_ID : tokenOriginChainId; - if (tokenOriginChainId == block.chainid) { + if (_tokenOriginChainId == block.chainid) { revert DeployingBridgedTokenForNativeToken(); } - originChainId[DataEncoding.encodeNTVAssetId(tokenOriginChainId, _originToken)] = tokenOriginChainId; + bytes32 salt = _getCreate2Salt(_tokenOriginChainId, _originToken); + + BeaconProxy l2Token = _deployBeaconProxy(salt, _tokenOriginChainId); + BridgedStandardERC20(address(l2Token)).bridgeInitialize(_assetId, _originToken, _erc20Data); + + originChainId[_assetId] = _tokenOriginChainId; return address(l2Token); } @@ -453,7 +465,10 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 /// for the code of the proxy. /// @param _salt The salt used for beacon proxy deployment of the bridged token (we pass the native token address). /// @return proxy The beacon proxy, i.e. bridged token. - function _deployBeaconProxy(bytes32 _salt) internal virtual returns (BeaconProxy proxy); + function _deployBeaconProxy( + bytes32 _salt, + uint256 _tokenOriginChainId + ) internal virtual returns (BeaconProxy proxy); /*////////////////////////////////////////////////////////////// PAUSE diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index e084bc3d1..c31bcf285 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -229,7 +229,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @notice Used for the upgrade to set the baseTokenAssetId previously stored as baseToken. /// @param _chainId the chainId of the chain. function setLegacyBaseTokenAssetId(uint256 _chainId) external override { - if (baseTokenAssetId[_chainId] == bytes32(0)) { + if (baseTokenAssetId[_chainId] != bytes32(0)) { return; } address token = __DEPRECATED_baseToken[_chainId]; @@ -435,11 +435,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus if (ctmAddress == address(0)) { revert ChainIdNotRegistered(_chainId); } - return ctmAssetIdFromAddress[chainTypeManager[_chainId]]; - } - - function calculateCtmAssetId(address _ctmAddress) internal view returns (bytes32) { - return keccak256(abi.encode(L1_CHAIN_ID, address(l1CtmDeployer), bytes32(uint256(uint160(_ctmAddress))))); + return ctmAssetIdFromAddress[ctmAddress]; } /*////////////////////////////////////////////////////////////// @@ -777,7 +773,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus ) external payable override onlyAssetRouter onlyL1 { BridgehubBurnCTMAssetData memory bridgehubData = abi.decode(_data, (BridgehubBurnCTMAssetData)); - delete settlementLayer[bridgehubData.chainId]; + settlementLayer[bridgehubData.chainId] = block.chainid; IChainTypeManager(chainTypeManager[bridgehubData.chainId]).forwardedBridgeRecoverFailedTransfer({ _chainId: bridgehubData.chainId, diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 89fbf482d..a31ac3f14 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -5,6 +5,8 @@ pragma solidity ^0.8.21; error AccessToFallbackDenied(address target, address invoker); // 0x3995f750 error AccessToFunctionDenied(address target, bytes4 selector, address invoker); +// 0x8164f842 +error ApprovalFailed(); // 0x6c167909 error OnlySelfAllowed(); // 0x52e22c98 diff --git a/l1-contracts/contracts/common/libraries/DataEncoding.sol b/l1-contracts/contracts/common/libraries/DataEncoding.sol index 9df83d67a..e586bd051 100644 --- a/l1-contracts/contracts/common/libraries/DataEncoding.sol +++ b/l1-contracts/contracts/common/libraries/DataEncoding.sol @@ -108,7 +108,7 @@ library DataEncoding { if (_encodingVersion == LEGACY_ENCODING_VERSION) { address tokenAddress = INativeTokenVault(_nativeTokenVault).tokenAddress(_assetId); (uint256 depositAmount, ) = abi.decode(_transferData, (uint256, address)); - txDataHash = keccak256(abi.encode(_originalCaller, tokenAddress, depositAmount)); + txDataHash = encodeLegacyTxDataHash(_originalCaller, tokenAddress, depositAmount); } else if (_encodingVersion == NEW_ENCODING_VERSION) { // Similarly to calldata, the txDataHash is collision-resistant. // In the legacy data hash, the first encoded variable was the address, which is padded with zeros during `abi.encode`. @@ -120,6 +120,20 @@ library DataEncoding { } } + /// @notice Encodes the legacy transaction data hash. + /// @dev the encoding strats with 0t + /// @param _originalCaller The address of the entity that initiated the deposit. + /// @param _l1Token The address of the L1 token. + /// @param _amount The amount of the L1 token. + /// @return txDataHash The resulting encoded transaction data hash. + function encodeLegacyTxDataHash( + address _originalCaller, + address _l1Token, + uint256 _amount + ) internal pure returns (bytes32) { + return keccak256(abi.encode(_originalCaller, _l1Token, _amount)); + } + /// @notice Decodes the token data by combining chain id, asset deployment tracker and asset data. function decodeTokenData( bytes calldata _tokenData diff --git a/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol b/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol index fc1991503..46960489c 100644 --- a/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol +++ b/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol @@ -66,7 +66,7 @@ contract L2NativeTokenVaultDev is L2NativeTokenVault { // test } - function _deployBeaconProxy(bytes32 _salt) internal virtual override returns (BeaconProxy proxy) { + function _deployBeaconProxy(bytes32 _salt, uint256) internal virtual override returns (BeaconProxy proxy) { // Use CREATE2 to deploy the BeaconProxy address proxyAddress = Create2.deploy( 0, diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol index 27bbe3155..1f25dc6df 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol @@ -320,7 +320,7 @@ contract AdminFacet is ZKChainBase, IAdmin { ); require(_contractAlreadyDeployed, "Af: contract not deployed"); require(s.settlementLayer != address(0), "Af: not migrated"); - s.priorityTree.checkL1Reinit(_commitment.priorityTree); + s.priorityTree.l1Reinit(_commitment.priorityTree); } else if (_contractAlreadyDeployed) { require(s.settlementLayer != address(0), "Af: not migrated 2"); s.priorityTree.checkGWReinit(_commitment.priorityTree); diff --git a/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol b/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol index 71d6d9df1..9b3360f03 100644 --- a/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol +++ b/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol @@ -98,10 +98,12 @@ library PriorityTree { } /// @notice Reinitialize the tree from a commitment on L1. - function checkL1Reinit(Tree storage _tree, PriorityTreeCommitment memory _commitment) internal view { + function l1Reinit(Tree storage _tree, PriorityTreeCommitment memory _commitment) internal { require(_tree.startIndex == _commitment.startIndex, "PT: invalid start index"); - require(_tree.unprocessedIndex >= _commitment.unprocessedIndex, "PT: invalid unprocessed index"); + require(_tree.unprocessedIndex <= _commitment.unprocessedIndex, "PT: invalid unprocessed index"); require(_tree.tree._nextLeafIndex >= _commitment.nextLeafIndex, "PT: invalid next leaf index"); + + _tree.unprocessedIndex = _commitment.unprocessedIndex; } /// @notice Reinitialize the tree from a commitment on GW. diff --git a/l1-contracts/test/foundry/l1/integration/AssetRouterTest.t.sol b/l1-contracts/test/foundry/l1/integration/AssetRouterTest.t.sol index 7d122638d..a3060b9ec 100644 --- a/l1-contracts/test/foundry/l1/integration/AssetRouterTest.t.sol +++ b/l1-contracts/test/foundry/l1/integration/AssetRouterTest.t.sol @@ -90,7 +90,7 @@ contract AssetRouterTest is L1ContractDeployer, ZKChainDeployer, TokenDeployer, bytes memory transferData = DataEncoding.encodeBridgeMintData({ _originalCaller: ETH_TOKEN_ADDRESS, _l2Receiver: address(this), - _l1Token: ETH_TOKEN_ADDRESS, + _l1Token: _tokenAddress, _amount: 100, _erc20Metadata: BridgeHelper.getERC20Getters(_tokenAddress, chainId) });