diff --git a/l1-contracts/contracts/bridge/L1AssetRouter.sol b/l1-contracts/contracts/bridge/L1AssetRouter.sol index 6dff9cc36..02ce8452a 100644 --- a/l1-contracts/contracts/bridge/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/L1AssetRouter.sol @@ -31,6 +31,8 @@ import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR, L2_ASSET_ROUTER_ADDR} from "../commo import {BridgeHelper} from "./BridgeHelper.sol"; +import {IL1AssetDeploymentTracker} from "../bridge/interfaces/IL1AssetDeploymentTracker.sol"; + /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @dev Bridges assets between L1 and ZK chain, supporting both ETH and ERC20 tokens. @@ -50,6 +52,15 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab /// @dev The address of ZKsync Era diamond proxy contract. address internal immutable ERA_DIAMOND_PROXY; + /// @dev The encoding version used for new txs. + bytes1 internal constant LEGACY_ENCODING_VERSION = 0x00; + + /// @dev The encoding version used for legacy txs. + bytes1 internal constant NEW_ENCODING_VERSION = 0x01; + + /// @dev The encoding version used for txs that set the asset handler on the counterpart contract. + bytes1 internal constant SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION = 0x02; + /// @dev Stores the first batch number on the ZKsync Era Diamond Proxy that was settled after Diamond proxy upgrade. /// This variable is used to differentiate between pre-upgrade and post-upgrade Eth withdrawals. Withdrawals from batches older /// than this value are considered to have been finalized prior to the upgrade and handled separately. @@ -237,7 +248,7 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab /// @notice Sets the asset handler address for a specified asset ID on the chain of the asset deployment tracker. /// @dev The caller of this function is encoded within the `assetId`, therefore, it should be invoked by the asset deployment tracker contract. /// @dev Typically, for most tokens, ADT is the native token vault. However, custom tokens may have their own specific asset deployment trackers. - /// @dev `setAssetHandlerAddressOnCounterPart` should be called on L1 to set asset handlers on L2 chains for a specific asset ID. + /// @dev `setAssetHandlerAddressOnCounterpart` should be called on L1 to set asset handlers on L2 chains for a specific asset ID. /// @param _assetRegistrationData The asset data which may include the asset address and any additional required data or encodings. /// @param _assetHandlerAddress The address of the asset handler to be set for the provided asset. function setAssetHandlerAddressThisChain(bytes32 _assetRegistrationData, address _assetHandlerAddress) external { @@ -255,41 +266,33 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab /// @notice Used to set the asset handler address for a given asset ID on a remote ZK chain /// @dev No access control on the caller, as msg.sender is encoded in the assetId. /// @param _chainId The ZK chain ID. - /// @param _mintValue The value withdrawn by base token bridge to cover for l2 gas and l2 msg.value costs. - /// @param _l2TxGasLimit The L2 gas limit to be used in the corresponding L2 transaction. - /// @param _l2TxGasPerPubdataByte The gasPerPubdataByteLimit to be used in the corresponding L2 transaction. - /// @param _refundRecipient The address on L2 that will receive the refund for the transaction. /// @param _assetId The encoding of asset ID. - /// @param _assetHandlerAddressOnCounterPart The address of the asset handler, which will hold the token of interest. - /// @return txHash The L2 transaction hash of setting asset handler on remote chain. - function setAssetHandlerAddressOnCounterPart( + /// @param _assetHandlerAddressOnCounterpart The address of the asset handler, which will hold the token of interest. + /// @return request The tx request sent to the Bridgehub + function _setAssetHandlerAddressOnCounterpart( uint256 _chainId, - uint256 _mintValue, - uint256 _l2TxGasLimit, - uint256 _l2TxGasPerPubdataByte, - address _refundRecipient, + address _prevMsgSender, bytes32 _assetId, - address _assetHandlerAddressOnCounterPart - ) external payable returns (bytes32 txHash) { - require(msg.sender == assetDeploymentTracker[_assetId] || msg.sender == owner(), "L1AR: only ADT or owner"); + address _assetHandlerAddressOnCounterpart + ) internal returns (L2TransactionRequestTwoBridgesInner memory request) { + IL1AssetDeploymentTracker(assetDeploymentTracker[_assetId]).bridgeCheckCounterpartAddress( + _chainId, + _assetId, + _prevMsgSender, + _assetHandlerAddressOnCounterpart + ); bytes memory l2Calldata = abi.encodeCall( IL2Bridge.setAssetHandlerAddress, - (_assetId, _assetHandlerAddressOnCounterPart) + (_assetId, _assetHandlerAddressOnCounterpart) ); - - L2TransactionRequestDirect memory request = L2TransactionRequestDirect({ - chainId: _chainId, + request = L2TransactionRequestTwoBridgesInner({ + magicValue: TWO_BRIDGES_MAGIC_VALUE, l2Contract: L2_ASSET_ROUTER_ADDR, - mintValue: _mintValue, // l2 gas + l2 msg.value the bridgehub will withdraw the mintValue from the base token bridge for gas - l2Value: 0, // For base token deposits, there is no msg.value during the call, as the base token is minted to the recipient address l2Calldata: l2Calldata, - l2GasLimit: _l2TxGasLimit, - l2GasPerPubdataByteLimit: _l2TxGasPerPubdataByte, factoryDeps: new bytes[](0), - refundRecipient: _refundRecipient + txDataHash: bytes32(0x00) }); - txHash = BRIDGE_HUB.requestL2TransactionDirect{value: msg.value}(request); } /// @notice Allows bridgehub to acquire mintValue for L1->L2 transactions. @@ -342,12 +345,20 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab { bytes32 assetId; bytes memory transferData; - bool legacyDeposit = false; bytes1 encodingVersion = _data[0]; // The new encoding ensures that the calldata is collision-resistant with respect to the legacy format. // In the legacy calldata, the first input was the address, meaning the most significant byte was always `0x00`. - if (encodingVersion == 0x01) { + if (encodingVersion == SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION) { + (bytes32 _assetId, address _assetHandlerAddressOnCounterpart) = abi.decode(_data[1:], (bytes32, address)); + return + _setAssetHandlerAddressOnCounterpart( + _chainId, + _prevMsgSender, + _assetId, + _assetHandlerAddressOnCounterpart + ); + } else if (encodingVersion == NEW_ENCODING_VERSION) { (assetId, transferData) = abi.decode(_data[1:], (bytes32, bytes)); require( assetHandlerAddress[assetId] != address(nativeTokenVault), @@ -355,7 +366,6 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab ); } else { (assetId, transferData) = _handleLegacyData(_data, _prevMsgSender); - legacyDeposit = true; } require(BRIDGE_HUB.baseTokenAssetId(_chainId) != assetId, "L1AR: baseToken deposit not supported"); @@ -368,7 +378,7 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab _transferData: transferData, _passValue: true }); - bytes32 txDataHash = this.encodeTxDataHash(legacyDeposit, _prevMsgSender, assetId, transferData); + bytes32 txDataHash = this.encodeTxDataHash(encodingVersion, _prevMsgSender, assetId, transferData); request = _requestToBridge({ _prevMsgSender: _prevMsgSender, @@ -427,18 +437,18 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab } /// @dev Calls the internal `_encodeTxDataHash`. Used as a wrapped for try / catch case. - /// @param _isLegacyEncoding Boolean flag indicating whether to use the legacy encoding standard (true) or the latest encoding standard (false). + /// @param _encodingVersion The version of the encoding. /// @param _prevMsgSender The address of the entity that initiated the deposit. /// @param _assetId The unique identifier of the deposited L1 token. /// @param _transferData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. /// @return txDataHash The resulting encoded transaction data hash. function encodeTxDataHash( - bool _isLegacyEncoding, + bytes1 _encodingVersion, address _prevMsgSender, bytes32 _assetId, bytes calldata _transferData ) external view returns (bytes32 txDataHash) { - return _encodeTxDataHash(_isLegacyEncoding, _prevMsgSender, _assetId, _transferData); + return _encodeTxDataHash(_encodingVersion, _prevMsgSender, _assetId, _transferData); } /// @dev Withdraw funds from the initiated deposit, that failed when finalizing on L2. @@ -484,7 +494,7 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab // If the dataHash matches the legacy transaction hash, skip the next step. // Otherwise, perform the check using the new transaction data hash encoding. if (!isLegacyTxDataHash) { - bytes32 txDataHash = _encodeTxDataHash(false, _depositSender, _assetId, _assetData); + bytes32 txDataHash = _encodeTxDataHash(NEW_ENCODING_VERSION, _depositSender, _assetId, _assetData); require(dataHash == txDataHash, "L1AR: d.it not hap"); } } @@ -702,7 +712,9 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab bytes memory _transferData, bytes32 _expectedTxDataHash ) internal view returns (bool isLegacyTxDataHash) { - try this.encodeTxDataHash(true, _prevMsgSender, _assetId, _transferData) returns (bytes32 txDataHash) { + try this.encodeTxDataHash(LEGACY_ENCODING_VERSION, _prevMsgSender, _assetId, _transferData) returns ( + bytes32 txDataHash + ) { return txDataHash == _expectedTxDataHash; } catch { return false; @@ -710,24 +722,24 @@ contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeab } /// @dev Encodes the transaction data hash using either the latest encoding standard or the legacy standard. - /// @param _isLegacyEncoding Boolean flag indicating whether to use the legacy encoding standard (true) or the latest encoding standard (false). + /// @param _encodingVersion EncodingVersion. /// @param _prevMsgSender The address of the entity that initiated the deposit. /// @param _assetId The unique identifier of the deposited L1 token. /// @param _transferData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. /// @return txDataHash The resulting encoded transaction data hash. function _encodeTxDataHash( - bool _isLegacyEncoding, + bytes1 _encodingVersion, address _prevMsgSender, bytes32 _assetId, bytes memory _transferData ) internal view returns (bytes32 txDataHash) { - if (_isLegacyEncoding) { + if (_encodingVersion == LEGACY_ENCODING_VERSION) { (uint256 depositAmount, ) = abi.decode(_transferData, (uint256, address)); txDataHash = keccak256(abi.encode(_prevMsgSender, nativeTokenVault.tokenAddress(_assetId), depositAmount)); } else { // 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`. - txDataHash = keccak256(bytes.concat(bytes1(0x01), abi.encode(_prevMsgSender, _assetId, _transferData))); + txDataHash = keccak256(bytes.concat(_encodingVersion, abi.encode(_prevMsgSender, _assetId, _transferData))); } } diff --git a/l1-contracts/contracts/bridge/interfaces/IL1AssetDeploymentTracker.sol b/l1-contracts/contracts/bridge/interfaces/IL1AssetDeploymentTracker.sol new file mode 100644 index 000000000..cb464e5a1 --- /dev/null +++ b/l1-contracts/contracts/bridge/interfaces/IL1AssetDeploymentTracker.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +interface IL1AssetDeploymentTracker { + function bridgeCheckCounterpartAddress( + uint256 _chainId, + bytes32 _assetId, + address _prevMsgSender, + address _assetHandlerAddressOnCounterpart + ) external view; +} diff --git a/l1-contracts/contracts/bridge/interfaces/IL1AssetRouter.sol b/l1-contracts/contracts/bridge/interfaces/IL1AssetRouter.sol index c5cbbc87f..945f272f2 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL1AssetRouter.sol @@ -152,16 +152,6 @@ interface IL1AssetRouter { function setAssetHandlerAddressThisChain(bytes32 _additionalData, address _assetHandlerAddress) external; - function setAssetHandlerAddressOnCounterPart( - uint256 _chainId, - uint256 _mintValue, - uint256 _l2TxGasLimit, - uint256 _l2TxGasPerPubdataByte, - address _refundRecipient, - bytes32 _assetId, - address _assetAddressOnCounterPart - ) external payable returns (bytes32 l2TxHash); - function assetHandlerAddress(bytes32 _assetId) external view returns (address); function nativeTokenVault() external view returns (IL1NativeTokenVault); diff --git a/l1-contracts/contracts/bridgehub/ISTMDeploymentTracker.sol b/l1-contracts/contracts/bridgehub/ISTMDeploymentTracker.sol index a1f71cdbf..e55da57ee 100644 --- a/l1-contracts/contracts/bridgehub/ISTMDeploymentTracker.sol +++ b/l1-contracts/contracts/bridgehub/ISTMDeploymentTracker.sol @@ -4,10 +4,11 @@ pragma solidity 0.8.24; import {L2TransactionRequestTwoBridgesInner, IBridgehub} from "./IBridgehub.sol"; import {IL1AssetRouter} from "../bridge/interfaces/IL1AssetRouter.sol"; +import {IL1AssetDeploymentTracker} from "../bridge/interfaces/IL1AssetDeploymentTracker.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev -interface ISTMDeploymentTracker { +interface ISTMDeploymentTracker is IL1AssetDeploymentTracker { function bridgehubDeposit( uint256 _chainId, address _prevMsgSender, @@ -17,19 +18,9 @@ interface ISTMDeploymentTracker { function BRIDGE_HUB() external view returns (IBridgehub); - function SHARED_BRIDGE() external view returns (IL1AssetRouter); + function L1_ASSET_ROUTER() external view returns (IL1AssetRouter); function registerSTMAssetOnL1(address _stmAddress) external; function getAssetId(address _l1STM) external view returns (bytes32); - - // todo temporary, will move into L1AssetRouter bridgehubDeposit - function registerSTMAssetOnL2SharedBridge( - uint256 _chainId, - address _stmL1Address, - uint256 _mintValue, - uint256 _l2TxGasLimit, - uint256 _l2TxGasPerPubdataByteLimit, - address _refundRecipient - ) external payable; } diff --git a/l1-contracts/contracts/bridgehub/STMDeploymentTracker.sol b/l1-contracts/contracts/bridgehub/STMDeploymentTracker.sol index 3c99e61d7..e9ccacb5a 100644 --- a/l1-contracts/contracts/bridgehub/STMDeploymentTracker.sol +++ b/l1-contracts/contracts/bridgehub/STMDeploymentTracker.sol @@ -23,7 +23,7 @@ contract STMDeploymentTracker is ISTMDeploymentTracker, ReentrancyGuard, Ownable IBridgehub public immutable override BRIDGE_HUB; /// @dev Bridgehub smart contract that is used to operate with L2 via asynchronous L2 <-> L1 communication. - IL1AssetRouter public immutable override SHARED_BRIDGE; + IL1AssetRouter public immutable override L1_ASSET_ROUTER; /// @notice Checks that the message sender is the bridgehub. modifier onlyBridgehub() { @@ -32,12 +32,19 @@ contract STMDeploymentTracker is ISTMDeploymentTracker, ReentrancyGuard, Ownable _; } + /// @notice Checks that the message sender is the bridgehub. + modifier onlyOwnerViaRouter(address _prevMsgSender) { + // solhint-disable-next-line gas-custom-errors + require(msg.sender == address(L1_ASSET_ROUTER) && _prevMsgSender == owner(), "STM DT: not owner via router"); + _; + } + /// @dev Contract is expected to be used as proxy implementation on L1. /// @dev Initialize the implementation to prevent Parity hack. constructor(IBridgehub _bridgehub, IL1AssetRouter _sharedBridge) reentrancyGuardInitializer { _disableInitializers(); BRIDGE_HUB = _bridgehub; - SHARED_BRIDGE = _sharedBridge; + L1_ASSET_ROUTER = _sharedBridge; } /// @notice used to initialize the contract @@ -52,7 +59,7 @@ contract STMDeploymentTracker is ISTMDeploymentTracker, ReentrancyGuard, Ownable // solhint-disable-next-line gas-custom-errors require(BRIDGE_HUB.stateTransitionManagerIsRegistered(_stmAddress), "STMDT: stm not registered"); - SHARED_BRIDGE.setAssetHandlerAddressThisChain(bytes32(uint256(uint160(_stmAddress))), address(BRIDGE_HUB)); + L1_ASSET_ROUTER.setAssetHandlerAddressThisChain(bytes32(uint256(uint160(_stmAddress))), address(BRIDGE_HUB)); BRIDGE_HUB.setAssetHandlerAddress(bytes32(uint256(uint160(_stmAddress))), _stmAddress); } @@ -91,31 +98,16 @@ contract STMDeploymentTracker is ISTMDeploymentTracker, ReentrancyGuard, Ownable /// @dev Not used in this contract. In case the transaction fails, we can just re-try it. function bridgehubConfirmL2Transaction(uint256 _chainId, bytes32 _txDataHash, bytes32 _txHash) external {} - // todo this has to be put in L1AssetRouter via TwoBridges for custom base tokens. Hard, because we have to have multiple msg types in bridgehubDeposit in the AssetRouter. /// @notice Used to register the stm asset in L2 AssetRouter. - /// @param _chainId the chainId of the chain - function registerSTMAssetOnL2SharedBridge( - uint256 _chainId, - address _stmL1Address, - uint256 _mintValue, - uint256 _l2TxGasLimit, - uint256 _l2TxGasPerPubdataByteLimit, - address _refundRecipient - ) public payable onlyOwner { - bytes32 assetId; - { - assetId = getAssetId(_stmL1Address); - } - // slither-disable-next-line unused-return - SHARED_BRIDGE.setAssetHandlerAddressOnCounterPart{value: msg.value}({ - _chainId: _chainId, - _mintValue: _mintValue, - _l2TxGasLimit: _l2TxGasLimit, - _l2TxGasPerPubdataByte: _l2TxGasPerPubdataByteLimit, - _refundRecipient: _refundRecipient, - _assetId: assetId, - _assetAddressOnCounterPart: L2_BRIDGEHUB_ADDR - }); + /// @param _prevMsgSender the address that called the Router + /// @param _assetHandlerAddressOnCounterpart the address of the asset handler on the counterpart chain. + function bridgeCheckCounterpartAddress( + uint256, + bytes32, + address _prevMsgSender, + address _assetHandlerAddressOnCounterpart + ) external view override onlyOwnerViaRouter(_prevMsgSender) { + require(_assetHandlerAddressOnCounterpart == L2_BRIDGEHUB_ADDR, "STMDT: wrong counter part"); } function getAssetId(address _l1STM) public view override returns (bytes32) { diff --git a/l1-contracts/deploy-scripts/Gateway.s.sol b/l1-contracts/deploy-scripts/Gateway.s.sol index 84bc3909c..6e24be0cf 100644 --- a/l1-contracts/deploy-scripts/Gateway.s.sol +++ b/l1-contracts/deploy-scripts/Gateway.s.sol @@ -17,6 +17,7 @@ import {IZkSyncHyperchain} from "contracts/state-transition/chain-interfaces/IZk // import {IL1NativeTokenVault} from "contracts/bridge/interfaces/IL1NativeTokenVault.sol"; import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA} from "contracts/common/Config.sol"; import {L2TransactionRequestTwoBridgesOuter} from "contracts/bridgehub/IBridgehub.sol"; +import {L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; // import {IL1AssetRouter} from "contracts/bridge/interfaces/IL1AssetRouter.sol"; @@ -189,8 +190,22 @@ contract GatewayScript is Script { l2GasLimit, REQUIRED_L2_GAS_PRICE_PER_PUBDATA ) * 2; - - L2TransactionRequestTwoBridgesOuter memory request = L2TransactionRequestTwoBridgesOuter({ + bytes32 assetId = bridgehub.stmAssetIdFromChainId(config.chainChainId); + bytes memory routerData = bytes.concat(bytes1(0x02), abi.encode(assetId, L2_BRIDGEHUB_ADDR)); + L2TransactionRequestTwoBridgesOuter + memory assetRouterRegistrationRequest = L2TransactionRequestTwoBridgesOuter({ + chainId: config.chainChainId, + mintValue: expectedCost, + l2Value: 0, + l2GasLimit: l2GasLimit, + l2GasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA, + refundRecipient: ownable.owner(), + secondBridgeAddress: config.sharedBridgeProxy, + secondBridgeValue: 0, + secondBridgeCalldata: routerData + }); + + L2TransactionRequestTwoBridgesOuter memory bridehubRegistrationRequest = L2TransactionRequestTwoBridgesOuter({ chainId: config.chainChainId, mintValue: expectedCost, l2Value: 0, @@ -202,7 +217,8 @@ contract GatewayScript is Script { secondBridgeCalldata: abi.encode(config.stateTransitionProxy, config.stateTransitionProxy) }); vm.startBroadcast(ownable.owner()); - bridgehub.requestL2TransactionTwoBridges{value: expectedCost}(request); + bridgehub.requestL2TransactionTwoBridges{value: expectedCost}(assetRouterRegistrationRequest); + bridgehub.requestL2TransactionTwoBridges{value: expectedCost}(bridehubRegistrationRequest); vm.stopBroadcast(); } } diff --git a/l1-contracts/scripts/sync-layer.ts b/l1-contracts/scripts/sync-layer.ts index d6f790573..73a69ea66 100644 --- a/l1-contracts/scripts/sync-layer.ts +++ b/l1-contracts/scripts/sync-layer.ts @@ -15,6 +15,7 @@ import { ADDRESS_ONE, REQUIRED_L2_GAS_PRICE_PER_PUBDATA, priorityTxMaxGasLimit, + L2_BRIDGEHUB_ADDRESS, } from "../src.ts/utils"; import { Wallet as ZkWallet, Provider as ZkProvider, utils as zkUtils } from "zksync-ethers"; @@ -309,11 +310,9 @@ async function main() { async function registerSLContractsOnL1(deployer: Deployer) { /// STM asset info /// l2Bridgehub in L1Bridghub - const bridgehubOnGateway = getAddressFromEnv("GATEWAY_BRIDGEHUB_PROXY_ADDR"); const chainId = getNumberFromEnv("CHAIN_ETH_ZKSYNC_NETWORK_ID"); - console.log(`Bridghub on Gateway: ${bridgehubOnGateway}`); console.log(`Gateway chain Id: ${chainId}`); const l1STM = deployer.stateTransitionManagerContract(deployer.deployWallet); @@ -329,12 +328,6 @@ async function registerSLContractsOnL1(deployer: Deployer) { ); console.log("Registering Bridgehub counter part on the Gateway", receipt1.transactionHash); - // await deployer.executeUpgrade( - // l1Bridgehub.address, // kl todo fix. The BH has the counterpart, the BH needs to be deployed on L2, and the STM needs to be registered in the L2 BH. - // 0, - // l1Bridgehub.interface.encodeFunctionData("registerCounterpart", [chainId, bridgehubOnGateway]) - // ); - // console.log("Gateway registration completed in L1 Bridgehub"); const gasPrice = (await deployer.deployWallet.provider.getGasPrice()).mul(GAS_MULTIPLIER); const value = ( @@ -352,17 +345,26 @@ async function registerSLContractsOnL1(deployer: Deployer) { ); } const stmDeploymentTracker = deployer.stmDeploymentTracker(deployer.deployWallet); + const assetRouter = deployer.defaultSharedBridge(deployer.deployWallet); + const assetId = await l1Bridgehub.stmAssetIdFromChainId(chainId); const receipt2 = await deployer.executeUpgrade( - stmDeploymentTracker.address, - value, - stmDeploymentTracker.interface.encodeFunctionData("registerSTMAssetOnL2SharedBridge", [ - chainId, - l1STM.address, - value, - priorityTxMaxGasLimit, - SYSTEM_CONFIG.requiredL2GasPricePerPubdata, - deployer.deployWallet.address, + l1Bridgehub.address, + ethIsBaseToken ? value : 0, + l1Bridgehub.encodeFunctionData("requestL2TransactionTwoBridges", [ + { + chainId, + mintValue: value, + l2Value: 0, + l2GasLimit: priorityTxMaxGasLimit, + l2GasPerPubdataByteLimit: SYSTEM_CONFIG.requiredL2GasPricePerPubdata, + refundRecipient: deployer.deployWallet.address, + secondBridgeAddress: assetRouter.address, + secondBridgeValue: 0, + secondBridgeCalldata: + "0x02" + + ethers.utils.defaultAbiCoder.encode(["address", "address"], [assetId, L2_BRIDGEHUB_ADDRESS]).slice(2), + }, ]) ); const l2TxHash = zkUtils.getL2HashFromPriorityOp(receipt2, gatewayAddress); diff --git a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol index 3ced5fb4e..5b1b88ba4 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeBase.t.sol @@ -34,25 +34,6 @@ contract L1AssetRouterTestBase is L1AssetRouterTest { assertEq(sharedBridge.paused(), false, "Shared Bridge Remains Paused"); } - function test_setAssetHandlerAddressOnCounterPart() public payable { - uint256 l2TxGasLimit = 100000; - uint256 l2TxGasPerPubdataByte = 100; - uint256 mintValue = 1; - address refundRecipient = address(0); - - vm.deal(owner, amount); - vm.prank(owner); - sharedBridge.setAssetHandlerAddressOnCounterPart{value: 1}( - eraChainId, - mintValue, - l2TxGasLimit, - l2TxGasPerPubdataByte, - refundRecipient, - tokenAssetId, - address(token) - ); - } - function test_bridgehubDepositBaseToken_Eth() public { vm.prank(bridgehubAddress); // solhint-disable-next-line func-named-parameters diff --git a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol index ccfac0b39..685b1d9b8 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol @@ -93,23 +93,23 @@ contract L1AssetRouterFailTest is L1AssetRouterTest { sharedBridge.setNativeTokenVault(IL1NativeTokenVault(address(0))); } - function test_setAssetHandlerAddressOnCounterPart_notOwnerOrADT() public { - uint256 l2TxGasLimit = 100000; - uint256 l2TxGasPerPubdataByte = 100; - address refundRecipient = address(0); - - vm.prank(alice); - vm.expectRevert("L1AR: only ADT or owner"); - sharedBridge.setAssetHandlerAddressOnCounterPart( - eraChainId, - mintValue, - l2TxGasLimit, - l2TxGasPerPubdataByte, - refundRecipient, - tokenAssetId, - address(token) - ); - } + // function test_setAssetHandlerAddressOnCounterpart_notOwnerOrADT() public { + // uint256 l2TxGasLimit = 100000; + // uint256 l2TxGasPerPubdataByte = 100; + // address refundRecipient = address(0); + + // vm.prank(alice); + // vm.expectRevert("L1AR: only ADT or owner"); + // sharedBridge.setAssetHandlerAddressOnCounterpart( + // eraChainId, + // mintValue, + // l2TxGasLimit, + // l2TxGasPerPubdataByte, + // refundRecipient, + // tokenAssetId, + // address(token) + // ); + // } // function test_transferFundsToSharedBridge_Eth_CallFailed() public { // vm.mockCall(address(nativeTokenVault), "0x", abi.encode("")); diff --git a/l1-contracts/test/unit_tests/synclayer.spec.ts b/l1-contracts/test/unit_tests/synclayer.spec.ts index d5ee3fe43..0e2ec50aa 100644 --- a/l1-contracts/test/unit_tests/synclayer.spec.ts +++ b/l1-contracts/test/unit_tests/synclayer.spec.ts @@ -11,12 +11,17 @@ import { defaultDeployerForTests, registerHyperchainWithBridgeRegistration, } from "../../src.ts/deploy-test-process"; -import { ethTestConfig, REQUIRED_L2_GAS_PRICE_PER_PUBDATA, priorityTxMaxGasLimit } from "../../src.ts/utils"; +import { + ethTestConfig, + REQUIRED_L2_GAS_PRICE_PER_PUBDATA, + priorityTxMaxGasLimit, + L2_BRIDGEHUB_ADDRESS, +} from "../../src.ts/utils"; import { SYSTEM_CONFIG } from "../../scripts/utils"; import type { Deployer } from "../../src.ts/deploy"; -describe("Synclayer", function () { +describe("Gateway", function () { let bridgehub: Bridgehub; // let stateTransition: StateTransitionManager; let owner: ethers.Signer; @@ -76,7 +81,7 @@ describe("Synclayer", function () { it("Check start move chain to synclayer", async () => { const gasPrice = await owner.provider.getGasPrice(); - await migratingDeployer.moveChainToGateway(gatewayDeployer.chainId.toString(), gasPrice, false); + await migratingDeployer.moveChainToGateway(gatewayDeployer.chainId.toString(), gasPrice); expect(await bridgehub.settlementLayer(migratingDeployer.chainId)).to.equal(gatewayDeployer.chainId); }); @@ -86,18 +91,30 @@ describe("Synclayer", function () { const value = ( await bridgehub.l2TransactionBaseCost(chainId, gasPrice, priorityTxMaxGasLimit, REQUIRED_L2_GAS_PRICE_PER_PUBDATA) ).mul(10); - // const baseTokenAddress = await bridgehub.baseToken(chainId); - // const ethIsBaseToken = baseTokenAddress == ADDRESS_ONE; + const stmDeploymentTracker = migratingDeployer.stmDeploymentTracker(migratingDeployer.deployWallet); - const calldata = stmDeploymentTracker.interface.encodeFunctionData("registerSTMAssetOnL2SharedBridge", [ - chainId, - gatewayDeployer.addresses.StateTransition.StateTransitionProxy, + const assetRouter = migratingDeployer.defaultSharedBridge(migratingDeployer.deployWallet); + const assetId = await bridgehub.stmAssetIdFromChainId(chainId); + + await migratingDeployer.executeUpgrade( + bridgehub.address, value, - priorityTxMaxGasLimit, - SYSTEM_CONFIG.requiredL2GasPricePerPubdata, - gatewayDeployer.deployWallet.address, - ]); - await migratingDeployer.executeUpgrade(stmDeploymentTracker.address, value, calldata); + bridgehub.interface.encodeFunctionData("requestL2TransactionTwoBridges", [ + { + chainId, + mintValue: value, + l2Value: 0, + l2GasLimit: priorityTxMaxGasLimit, + l2GasPerPubdataByteLimit: SYSTEM_CONFIG.requiredL2GasPricePerPubdata, + refundRecipient: migratingDeployer.deployWallet.address, + secondBridgeAddress: assetRouter.address, + secondBridgeValue: 0, + secondBridgeCalldata: + "0x02" + + ethers.utils.defaultAbiCoder.encode(["bytes32", "address"], [assetId, L2_BRIDGEHUB_ADDRESS]).slice(2), + }, + ]) + ); await migratingDeployer.executeUpgrade( bridgehub.address, value, diff --git a/l2-contracts/contracts/bridge/L2AssetRouter.sol b/l2-contracts/contracts/bridge/L2AssetRouter.sol index f7f7f8cc3..796ed1464 100644 --- a/l2-contracts/contracts/bridge/L2AssetRouter.sol +++ b/l2-contracts/contracts/bridge/L2AssetRouter.sol @@ -155,6 +155,12 @@ contract L2AssetRouter is IL2AssetRouter, Initializable { finalizeDeposit(assetId, data); } + /// @notice Legacy withdraw. + /// @dev Finalizes the deposit and mint funds. + /// @param _l1Receiver The address of token receiver on L1. + /// @param _l2Token The address of token on L2. + /// @param _amount The amount of the token transferred. + /// @param _sender The original msg.sender. function withdrawLegacyBridge( address _l1Receiver, address _l2Token,