From 2511c27d4bed29d8cb7c40867261582493f34783 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Tue, 8 Oct 2024 19:51:43 +0400 Subject: [PATCH 01/60] (test) --- l1-contracts/.env | 1 + .../DeployZKAndBridgeToL1.s.sol | 157 ++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol diff --git a/l1-contracts/.env b/l1-contracts/.env index 0cbe2dbd1..1baa451ef 100644 --- a/l1-contracts/.env +++ b/l1-contracts/.env @@ -41,6 +41,7 @@ CONTRACTS_MAX_NUMBER_OF_ZK_CHAINS=100 L1_CONFIG=/script-config/config-deploy-l1.toml L1_OUTPUT=/script-out/output-deploy-l1.toml TOKENS_CONFIG=/script-config/config-deploy-erc20.toml +ZK_TOKEN_CONFIG=/script-config/config-deploy-zk.toml ZK_CHAIN_CONFIG=/script-config/register-zk-chain.toml ZK_CHAIN_OUTPUT=/script-out/output-deploy-zk-chain-era.toml FORCE_DEPLOYMENTS_CONFIG=/script-config/generate-force-deployments-data.toml diff --git a/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol b/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol new file mode 100644 index 000000000..277cf3c0c --- /dev/null +++ b/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +// solhint-disable no-console + +import {Script, console2 as console} from "forge-std/Script.sol"; +import {stdToml} from "forge-std/StdToml.sol"; + +// It's required to disable lints to force the compiler to compile the contracts +// solhint-disable no-unused-import +import {TestnetERC20Token} from "contracts/dev-contracts/TestnetERC20Token.sol"; +// solhint-disable no-unused-import +import {WETH9} from "contracts/dev-contracts/WETH9.sol"; + +import {Utils} from "./Utils.sol"; +import {MintFailed} from "./ZkSyncScriptErrors.sol"; + +contract DeployZKScript is Script { + using stdToml for string; + + struct Config { + TokenDescription zkToken; + address deployerAddress; + address[] additionalAddressesForMinting; + address create2FactoryAddr; + bytes32 create2FactorySalt; + uint256 chainId; + address l1SharedBridge; + address bridgehub; + } + + struct TokenDescription { + address addr; + string name; + string symbol; + uint256 decimals; + string implementation; + uint256 mint; + } + + Config internal config; + + function run() public { + console.log("Deploying ZK Token"); + + initializeConfig(); + deployZkToken(); + saveOutput(); + } + + function getTokenAddress() public view returns (address) { + return config.zkToken.addr; + } + + function initializeConfig() internal { + config.deployerAddress = msg.sender; + + string memory root = vm.projectRoot(); + + // Grab config from output of l1 deployment + string memory path = string.concat(root, vm.envString("L1_OUTPUT")); + string memory toml = vm.readFile(path); + + // Config file must be parsed key by key, otherwise values returned + // are parsed alfabetically and not by key. + // https://book.getfoundry.sh/cheatcodes/parse-toml + config.create2FactoryAddr = vm.parseTomlAddress(toml, "$.create2_factory_addr"); + config.create2FactorySalt = vm.parseTomlBytes32(toml, "$.create2_factory_salt"); + + // Grab config from custom config file + path = string.concat(root, vm.envString("ZK_TOKEN_CONFIG")); + toml = vm.readFile(path); + config.additionalAddressesForMinting = vm.parseTomlAddressArray(toml, "$.additional_addresses_for_minting"); + + // Parse the ZK token configuration + string memory key = "$.tokens.ZK"; + config.zkToken.name = toml.readString(string.concat(key, ".name")); + config.zkToken.symbol = toml.readString(string.concat(key, ".symbol")); + config.zkToken.decimals = toml.readUint(string.concat(key, ".decimals")); + config.zkToken.implementation = toml.readString(string.concat(key, ".implementation")); + config.zkToken.mint = toml.readUint(string.concat(key, ".mint")); + } + + function deployZkToken() internal { + TokenDescription storage token = config.zkToken; + console.log("Deploying token:", token.name); + address tokenAddress = deployErc20({ + name: token.name, + symbol: token.symbol, + decimals: token.decimals, + implementation: token.implementation, + mint: token.mint, + additionalAddressesForMinting: config.additionalAddressesForMinting + }); + console.log("Token deployed at:", tokenAddress); + token.addr = tokenAddress; + } + + function deployErc20( + string memory name, + string memory symbol, + uint256 decimals, + string memory implementation, + uint256 mint, + address[] storage additionalAddressesForMinting + ) internal returns (address) { + // bytes memory args; + // // WETH9 constructor has no arguments + // if (keccak256(bytes(implementation)) != keccak256(bytes("WETH9.sol"))) { + // args = abi.encode(name, symbol, decimals); + // } + + // bytes memory bytecode = abi.encodePacked(vm.getCode(implementation), args); + + vm.broadcast(); + TestnetERC20Token token = new TestnetERC20Token(name, symbol, uint8(decimals)); + address tokenAddress = address(token); + + // if (mint > 0) { + // vm.broadcast(); + // additionalAddressesForMinting.push(config.deployerAddress); + // uint256 addressMintListLength = additionalAddressesForMinting.length; + // for (uint256 i = 0; i < addressMintListLength; ++i) { + // (bool success, ) = tokenAddress.call( + // abi.encodeWithSignature("mint(address,uint256)", additionalAddressesForMinting[i], mint) + // ); + // if (!success) { + // revert MintFailed(); + // } + // console.log("Minting to:", additionalAddressesForMinting[i]); + // if (!success) { + // revert MintFailed(); + // } + // } + // } + + return tokenAddress; + } + + function saveOutput() internal { + TokenDescription memory token = config.zkToken; + vm.serializeString(token.symbol, "name", token.name); + vm.serializeString(token.symbol, "symbol", token.symbol); + vm.serializeUint(token.symbol, "decimals", token.decimals); + vm.serializeString(token.symbol, "implementation", token.implementation); + vm.serializeUintToHex(token.symbol, "mint", token.mint); + string memory tokenInfo = vm.serializeAddress(token.symbol, "address", token.addr); + + string memory toml = vm.serializeString("root", "tokens", tokenInfo); + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/script-out/output-deploy-erc20.toml"); + vm.writeToml(toml, path); + } + + // add this to be excluded from coverage report + function test() internal {} +} From 5410fde70c54082be5c4d2a9a0ca5f5fe0e151d4 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Thu, 10 Oct 2024 17:49:33 +0200 Subject: [PATCH 02/60] gas optimization --- .../contracts/governance/L2AdminFactory.sol | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/l1-contracts/contracts/governance/L2AdminFactory.sol b/l1-contracts/contracts/governance/L2AdminFactory.sol index d4fe4637c..ade606f2b 100644 --- a/l1-contracts/contracts/governance/L2AdminFactory.sol +++ b/l1-contracts/contracts/governance/L2AdminFactory.sol @@ -27,14 +27,18 @@ contract L2AdminFactory { /// @notice Deploys a new L2 admin contract. /// @return admin The address of the deployed admin contract. function deployAdmin(address[] calldata _additionalRestrictions, bytes32 _salt) external returns (address admin) { - address[] memory restrictions = new address[](requiredRestrictions.length + _additionalRestrictions.length); uint256 cachedRequired = requiredRestrictions.length; - for (uint256 i = 0; i < cachedRequired; ++i) { - restrictions[i] = requiredRestrictions[i]; - } uint256 cachedAdditional = _additionalRestrictions.length; - for (uint256 i = 0; i < cachedAdditional; ++i) { - restrictions[requiredRestrictions.length + i] = _additionalRestrictions[i]; + + address[] memory restrictions = new address[](cachedRequired + cachedRequired); + + unchecked { + for (uint256 i = 0; i < cachedRequired; ++i) { + restrictions[i] = requiredRestrictions[i]; + } + for (uint256 i = 0; i < cachedAdditional; ++i) { + restrictions[cachedRequired + i] = _additionalRestrictions[i]; + } } admin = address(new ChainAdmin{salt: _salt}(restrictions)); From cacd00f751d5f49d6091f71dc1cd2ce3492577ca Mon Sep 17 00:00:00 2001 From: Stanislav Bezkorovainyi Date: Mon, 14 Oct 2024 13:52:07 +0200 Subject: [PATCH 03/60] Update l1-contracts/contracts/governance/PermanentRestriction.sol Co-authored-by: Vlad Bochok <41153528+vladbochok@users.noreply.github.com> --- l1-contracts/contracts/governance/PermanentRestriction.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index b1668f731..92fa05182 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -58,7 +58,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St L2_ADMIN_FACTORY = _l2AdminFactory; } - /// @notice The intialization function for the proxy contract. + /// @notice The initialization function for the proxy contract. /// @param _initialOwner The initial owner of the permanent restriction. /// @dev Expected to be delegatecalled by the `TransparentUpgradableProxy` /// upon initialization. From dfcb9cdb508a20779c0ef49c4527554307c19e14 Mon Sep 17 00:00:00 2001 From: vladbochok Date: Mon, 14 Oct 2024 18:44:10 +0400 Subject: [PATCH 04/60] Add ChainAdmin contract --- l1-contracts/contracts/governance/ChainAdmin.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/l1-contracts/contracts/governance/ChainAdmin.sol b/l1-contracts/contracts/governance/ChainAdmin.sol index f6a93146f..6fbc51e01 100644 --- a/l1-contracts/contracts/governance/ChainAdmin.sol +++ b/l1-contracts/contracts/governance/ChainAdmin.sol @@ -20,6 +20,13 @@ import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; contract ChainAdmin is IChainAdmin, ReentrancyGuard { using EnumerableSet for EnumerableSet.AddressSet; + /// @notice Mapping of protocol versions to their expected upgrade timestamps. + /// @dev Needed for the offchain node administration to know when to start building batches with the new protocol version. + mapping(uint256 protocolVersion => uint256 upgradeTimestamp) public protocolVersionToUpgradeTimestamp; + + /// @notice The set of active restrictions. + EnumerableSet.AddressSet internal activeRestrictions; + /// @notice Ensures that only the `ChainAdmin` contract itself can call the function. /// @dev All functions that require access-control should use `onlySelf` modifier, while the access control logic /// should be implemented in the restriction contracts. @@ -38,13 +45,6 @@ contract ChainAdmin is IChainAdmin, ReentrancyGuard { } } - /// @notice Mapping of protocol versions to their expected upgrade timestamps. - /// @dev Needed for the offchain node administration to know when to start building batches with the new protocol version. - mapping(uint256 protocolVersion => uint256 upgradeTimestamp) public protocolVersionToUpgradeTimestamp; - - /// @notice The set of active restrictions. - EnumerableSet.AddressSet internal activeRestrictions; - /// @notice Returns the list of active restrictions. function getRestrictions() public view returns (address[] memory) { return activeRestrictions.values(); From c832b06c4ba6ceac92be83cf95f63e94dc6081b3 Mon Sep 17 00:00:00 2001 From: vladbochok Date: Mon, 14 Oct 2024 18:47:07 +0400 Subject: [PATCH 05/60] Fix N-03 --- l1-contracts/contracts/governance/ChainAdmin.sol | 4 ++-- .../contracts/governance/PermanentRestriction.sol | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/l1-contracts/contracts/governance/ChainAdmin.sol b/l1-contracts/contracts/governance/ChainAdmin.sol index f6a93146f..87ddbd5bc 100644 --- a/l1-contracts/contracts/governance/ChainAdmin.sol +++ b/l1-contracts/contracts/governance/ChainAdmin.sol @@ -107,7 +107,7 @@ contract ChainAdmin is IChainAdmin, ReentrancyGuard { /// @notice Function that returns the current admin can perform the call. /// @dev By default it always returns true, but can be overridden in derived contracts. - function _validateCall(Call calldata _call) internal view { + function _validateCall(Call calldata _call) private view { address[] memory restrictions = getRestrictions(); unchecked { @@ -119,7 +119,7 @@ contract ChainAdmin is IChainAdmin, ReentrancyGuard { /// @notice Adds a new restriction to the active restrictions set. /// @param _restriction The address of the restriction contract to be added. - function _addRestriction(address _restriction) internal { + function _addRestriction(address _restriction) private { if (!activeRestrictions.add(_restriction)) { revert RestrictionWasAlreadyPresent(_restriction); } diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 153ce369e..ac5a6fd9c 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -129,7 +129,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @param _call The call data. /// @dev Note that we do not need to validate the migration to the L1 layer as the admin /// is not changed in this case. - function _validateMigrationToL2(Call calldata _call) internal view { + function _validateMigrationToL2(Call calldata _call) private view { _ensureEnoughGas(); try this.tryGetNewAdminFromMigration(_call) returns (address admin) { if (!allowedL2Admins[admin]) { @@ -142,7 +142,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice Validates the call as the chain admin /// @param _call The call data. - function _validateAsChainAdmin(Call calldata _call) internal view { + function _validateAsChainAdmin(Call calldata _call) private view { if (!_isAdminOfAChain(_call.target)) { // We only validate calls related to being an admin of a chain return; @@ -174,7 +174,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice Validates the correctness of the new admin. /// @param _call The call data. /// @dev Ensures that the admin has a whitelisted implementation and does not remove this restriction. - function _validateNewAdmin(Call calldata _call) internal view { + function _validateNewAdmin(Call calldata _call) private view { address newChainAdmin = abi.decode(_call.data[4:], (address)); bytes32 implementationCodeHash = newChainAdmin.codehash; @@ -193,7 +193,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice Validates the removal of the restriction. /// @param _call The call data. /// @dev Ensures that this restriction is not removed. - function _validateRemoveRestriction(Call calldata _call) internal view { + function _validateRemoveRestriction(Call calldata _call) private view { if (_call.target != msg.sender) { return; } @@ -211,7 +211,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice Checks if the `msg.sender` is an admin of a certain ZkSyncHyperchain. /// @param _chain The address of the chain. - function _isAdminOfAChain(address _chain) internal view returns (bool) { + function _isAdminOfAChain(address _chain) private view returns (bool) { _ensureEnoughGas(); (bool success, ) = address(this).staticcall(abi.encodeCall(this.tryCompareAdminOfAChain, (_chain, msg.sender))); return success; @@ -303,7 +303,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St return l2Admin; } - function _ensureEnoughGas() internal view { + function _ensureEnoughGas() private view { if (gasleft() < MIN_GAS_FOR_FALLABLE_CALL) { revert NotEnoughGas(); } From f954a58ea6e5ae15244c094142f11cd21e42fd60 Mon Sep 17 00:00:00 2001 From: vladbochok Date: Mon, 14 Oct 2024 18:57:44 +0400 Subject: [PATCH 06/60] Emit event --- l1-contracts/contracts/governance/L2AdminFactory.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/l1-contracts/contracts/governance/L2AdminFactory.sol b/l1-contracts/contracts/governance/L2AdminFactory.sol index d4fe4637c..ddcf978d4 100644 --- a/l1-contracts/contracts/governance/L2AdminFactory.sol +++ b/l1-contracts/contracts/governance/L2AdminFactory.sol @@ -38,5 +38,7 @@ contract L2AdminFactory { } admin = address(new ChainAdmin{salt: _salt}(restrictions)); + + emit AdminDeployed(admin); } } From 3a4f4266de4f4be957d91746aeaad9fa93a62f84 Mon Sep 17 00:00:00 2001 From: vladbochok Date: Mon, 14 Oct 2024 19:01:56 +0400 Subject: [PATCH 07/60] Fix N-05 --- .../contracts/governance/AccessControlRestriction.sol | 6 +++--- l1-contracts/contracts/governance/PermanentRestriction.sol | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/l1-contracts/contracts/governance/AccessControlRestriction.sol b/l1-contracts/contracts/governance/AccessControlRestriction.sol index 3fc67f875..5c5729a37 100644 --- a/l1-contracts/contracts/governance/AccessControlRestriction.sol +++ b/l1-contracts/contracts/governance/AccessControlRestriction.sol @@ -11,14 +11,14 @@ import {Call} from "./Common.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @notice The Restriction that is designed to provide the access control logic for the `ChainAdmin` contract. -/// @dev It inherits from `AccessControlDefaultAdminRules` without overriding `_setRoleAdmin` functionaity. In other +/// @dev It inherits from `AccessControlDefaultAdminRules` without overriding `_setRoleAdmin` functionality. In other /// words, the `DEFAULT_ADMIN_ROLE` is the only role that can manage roles. This is done for simplicity. /// @dev An instance of this restriction should be deployed separately for each `ChainAdmin` contract. /// @dev IMPORTANT: this function does not validate the ability of the invoker to use `msg.value`. Thus, /// either all callers with access to functions should be trusted to not steal ETH from the `ChainAdmin` account -/// or not ETH should be passively stored in `ChainAdmin` account. +/// or no ETH should be passively stored in `ChainAdmin` account. contract AccessControlRestriction is IRestriction, IAccessControlRestriction, AccessControlDefaultAdminRules { - /// @notice Required roles to call a specific functions. + /// @notice Required roles to call a specific function. /// @dev Note, that the role 0 means the `DEFAULT_ADMIN_ROLE` from the `AccessControlDefaultAdminRules` contract. mapping(address target => mapping(bytes4 selector => bytes32 requiredRole)) public requiredRoles; diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 153ce369e..ef055886a 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -30,7 +30,7 @@ uint256 constant MIN_GAS_FOR_FALLABLE_CALL = 5_000_000; /// @notice This contract should be used by chains that wish to guarantee that certain security /// properties are preserved forever. /// @dev To be deployed as a transparent upgradable proxy, owned by a trusted decentralized governance. -/// @dev Once of the instances of such contract is to ensure that a ZkSyncHyperchain is a rollup forever. +/// @dev One of the instances of such contract is enough to ensure that a ZkSyncHyperchain is a rollup forever. contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2StepUpgradeable { /// @notice The address of the Bridgehub contract. IBridgehub public immutable BRIDGE_HUB; From 7ce1e00bdf1c5aed385cc95f2a5b4a930e684c82 Mon Sep 17 00:00:00 2001 From: vladbochok Date: Mon, 14 Oct 2024 19:07:11 +0400 Subject: [PATCH 08/60] Fix N-06 --- .../contracts/governance/PermanentRestriction.sol | 8 ++++---- .../unit/concrete/Governance/PermanentRestriction.t.sol | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 153ce369e..f5b184df1 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -48,7 +48,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St mapping(bytes allowedCalldata => bool isAllowed) public allowedCalls; /// @notice The mapping of the validated selectors. - mapping(bytes4 selector => bool isValidated) public validatedSelectors; + mapping(bytes4 selector => bool isValidated) public selectorsToValidate; /// @notice The mapping of whitelisted L2 admins. mapping(address adminAddress => bool isWhitelisted) public allowedL2Admins; @@ -87,8 +87,8 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice Allows a certain selector to be validated. /// @param _selector The selector of the function. /// @param _isValidated The flag that indicates if the selector is validated. - function setSelectorIsValidated(bytes4 _selector, bool _isValidated) external onlyOwner { - validatedSelectors[_selector] = _isValidated; + function setSelectorShouldBeValidated(bytes4 _selector, bool _isValidated) external onlyOwner { + selectorsToValidate[_selector] = _isValidated; emit SelectorValidationChanged(_selector, _isValidated); } @@ -161,7 +161,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St return; } - if (!validatedSelectors[selector]) { + if (!selectorsToValidate[selector]) { // The selector is not validated, any data is allowed. return; } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index bcfe6ae2c..92e58c888 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -92,12 +92,12 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { permRestriction.setAllowedData(data, true); } - function test_setSelectorIsValidated(bytes4 selector) public { + function test_setSelectorShouldBeValidated(bytes4 selector) public { vm.expectEmit(true, false, false, true); emit IPermanentRestriction.SelectorValidationChanged(selector, true); vm.prank(owner); - permRestriction.setSelectorIsValidated(selector, true); + permRestriction.setSelectorShouldBeValidated(selector, true); } function test_tryCompareAdminOfAChainIsAddressZero() public { @@ -190,7 +190,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_validateCallCallNotAllowed() public { vm.prank(owner); - permRestriction.setSelectorIsValidated(IAdmin.acceptAdmin.selector, true); + permRestriction.setSelectorShouldBeValidated(IAdmin.acceptAdmin.selector, true); Call memory call = Call({ target: hyperchain, value: 0, @@ -206,7 +206,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_validateCall() public { vm.prank(owner); - permRestriction.setSelectorIsValidated(IAdmin.acceptAdmin.selector, true); + permRestriction.setSelectorShouldBeValidated(IAdmin.acceptAdmin.selector, true); Call memory call = Call({ target: hyperchain, value: 0, From 04268f34b6941b06947b00e8e5d93a09d350c333 Mon Sep 17 00:00:00 2001 From: vladbochok Date: Mon, 14 Oct 2024 19:10:31 +0400 Subject: [PATCH 09/60] Fix N-07 --- l1-contracts/contracts/governance/PermanentRestriction.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 153ce369e..9fb8f6722 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -54,6 +54,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St mapping(address adminAddress => bool isWhitelisted) public allowedL2Admins; constructor(IBridgehub _bridgehub, address _l2AdminFactory) { + _disableInitializers(); BRIDGE_HUB = _bridgehub; L2_ADMIN_FACTORY = _l2AdminFactory; } From c9f2e47608c281f98090438529bf42a29473777b Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 15 Oct 2024 00:25:10 +0100 Subject: [PATCH 10/60] fix: kl/l01-2gw renaming --- l1-contracts/contracts/bridge/L1Nullifier.sol | 8 ++++---- .../bridge/asset-router/L2AssetRouter.sol | 6 +++--- .../bridge/ntv/L1NativeTokenVault.sol | 4 ++-- .../bridge/ntv/L2NativeTokenVault.sol | 10 +++++----- .../contracts/bridge/ntv/NativeTokenVault.sol | 8 ++++---- .../contracts/bridgehub/Bridgehub.sol | 7 ++++--- .../common/libraries/DataEncoding.sol | 20 +++++++++---------- .../l1/integration/AssetRouterTest.t.sol | 4 ++-- 8 files changed, 34 insertions(+), 33 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 789f122df..81895cdba 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -568,8 +568,8 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, address baseToken = BRIDGE_HUB.baseToken(_chainId); transferData = DataEncoding.encodeBridgeMintData({ _originalCaller: address(0), - _l2Receiver: l1Receiver, - _l1Token: baseToken, + _remoteReceiver: l1Receiver, + _originToken: baseToken, _amount: amount, _erc20Metadata: new bytes(0) }); @@ -592,8 +592,8 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, assetId = DataEncoding.encodeNTVAssetId(block.chainid, l1Token); transferData = DataEncoding.encodeBridgeMintData({ _originalCaller: address(0), - _l2Receiver: l1Receiver, - _l1Token: l1Token, + _remoteReceiver: l1Receiver, + _originToken: l1Token, _amount: amount, _erc20Metadata: new bytes(0) }); diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 6c122ccc6..fbee665f5 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -89,10 +89,10 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { function setAssetHandlerAddress( uint256 _originChainId, bytes32 _assetId, - address _assetAddress + address _assetHandlerAddress ) external override onlyAssetRouterCounterpart(_originChainId) { - assetHandlerAddress[_assetId] = _assetAddress; - emit AssetHandlerRegistered(_assetId, _assetAddress); + assetHandlerAddress[_assetId] = _assetHandlerAddress; + emit AssetHandlerRegistered(_assetId, _assetHandlerAddress); } /// @inheritdoc IAssetRouterBase diff --git a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol index be456db43..149e8614b 100644 --- a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol @@ -191,9 +191,9 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken // get the computed address before the contract DeployWithCreate2 deployed using Bytecode of contract DeployWithCreate2 and salt specified by the sender function calculateCreate2TokenAddress( uint256 _originChainId, - address _l1Token + address _nonNativeToken ) public view override(INativeTokenVault, NativeTokenVault) returns (address) { - bytes32 salt = _getCreate2Salt(_originChainId, _l1Token); + bytes32 salt = _getCreate2Salt(_originChainId, _nonNativeToken); return Create2.computeAddress( salt, diff --git a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol index e96a6d289..daad4b803 100644 --- a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol @@ -170,16 +170,16 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { //////////////////////////////////////////////////////////////*/ /// @notice Calculates L2 wrapped token address given the currently stored beacon proxy bytecode hash and beacon address. - /// @param _l1Token The address of token on L1. + /// @param _nonNativeToken The address of token on its origin chain.. /// @return Address of an L2 token counterpart. function calculateCreate2TokenAddress( uint256 _originChainId, - address _l1Token - ) public view override(INativeTokenVault, NativeTokenVault) returns (address) { + address _nonNativeToken + ) public view virtual override(INativeTokenVault, NativeTokenVault) returns (address) { bytes32 constructorInputHash = keccak256(abi.encode(address(bridgedTokenBeacon), "")); - bytes32 salt = _getCreate2Salt(_originChainId, _l1Token); + bytes32 salt = _getCreate2Salt(_originChainId, _nonNativeToken); if (address(L2_LEGACY_SHARED_BRIDGE) != address(0)) { - return L2_LEGACY_SHARED_BRIDGE.l2TokenAddress(_l1Token); + return L2_LEGACY_SHARED_BRIDGE.l2TokenAddress(_nonNativeToken); } else { return L2ContractHelper.computeCreate2Address( diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 5e76d7630..f5c8bbb12 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -224,8 +224,8 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 _bridgeMintData = DataEncoding.encodeBridgeMintData({ _originalCaller: _originalCaller, - _l2Receiver: _receiver, - _l1Token: originToken, + _remoteReceiver: _receiver, + _originToken: originToken, _amount: _amount, _erc20Metadata: erc20Metadata }); @@ -280,8 +280,8 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 } _bridgeMintData = DataEncoding.encodeBridgeMintData({ _originalCaller: _originalCaller, - _l2Receiver: _receiver, - _l1Token: nativeToken, + _remoteReceiver: _receiver, + _originToken: nativeToken, _amount: amount, _erc20Metadata: erc20Metadata }); diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 8e23a9f3d..204844e57 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -318,9 +318,10 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus revert CTMNotRegistered(); } - bytes32 assetInfo = keccak256(abi.encode(L1_CHAIN_ID, sender, _additionalData)); - ctmAssetIdToAddress[assetInfo] = _assetAddress; - emit AssetRegistered(assetInfo, _assetAddress, _additionalData, msg.sender); + bytes32 ctmAssetId = keccak256(abi.encode(L1_CHAIN_ID, sender, _additionalData)); + ctmAssetIdToAddress[ctmAssetId] = _assetAddress; + ctmAssetIdFromAddress[_assetAddress] = ctmAssetId; + emit AssetRegistered(ctmAssetId, _assetAddress, _additionalData, msg.sender); } /*////////////////////////////////////////////////////////////// diff --git a/l1-contracts/contracts/common/libraries/DataEncoding.sol b/l1-contracts/contracts/common/libraries/DataEncoding.sol index 9df83d67a..3536d1d14 100644 --- a/l1-contracts/contracts/common/libraries/DataEncoding.sol +++ b/l1-contracts/contracts/common/libraries/DataEncoding.sol @@ -15,27 +15,27 @@ import {UnsupportedEncodingVersion} from "../L1ContractErrors.sol"; library DataEncoding { /// @notice Abi.encodes the data required for bridgeMint on remote chain. /// @param _originalCaller The address which initiated the transfer. - /// @param _l2Receiver The address which to receive tokens on remote chain. - /// @param _l1Token The transferred token address. + /// @param _remoteReceiver The address which to receive tokens on remote chain. + /// @param _originToken The transferred token address. /// @param _amount The amount of token to be transferred. /// @param _erc20Metadata The transferred token metadata. /// @return The encoded bridgeMint data function encodeBridgeMintData( address _originalCaller, - address _l2Receiver, - address _l1Token, + address _remoteReceiver, + address _originToken, uint256 _amount, bytes memory _erc20Metadata ) internal pure returns (bytes memory) { // solhint-disable-next-line func-named-parameters - return abi.encode(_originalCaller, _l2Receiver, _l1Token, _amount, _erc20Metadata); + return abi.encode(_originalCaller, _remoteReceiver, _originToken, _amount, _erc20Metadata); } /// @notice Function decoding transfer data previously encoded with this library. /// @param _bridgeMintData The encoded bridgeMint data /// @return _originalCaller The address which initiated the transfer. - /// @return _l2Receiver The address which to receive tokens on remote chain. - /// @return _parsedL1Token The transferred token address. + /// @return _remoteReceiver The address which to receive tokens on remote chain. + /// @return _parsedOriginToken The transferred token address. /// @return _amount The amount of token to be transferred. /// @return _erc20Metadata The transferred token metadata. function decodeBridgeMintData( @@ -45,13 +45,13 @@ library DataEncoding { pure returns ( address _originalCaller, - address _l2Receiver, - address _parsedL1Token, + address _remoteReceiver, + address _parsedOriginToken, uint256 _amount, bytes memory _erc20Metadata ) { - (_originalCaller, _l2Receiver, _parsedL1Token, _amount, _erc20Metadata) = abi.decode( + (_originalCaller, _remoteReceiver, _parsedOriginToken, _amount, _erc20Metadata) = abi.decode( _bridgeMintData, (address, address, address, uint256, bytes) ); diff --git a/l1-contracts/test/foundry/l1/integration/AssetRouterTest.t.sol b/l1-contracts/test/foundry/l1/integration/AssetRouterTest.t.sol index 83913d2a4..e06b11188 100644 --- a/l1-contracts/test/foundry/l1/integration/AssetRouterTest.t.sol +++ b/l1-contracts/test/foundry/l1/integration/AssetRouterTest.t.sol @@ -87,8 +87,8 @@ contract AssetRouterTest is L1ContractDeployer, ZKChainDeployer, TokenDeployer, l2TokenAssetId = DataEncoding.encodeNTVAssetId(chainId, address(1)); bytes memory transferData = DataEncoding.encodeBridgeMintData({ _originalCaller: ETH_TOKEN_ADDRESS, - _l2Receiver: address(this), - _l1Token: ETH_TOKEN_ADDRESS, + _remoteReceiver: address(this), + _originToken: ETH_TOKEN_ADDRESS, _amount: 100, _erc20Metadata: BridgeHelper.getERC20Getters(_tokenAddress, chainId) }); From e1da926d0c437b0e5bc7ef6c450ac3de42f4be71 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 15 Oct 2024 00:28:13 +0100 Subject: [PATCH 11/60] fix: kl/l02-2gw redundunt code --- l1-contracts/contracts/bridge/L1Nullifier.sol | 16 ---------------- .../bridge/asset-router/IAssetRouterBase.sol | 7 ------- .../bridge/asset-router/L1AssetRouter.sol | 4 +--- .../bridge/interfaces/IAssetHandler.sol | 3 --- .../contracts/bridge/ntv/L1NativeTokenVault.sol | 6 ------ l1-contracts/contracts/bridgehub/Bridgehub.sol | 7 ------- .../state-transition/IChainTypeManager.sol | 2 -- .../state-transition/ValidatorTimelock.sol | 6 +----- .../chain-deps/facets/ZKChainBase.sol | 14 -------------- .../state-transition/chain-interfaces/IAdmin.sol | 2 -- l1-contracts/deploy-scripts/DeployL1.s.sol | 1 - l1-contracts/src.ts/deploy.ts | 6 ++---- .../concrete/Bridgehub/experimental_bridge.t.sol | 2 +- .../L1Erc20Bridge/_L1Erc20Bridge_Shared.t.sol | 7 +------ .../L1SharedBridge/_L1SharedBridge_Shared.t.sol | 1 - .../ValidatorTimelock/ValidatorTimelock.t.sol | 2 +- .../chain-deps/facets/Base/_Base_Shared.t.sol | 3 --- 17 files changed, 7 insertions(+), 82 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 789f122df..d6e507c9b 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -122,14 +122,6 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, _; } - /// @notice Checks that the message sender is the bridgehub or ZKsync Era Diamond Proxy. - modifier onlyBridgehubOrEra(uint256 _chainId) { - if (msg.sender != address(BRIDGE_HUB) && (_chainId != ERA_CHAIN_ID || msg.sender != ERA_DIAMOND_PROXY)) { - revert Unauthorized(msg.sender); - } - _; - } - /// @notice Checks that the message sender is the legacy bridge. modifier onlyLegacyBridge() { if (msg.sender != address(legacyBridge)) { @@ -138,14 +130,6 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, _; } - /// @notice Checks that the message sender is the legacy bridge. - modifier onlyAssetRouterOrErc20Bridge() { - if (msg.sender != address(l1AssetRouter) && msg.sender != address(legacyBridge)) { - revert Unauthorized(msg.sender); - } - _; - } - /// @dev Contract is expected to be used as proxy implementation. /// @dev Initialize the implementation to prevent Parity hack. constructor(IBridgehub _bridgehub, uint256 _eraChainId, address _eraDiamondProxy) reentrancyGuardInitializer { diff --git a/l1-contracts/contracts/bridge/asset-router/IAssetRouterBase.sol b/l1-contracts/contracts/bridge/asset-router/IAssetRouterBase.sol index a307ba526..2f5ba8954 100644 --- a/l1-contracts/contracts/bridge/asset-router/IAssetRouterBase.sol +++ b/l1-contracts/contracts/bridge/asset-router/IAssetRouterBase.sol @@ -32,13 +32,6 @@ interface IAssetRouterBase { bytes bridgeMintCalldata ); - event BridgehubWithdrawalInitiated( - uint256 chainId, - address indexed sender, - bytes32 indexed assetId, - bytes32 assetDataHash // Todo: What's the point of emitting hash? - ); - event AssetHandlerRegisteredInitial( bytes32 indexed assetId, address indexed assetHandlerAddress, diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index 0c3b2001f..0818e6c05 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -141,9 +141,7 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { bytes32 _assetRegistrationData, address _assetDeploymentTracker ) external onlyOwner { - bytes32 assetId = keccak256( - abi.encode(uint256(block.chainid), _assetDeploymentTracker, _assetRegistrationData) - ); + bytes32 assetId = keccak256(abi.encode(block.chainid, _assetDeploymentTracker, _assetRegistrationData)); assetDeploymentTracker[assetId] = _assetDeploymentTracker; emit AssetDeploymentTrackerSet(assetId, _assetDeploymentTracker, _assetRegistrationData); } diff --git a/l1-contracts/contracts/bridge/interfaces/IAssetHandler.sol b/l1-contracts/contracts/bridge/interfaces/IAssetHandler.sol index 57f58eb59..43f67bb4c 100644 --- a/l1-contracts/contracts/bridge/interfaces/IAssetHandler.sol +++ b/l1-contracts/contracts/bridge/interfaces/IAssetHandler.sol @@ -7,9 +7,6 @@ pragma solidity 0.8.24; /// @custom:security-contact security@matterlabs.dev /// @notice Used for any asset handler and called by the AssetRouter interface IAssetHandler { - /// @dev Emitted when a new token is initialized - event BridgeInitialize(address indexed token, string name, string symbol, uint8 decimals); - /// @dev Emitted when a token is minted event BridgeMint(uint256 indexed chainId, bytes32 indexed assetId, address receiver, uint256 amount); diff --git a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol index be456db43..02247e2dd 100644 --- a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol @@ -34,9 +34,6 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken /// @dev L1 nullifier contract that handles legacy functions & finalize withdrawal, confirm l2 tx mappings IL1Nullifier public immutable override L1_NULLIFIER; - /// @dev Era's chainID - uint256 public immutable ERA_CHAIN_ID; - /// @dev Maps token balances for each chain to prevent unauthorized spending across ZK chains. /// This serves as a security measure until hyperbridging is implemented. /// NOTE: this function may be removed in the future, don't rely on it! @@ -46,12 +43,10 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken /// @dev Initialize the implementation to prevent Parity hack. /// @param _l1WethAddress Address of WETH on deployed chain /// @param _l1AssetRouter Address of Asset Router on L1. - /// @param _eraChainId ID of Era. /// @param _l1Nullifier Address of the nullifier contract, which handles transaction progress between L1 and ZK chains. constructor( address _l1WethAddress, address _l1AssetRouter, - uint256 _eraChainId, IL1Nullifier _l1Nullifier ) NativeTokenVault( @@ -61,7 +56,6 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken block.chainid ) { - ERA_CHAIN_ID = _eraChainId; L1_NULLIFIER = _l1Nullifier; } diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 8e23a9f3d..06cdc1607 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -107,13 +107,6 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus _; } - modifier onlyChainCTM(uint256 _chainId) { - if (msg.sender != chainTypeManager[_chainId]) { - revert Unauthorized(msg.sender); - } - _; - } - modifier onlyL1() { if (L1_CHAIN_ID != block.chainid) { revert Unauthorized(msg.sender); diff --git a/l1-contracts/contracts/state-transition/IChainTypeManager.sol b/l1-contracts/contracts/state-transition/IChainTypeManager.sol index b5202e975..fbbbc955c 100644 --- a/l1-contracts/contracts/state-transition/IChainTypeManager.sol +++ b/l1-contracts/contracts/state-transition/IChainTypeManager.sol @@ -156,8 +156,6 @@ interface IChainTypeManager { function registerSettlementLayer(uint256 _newSettlementLayerChainId, bool _isWhitelisted) external; - event BridgeInitialize(address indexed l1Token, string name, string symbol, uint8 decimals); - function forwardedBridgeBurn( uint256 _chainId, bytes calldata _data diff --git a/l1-contracts/contracts/state-transition/ValidatorTimelock.sol b/l1-contracts/contracts/state-transition/ValidatorTimelock.sol index 64cc0bc20..190ab12b3 100644 --- a/l1-contracts/contracts/state-transition/ValidatorTimelock.sol +++ b/l1-contracts/contracts/state-transition/ValidatorTimelock.sol @@ -52,13 +52,9 @@ contract ValidatorTimelock is IExecutor, Ownable2Step { /// @dev The delay between committing and executing batches. uint32 public executionDelay; - /// @dev Era's chainID - uint256 internal immutable ERA_CHAIN_ID; - - constructor(address _initialOwner, uint32 _executionDelay, uint256 _eraChainId) { + constructor(address _initialOwner, uint32 _executionDelay) { _transferOwnership(_initialOwner); executionDelay = _executionDelay; - ERA_CHAIN_ID = _eraChainId; } /// @notice Checks if the caller is the admin of the chain. diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/ZKChainBase.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/ZKChainBase.sol index 45c360197..e318b44a3 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/ZKChainBase.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/ZKChainBase.sol @@ -55,20 +55,6 @@ contract ZKChainBase is ReentrancyGuard { _; } - modifier onlyValidatorOrChainTypeManager() { - if (!s.validators[msg.sender] && msg.sender != s.chainTypeManager) { - revert Unauthorized(msg.sender); - } - _; - } - - modifier onlyBaseTokenBridge() { - if (msg.sender != s.baseTokenBridge) { - revert Unauthorized(msg.sender); - } - _; - } - function _getTotalPriorityTxs() internal view returns (uint256) { if (s.priorityQueue.getFirstUnprocessedPriorityTx() >= s.priorityTree.startIndex) { return s.priorityTree.getTotalPriorityTxs(); diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol index 4a2ad7170..f46bf196d 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol @@ -128,8 +128,6 @@ interface IAdmin is IZKChainBase { event NewL2DAValidator(address indexed oldL2DAValidator, address indexed newL2DAValidator); event NewL1DAValidator(address indexed oldL1DAValidator, address indexed newL1DAValidator); - event BridgeInitialize(address indexed l1Token, string name, string symbol, uint8 decimals); - event BridgeMint(address indexed _account, uint256 _amount); /// @dev Similar to IL1AssetHandler interface, used to send chains. diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 868fdbc47..f4bd03f1d 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -797,7 +797,6 @@ contract DeployL1Script is Script { abi.encode( config.tokens.tokenWethAddress, addresses.bridges.sharedBridgeProxy, - config.eraChainId, addresses.bridges.l1NullifierProxy ) ); diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index c75a46e1f..39dd0d6b4 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -971,12 +971,11 @@ export class Deployer { create2Salt: string, ethTxOptions: ethers.providers.TransactionRequest ) { - const eraChainId = getNumberFromEnv("CONTRACTS_ERA_CHAIN_ID"); const tokens = getTokens(); const l1WethToken = tokens.find((token: { symbol: string }) => token.symbol == "WETH")!.address; const contractAddress = await this.deployViaCreate2( "L1NativeTokenVault", - [l1WethToken, this.addresses.Bridges.SharedBridgeProxy, eraChainId, this.addresses.Bridges.L1NullifierProxy], + [l1WethToken, this.addresses.Bridges.SharedBridgeProxy, this.addresses.Bridges.L1NullifierProxy], create2Salt, ethTxOptions ); @@ -1565,10 +1564,9 @@ export class Deployer { public async deployValidatorTimelock(create2Salt: string, ethTxOptions: ethers.providers.TransactionRequest) { const executionDelay = getNumberFromEnv("CONTRACTS_VALIDATOR_TIMELOCK_EXECUTION_DELAY"); - const eraChainId = getNumberFromEnv("CONTRACTS_ERA_CHAIN_ID"); const contractAddress = await this.deployViaCreate2( "ValidatorTimelock", - [this.ownerAddress, executionDelay, eraChainId], + [this.ownerAddress, executionDelay], create2Salt, ethTxOptions ); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Bridgehub/experimental_bridge.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Bridgehub/experimental_bridge.t.sol index d9675912a..0a272be89 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Bridgehub/experimental_bridge.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Bridgehub/experimental_bridge.t.sol @@ -189,7 +189,7 @@ contract ExperimentalBridgeTest is Test { } function _deployNTV(address _sharedBridgeAddr) internal returns (L1NativeTokenVault addr) { - L1NativeTokenVault ntvImpl = new L1NativeTokenVault(weth, _sharedBridgeAddr, eraChainId, l1Nullifier); + L1NativeTokenVault ntvImpl = new L1NativeTokenVault(weth, _sharedBridgeAddr, l1Nullifier); TransparentUpgradeableProxy ntvProxy = new TransparentUpgradeableProxy( address(ntvImpl), address(bridgeOwner), diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1Erc20Bridge/_L1Erc20Bridge_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1Erc20Bridge/_L1Erc20Bridge_Shared.t.sol index fb0c30c58..acbc8ccec 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1Erc20Bridge/_L1Erc20Bridge_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1Erc20Bridge/_L1Erc20Bridge_Shared.t.sol @@ -47,12 +47,7 @@ contract L1Erc20BridgeTest is Test { ); address weth = makeAddr("weth"); - L1NativeTokenVault ntv = new L1NativeTokenVault( - weth, - sharedBridgeAddress, - eraChainId, - IL1Nullifier(l1NullifierAddress) - ); + L1NativeTokenVault ntv = new L1NativeTokenVault(weth, sharedBridgeAddress, IL1Nullifier(l1NullifierAddress)); vm.store(address(bridge), bytes32(uint256(212)), bytes32(0)); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol index 52bf0fbb0..9fe401968 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol @@ -153,7 +153,6 @@ contract L1AssetRouterTest is Test { nativeTokenVaultImpl = new L1NativeTokenVault({ _l1WethAddress: l1WethAddress, _l1AssetRouter: address(sharedBridge), - _eraChainId: eraChainId, _l1Nullifier: l1Nullifier }); address tokenBeacon = makeAddr("tokenBeacon"); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/ValidatorTimelock/ValidatorTimelock.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/ValidatorTimelock/ValidatorTimelock.t.sol index 3725f54e2..67c769ca2 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/ValidatorTimelock/ValidatorTimelock.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/ValidatorTimelock/ValidatorTimelock.t.sol @@ -46,7 +46,7 @@ contract ValidatorTimelockTest is Test { executionDelay = 10; chainTypeManager = new DummyChainTypeManagerForValidatorTimelock(owner, zkSync); - validator = new ValidatorTimelock(owner, executionDelay, eraChainId); + validator = new ValidatorTimelock(owner, executionDelay); vm.prank(owner); validator.setChainTypeManager(IChainTypeManager(address(chainTypeManager))); vm.prank(owner); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Base/_Base_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Base/_Base_Shared.t.sol index be93c91df..6894fb1ba 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Base/_Base_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Base/_Base_Shared.t.sol @@ -21,8 +21,6 @@ contract TestBaseFacet is ZKChainBase { function functionWithOnlyAdminOrChainTypeManagerModifier() external onlyAdminOrChainTypeManager {} - function functionWithonlyValidatorOrChainTypeManagerModifier() external onlyValidatorOrChainTypeManager {} - // add this to be excluded from coverage report function test() internal virtual {} } @@ -46,7 +44,6 @@ contract ZKChainBaseTest is Test { selectors[2] = TestBaseFacet.functionWithOnlyChainTypeManagerModifier.selector; selectors[3] = TestBaseFacet.functionWithOnlyBridgehubModifier.selector; selectors[4] = TestBaseFacet.functionWithOnlyAdminOrChainTypeManagerModifier.selector; - selectors[5] = TestBaseFacet.functionWithonlyValidatorOrChainTypeManagerModifier.selector; } function setUp() public virtual { From af8a60eeffd9185c8332c4bcdc92b24e40ffe092 Mon Sep 17 00:00:00 2001 From: vladbochok Date: Tue, 15 Oct 2024 12:14:59 +0400 Subject: [PATCH 12/60] Apply suggestion --- .../contracts/governance/PermanentRestriction.sol | 2 +- .../unit/concrete/Governance/PermanentRestriction.t.sol | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index f5b184df1..6796df354 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -69,7 +69,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice Allows a certain `ChainAdmin` implementation to be used as an admin. /// @param _implementationHash The hash of the implementation code. /// @param _isAllowed The flag that indicates if the implementation is allowed. - function allowAdminImplementation(bytes32 _implementationHash, bool _isAllowed) external onlyOwner { + function setAllowedAdminImplementation(bytes32 _implementationHash, bool _isAllowed) external onlyOwner { allowedAdminImplementations[_implementationHash] = _isAllowed; emit AdminImplementationAllowed(_implementationHash, _isAllowed); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index 92e58c888..103311925 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -76,12 +76,12 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { ); } - function test_allowAdminImplementation(bytes32 implementationHash) public { + function test_setAllowedAdminImplementation(bytes32 implementationHash) public { vm.expectEmit(true, false, false, true); emit IPermanentRestriction.AdminImplementationAllowed(implementationHash, true); vm.prank(owner); - permRestriction.allowAdminImplementation(implementationHash, true); + permRestriction.setAllowedAdminImplementation(implementationHash, true); } function test_setAllowedData(bytes memory data) public { @@ -143,7 +143,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_validateCallSetPendingAdminRemovingPermanentRestriction() public { vm.prank(owner); - permRestriction.allowAdminImplementation(address(chainAdmin).codehash, true); + permRestriction.setAllowedAdminImplementation(address(chainAdmin).codehash, true); Call memory call = Call({ target: hyperchain, @@ -160,7 +160,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_validateCallSetPendingAdmin() public { vm.prank(owner); - permRestriction.allowAdminImplementation(address(chainAdmin).codehash, true); + permRestriction.setAllowedAdminImplementation(address(chainAdmin).codehash, true); vm.prank(address(chainAdmin)); chainAdmin.addRestriction(address(permRestriction)); From 1aed805e35b50bc46d815e74a34ffd811a6b610b Mon Sep 17 00:00:00 2001 From: Stanislav Bezkorovainyi Date: Tue, 15 Oct 2024 10:24:05 +0200 Subject: [PATCH 13/60] Update l1-contracts/contracts/governance/L2AdminFactory.sol Co-authored-by: Vlad Bochok <41153528+vladbochok@users.noreply.github.com> --- l1-contracts/contracts/governance/L2AdminFactory.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/l1-contracts/contracts/governance/L2AdminFactory.sol b/l1-contracts/contracts/governance/L2AdminFactory.sol index d112d0c1b..67ef833b2 100644 --- a/l1-contracts/contracts/governance/L2AdminFactory.sol +++ b/l1-contracts/contracts/governance/L2AdminFactory.sol @@ -28,6 +28,7 @@ contract L2AdminFactory { /// @notice Deploys a new L2 admin contract. /// @return admin The address of the deployed admin contract. + // solhint-disable-next-line gas-calldata-parameters function deployAdmin(address[] memory _additionalRestrictions, bytes32 _salt) external returns (address admin) { // Even though the chain admin will likely perform similar checks, // we keep those here just in case, since it is not expensive, while allowing to fail fast. From b3971a6b0a7bc287d7dc4d2f1c4989bcc3f13b3b Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 15 Oct 2024 10:19:52 +0100 Subject: [PATCH 14/60] fix: kl/l03-2gw setLegacyTokenAddress --- .../contracts/bridge/ntv/L2NativeTokenVault.sol | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol index e96a6d289..1926cf3ab 100644 --- a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol @@ -22,7 +22,7 @@ import {L2ContractHelper, IContractDeployer} from "../../common/libraries/L2Cont import {SystemContractsCaller} from "../../common/libraries/SystemContractsCaller.sol"; import {DataEncoding} from "../../common/libraries/DataEncoding.sol"; -import {EmptyAddress, EmptyBytes32, AddressMismatch, DeployFailed, AssetIdNotSupported} from "../../common/L1ContractErrors.sol"; +import {EmptyAddress, EmptyBytes32, AddressMismatch, DeployFailed, AssetIdNotSupported, ZeroAddress} from "../../common/L1ContractErrors.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev @@ -85,9 +85,12 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { /// @notice Sets the legacy token asset ID for the given L2 token address. function setLegacyTokenAssetId(address _l2TokenAddress) public { address l1TokenAddress = L2_LEGACY_SHARED_BRIDGE.l1TokenAddress(_l2TokenAddress); - bytes32 assetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, l1TokenAddress); - tokenAddress[assetId] = _l2TokenAddress; - originChainId[assetId] = L1_CHAIN_ID; + if (l1TokenAddress == address(0)) { + revert ZeroAddress(); + } + bytes32 newAssetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, l1TokenAddress); + tokenAddress[newAssetId] = _l2TokenAddress; + originChainId[newAssetId] = L1_CHAIN_ID; } /// @notice Ensures that the token is deployed. From 5c58ad9817bd36442a1c1a4c8c788aec15d156b4 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 15 Oct 2024 10:22:02 +0100 Subject: [PATCH 15/60] fix: kl/l04-2gw remove pausable CTM DT --- l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol b/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol index 6ffee2482..3b103b66c 100644 --- a/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol +++ b/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol @@ -5,7 +5,6 @@ pragma solidity 0.8.24; // solhint-disable reason-string, gas-custom-errors import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; -import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/security/PausableUpgradeable.sol"; import {IBridgehub, L2TransactionRequestTwoBridgesInner} from "./IBridgehub.sol"; import {ICTMDeploymentTracker} from "./ICTMDeploymentTracker.sol"; @@ -18,7 +17,7 @@ import {L2_BRIDGEHUB_ADDR} from "../common/L2ContractAddresses.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @dev Contract to be deployed on L1, can link together other contracts based on AssetInfo. -contract CTMDeploymentTracker is ICTMDeploymentTracker, ReentrancyGuard, Ownable2StepUpgradeable, PausableUpgradeable { +contract CTMDeploymentTracker is ICTMDeploymentTracker, ReentrancyGuard, Ownable2StepUpgradeable { /// @dev Bridgehub smart contract that is used to operate with L2 via asynchronous L2 <-> L1 communication. IBridgehub public immutable override BRIDGE_HUB; From 865a625872392a818c546f12bc15ae4cb6db960e Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 14 Oct 2024 19:38:58 +0100 Subject: [PATCH 16/60] kl/l05-gw-audit-2 outdated refs --- l1-contracts/contracts/bridgehub/Bridgehub.sol | 10 +++++----- .../contracts/bridgehub/CTMDeploymentTracker.sol | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 8e23a9f3d..92b31eceb 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -46,7 +46,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// This is the temporary security measure. uint256 public immutable MAX_NUMBER_OF_ZK_CHAINS; - /// @notice all the ether and ERC20 tokens are held by NativeVaultToken managed by this shared Bridge. + /// @notice all the ether and ERC20 tokens are held by NativeVaultToken managed by the asset router. address public assetRouter; /// @notice ChainTypeManagers that are registered, and ZKchains that use these CTMs can use this bridgehub as settlement layer. @@ -243,7 +243,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus //// Registry - /// @notice State Transition can be any contract with the appropriate interface/functionality + /// @notice Chain Type Manager can be any contract with the appropriate interface/functionality /// @param _chainTypeManager the state transition manager address to be added function addChainTypeManager(address _chainTypeManager) external onlyOwner { if (_chainTypeManager == address(0)) { @@ -257,8 +257,8 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus emit ChainTypeManagerAdded(_chainTypeManager); } - /// @notice State Transition can be any contract with the appropriate interface/functionality - /// @notice this stops new Chains from using the STF, old chains are not affected + /// @notice Chain Type Manager can be any contract with the appropriate interface/functionality + /// @notice this stops new Chains from using the CTM, old chains are not affected /// @param _chainTypeManager the state transition manager address to be removed function removeChainTypeManager(address _chainTypeManager) external onlyOwner { if (_chainTypeManager == address(0)) { @@ -716,7 +716,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus } /// @dev IL1AssetHandler interface, used to receive a chain on the settlement layer. - /// @param _assetId the assetId of the chain's STM + /// @param _assetId the assetId of the chain's CTM /// @param _bridgehubMintData the data for the mint function bridgeMint( uint256, // originChainId diff --git a/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol b/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol index 6ffee2482..e31f60c62 100644 --- a/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol +++ b/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol @@ -44,10 +44,10 @@ contract CTMDeploymentTracker is ICTMDeploymentTracker, ReentrancyGuard, Ownable /// @dev Contract is expected to be used as proxy implementation on L1. /// @dev Initialize the implementation to prevent Parity hack. - constructor(IBridgehub _bridgehub, IAssetRouterBase _sharedBridge) reentrancyGuardInitializer { + constructor(IBridgehub _bridgehub, IAssetRouterBase _l1AssetRouter) reentrancyGuardInitializer { _disableInitializers(); BRIDGE_HUB = _bridgehub; - L1_ASSET_ROUTER = _sharedBridge; + L1_ASSET_ROUTER = _l1AssetRouter; } /// @notice used to initialize the contract From 0d3a8391817689e65e3c65aab3941c7f6e8dc829 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 14 Oct 2024 20:07:08 +0100 Subject: [PATCH 17/60] fix: kl/l06-gw-audit-2 misleading docu --- l1-contracts/contracts/bridge/BridgeHelper.sol | 2 +- l1-contracts/contracts/bridge/L1Nullifier.sol | 3 +-- .../bridge/asset-router/L1AssetRouter.sol | 1 - .../bridge/asset-router/L2AssetRouter.sol | 16 +++++++++++++++- .../bridge/interfaces/IAssetHandler.sol | 2 +- .../contracts/bridge/ntv/NativeTokenVault.sol | 6 +++--- l1-contracts/contracts/bridgehub/Bridgehub.sol | 2 +- .../contracts/bridgehub/CTMDeploymentTracker.sol | 2 +- l1-contracts/contracts/bridgehub/MessageRoot.sol | 2 +- .../contracts/common/L2ContractAddresses.sol | 2 +- .../contracts/common/libraries/DataEncoding.sol | 2 +- .../contracts/common/libraries/FullMerkle.sol | 4 +--- .../state-transition/ChainTypeManager.sol | 2 -- .../state-transition/chain-deps/facets/Admin.sol | 1 - 14 files changed, 27 insertions(+), 20 deletions(-) diff --git a/l1-contracts/contracts/bridge/BridgeHelper.sol b/l1-contracts/contracts/bridge/BridgeHelper.sol index bcc59327f..c43b8ed47 100644 --- a/l1-contracts/contracts/bridge/BridgeHelper.sol +++ b/l1-contracts/contracts/bridge/BridgeHelper.sol @@ -11,7 +11,7 @@ import {DataEncoding} from "../common/libraries/DataEncoding.sol"; /** * @author Matter Labs * @custom:security-contact security@matterlabs.dev - * @notice Helper library for working with L2 contracts on L1. + * @notice Helper library for working with native tokens on both L1 and L2. */ library BridgeHelper { /// @dev Receives and parses (name, symbol, decimals) from the token contract diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 789f122df..a4e7ce69c 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -267,7 +267,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, emit BridgehubDepositFinalized(_chainId, _txDataHash, _txHash); } - /// @dev Calls the internal `_encodeTxDataHash`. Used as a wrapped for try / catch case. + /// @dev Calls the library `encodeTxDataHash`. Used as a wrapped for try / catch case. /// @dev Encodes the transaction data hash using either the latest encoding standard or the legacy standard. /// @param _encodingVersion EncodingVersion. /// @param _originalCaller The address of the entity that initiated the deposit. @@ -411,7 +411,6 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, // Handling special case for withdrawal from ZKsync Era initiated before Shared Bridge. (bytes32 assetId, bytes memory transferData) = _verifyWithdrawal(_finalizeWithdrawalParams); - // Handling special case for withdrawal from zkSync Era initiated before Shared Bridge. if (_isPreSharedBridgeEraEthWithdrawal(chainId, l2BatchNumber)) { // Checks that the withdrawal wasn't finalized already. bool alreadyFinalized = IGetters(ERA_DIAMOND_PROXY).isEthWithdrawalFinalized(l2BatchNumber, l2MessageIndex); diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index 0c3b2001f..ead2554cc 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -157,7 +157,6 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { } /// @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 _originalCaller The `msg.sender` address from the external call that initiated current one. /// @param _assetId The encoding of asset ID. diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 6c122ccc6..338b17270 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -125,7 +125,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { } /*////////////////////////////////////////////////////////////// - LEGACY FUNCTIONS + INITIATTE DEPOSIT Functions //////////////////////////////////////////////////////////////*/ /// @notice Initiates a withdrawal by burning funds on the contract and sending the message to L1 @@ -137,6 +137,16 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { _withdrawSender(_assetId, _assetData, msg.sender, true); } + /*////////////////////////////////////////////////////////////// + Internal & Helpers + //////////////////////////////////////////////////////////////*/ + + /// @inheritdoc AssetRouterBase + function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) { + IL2NativeTokenVault nativeTokenVault = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR); + nativeTokenVault.ensureTokenIsRegistered(_token); + } + /// @notice Initiates a withdrawal by burning funds on the contract and sending the message to L1 /// where tokens would be unlocked /// @param _assetId The asset id of the withdrawn asset @@ -195,6 +205,10 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { return abi.encodePacked(IL1ERC20Bridge.finalizeWithdrawal.selector, _l1Receiver, _l1Token, _amount); } + /*////////////////////////////////////////////////////////////// + LEGACY FUNCTIONS + //////////////////////////////////////////////////////////////*/ + /// @notice Legacy finalizeDeposit. /// @dev Finalizes the deposit and mint funds. /// @param _l1Sender The address of token sender on L1. diff --git a/l1-contracts/contracts/bridge/interfaces/IAssetHandler.sol b/l1-contracts/contracts/bridge/interfaces/IAssetHandler.sol index 57f58eb59..7ab3e4b2a 100644 --- a/l1-contracts/contracts/bridge/interfaces/IAssetHandler.sol +++ b/l1-contracts/contracts/bridge/interfaces/IAssetHandler.sol @@ -27,7 +27,7 @@ interface IAssetHandler { /// @param _data the actual data specified for the function function bridgeMint(uint256 _chainId, bytes32 _assetId, bytes calldata _data) external payable; - /// @notice Burns bridged tokens and returns the calldata for L2 -> L1 message. + /// @notice Burns bridged tokens and returns the calldata for L2 <-> L1 message. /// @dev In case of native token vault _data is the tuple of _depositAmount and _l2Receiver. /// @param _chainId the chainId that the message will be sent to /// @param _msgValue the msg.value of the L2 transaction. For now it is always 0. diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 5e76d7630..28d589df6 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -46,8 +46,8 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 /// @dev For more details see https://docs.openzeppelin.com/contracts/3.x/api/proxy#UpgradeableBeacon. IBeacon public bridgedTokenBeacon; - /// @dev A mapping assetId => tokenAddress - mapping(bytes32 assetId => uint256 chainId) public originChainId; + /// @dev A mapping assetId => originChainId + mapping(bytes32 assetId => uint256 originChainId) public originChainId; /// @dev A mapping assetId => tokenAddress mapping(bytes32 assetId => address tokenAddress) public tokenAddress; @@ -160,7 +160,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 //////////////////////////////////////////////////////////////*/ /// @inheritdoc IAssetHandler - /// @notice Allows bridgehub to acquire mintValue for L1->L2 transactions. + /// @notice Allows bridgehub to acquire mintValue for L1->L2 and L2->L1 transactions. /// @dev In case of native token vault _data is the tuple of _depositAmount and _receiver. function bridgeBurn( uint256 _chainId, diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 8e23a9f3d..26d1c16f7 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -27,7 +27,7 @@ import {MigrationPaused, AssetIdAlreadyRegistered, ChainAlreadyLive, ChainNotLeg /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev -/// @dev The Bridgehub contract serves as the primary entry point for L1<->L2 communication, +/// @dev The Bridgehub contract serves as the primary entry point for L1->L2 communication, /// facilitating interactions between end user and bridges. /// It also manages state transition managers, base tokens, and chain registrations. /// Bridgehub is also an IL1AssetHandler for the chains themselves, which is used to migrate the chains diff --git a/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol b/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol index 6ffee2482..fc6d35bc2 100644 --- a/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol +++ b/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol @@ -22,7 +22,7 @@ contract CTMDeploymentTracker is ICTMDeploymentTracker, ReentrancyGuard, Ownable /// @dev Bridgehub smart contract that is used to operate with L2 via asynchronous L2 <-> L1 communication. IBridgehub public immutable override BRIDGE_HUB; - /// @dev Bridgehub smart contract that is used to operate with L2 via asynchronous L2 <-> L1 communication. + /// @dev L1AssetRouter smart contract that is used to bridge assets (including chains) between L1 and L2. IAssetRouterBase public immutable override L1_ASSET_ROUTER; /// @dev The encoding version of the data. diff --git a/l1-contracts/contracts/bridgehub/MessageRoot.sol b/l1-contracts/contracts/bridgehub/MessageRoot.sol index 3d81b990f..69613359e 100644 --- a/l1-contracts/contracts/bridgehub/MessageRoot.sol +++ b/l1-contracts/contracts/bridgehub/MessageRoot.sol @@ -21,7 +21,7 @@ bytes32 constant CHAIN_TREE_EMPTY_ENTRY_HASH = bytes32( 0x46700b4d40ac5c35af2c22dda2787a91eb567b06c924a8fb8ae9a05b20c08c21 ); -// Chain tree consists of batch commitments as their leaves. We use hash of "new bytes(96)" as the hash of an empty leaf. +// The single shared tree consists of the roots of chain trees as its. We use hash of "new bytes(96)" as the hash of an empty leaf. bytes32 constant SHARED_ROOT_TREE_EMPTY_HASH = bytes32( 0x46700b4d40ac5c35af2c22dda2787a91eb567b06c924a8fb8ae9a05b20c08c21 ); diff --git a/l1-contracts/contracts/common/L2ContractAddresses.sol b/l1-contracts/contracts/common/L2ContractAddresses.sol index a8fba013c..fa8695656 100644 --- a/l1-contracts/contracts/common/L2ContractAddresses.sol +++ b/l1-contracts/contracts/common/L2ContractAddresses.sol @@ -38,7 +38,7 @@ address constant L2_COMPLEX_UPGRADER_ADDR = address(0x800f); /// @dev The address used to execute the genesis upgrade address constant L2_GENESIS_UPGRADE_ADDR = address(0x10001); -/// @dev The address of the L2 bridge hub system contract, used to start L2<>L2 transactions +/// @dev The address of the L2 bridge hub system contract, used to start L1->L2 transactions address constant L2_BRIDGEHUB_ADDR = address(0x10002); /// @dev the address of the l2 asset router. diff --git a/l1-contracts/contracts/common/libraries/DataEncoding.sol b/l1-contracts/contracts/common/libraries/DataEncoding.sol index 9df83d67a..c92cedead 100644 --- a/l1-contracts/contracts/common/libraries/DataEncoding.sol +++ b/l1-contracts/contracts/common/libraries/DataEncoding.sol @@ -83,7 +83,7 @@ library DataEncoding { return keccak256(abi.encode(_chainId, L2_NATIVE_TOKEN_VAULT_ADDR, _assetData)); } - /// @notice Encodes the asset data by combining chain id, NTV as asset deployment tracker and asset data. + /// @notice Encodes the asset data by combining chain id, NTV as asset deployment tracker and token address. /// @param _chainId The id of the chain token is native to. /// @param _tokenAddress The address of token that has to be encoded (asset data is the address itself). /// @return The encoded asset data. diff --git a/l1-contracts/contracts/common/libraries/FullMerkle.sol b/l1-contracts/contracts/common/libraries/FullMerkle.sol index d39ccde48..7386ad3bc 100644 --- a/l1-contracts/contracts/common/libraries/FullMerkle.sol +++ b/l1-contracts/contracts/common/libraries/FullMerkle.sol @@ -20,11 +20,9 @@ library FullMerkle { } /** - * @dev Initialize a {Bytes32PushTree} using {Merkle.efficientHash} to hash internal nodes. + * @dev Initialize a {FullTree} using {Merkle.efficientHash} to hash internal nodes. * The capacity of the tree (i.e. number of leaves) is set to `2**levels`. * - * Calling this function on MerkleTree that was already setup and used will reset it to a blank state. - * * IMPORTANT: The zero value should be carefully chosen since it will be stored in the tree representing * empty leaves. It should be a value that is not expected to be part of the tree. * @param zero The zero value to be used in the tree. diff --git a/l1-contracts/contracts/state-transition/ChainTypeManager.sol b/l1-contracts/contracts/state-transition/ChainTypeManager.sol index 62df92419..b3e8cced6 100644 --- a/l1-contracts/contracts/state-transition/ChainTypeManager.sol +++ b/l1-contracts/contracts/state-transition/ChainTypeManager.sol @@ -359,7 +359,6 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg return getZKChain(_chainId); } - // check not registered Diamond.DiamondCutData memory diamondCut = abi.decode(_diamondCut, (Diamond.DiamondCutData)); { @@ -442,7 +441,6 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg function registerSettlementLayer(uint256 _newSettlementLayerChainId, bool _isWhitelisted) external onlyOwner { require(_newSettlementLayerChainId != 0, "Bad chain id"); - // Currently, we require that the sync layer is deployed by the same CTM. require(getZKChain(_newSettlementLayerChainId) != address(0), "CTM: sync layer not registered"); IBridgehub(BRIDGE_HUB).registerSettlementLayer(_newSettlementLayerChainId, _isWhitelisted); 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..18d3cd304 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol @@ -341,7 +341,6 @@ contract AdminFacet is ZKChainBase, IAdmin { } /// @inheritdoc IAdmin - /// @dev Note that this function does not check that the caller is the chain admin. function forwardedBridgeRecoverFailedTransfer( uint256 /* _chainId */, bytes32 /* _assetInfo */, From d4391b577f13163696274a3cdf982403fde97ca6 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 14 Oct 2024 20:26:06 +0100 Subject: [PATCH 18/60] kl/l07-gw-audit-2 onlyARorSelf issue --- l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 6c122ccc6..2090fff90 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -54,6 +54,8 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { if ((AddressAliasHelper.undoL1ToL2Alias(msg.sender) != l1AssetRouter) && (msg.sender != address(this))) { revert InvalidCaller(msg.sender); } + } else { + revert InvalidCaller(msg.sender); // xL2 messaging not supported for now } _; } From b398b368d281a9de7ffb9c586a35f2bf0d135773 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 14 Oct 2024 20:36:25 +0100 Subject: [PATCH 19/60] kl/l09-gw-audit-2 merkle proof doc string --- l1-contracts/contracts/common/libraries/Merkle.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/l1-contracts/contracts/common/libraries/Merkle.sol b/l1-contracts/contracts/common/libraries/Merkle.sol index 66db8ea75..457b54bfa 100644 --- a/l1-contracts/contracts/common/libraries/Merkle.sol +++ b/l1-contracts/contracts/common/libraries/Merkle.sol @@ -14,6 +14,7 @@ library Merkle { /// @dev Calculate Merkle root by the provided Merkle proof. /// NOTE: When using this function, check that the _path length is equal to the tree height to prevent shorter/longer paths attack + /// however, for chains settling on GW the proof includes the GW proof, so the path increases. See Mailbox for more details. /// @param _path Merkle path from the leaf to the root /// @param _index Leaf index in the tree /// @param _itemHash Hash of leaf content From 9448dcfe86d2e5f7a36c46ff4f60df0f3cff751b Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 14 Oct 2024 20:47:32 +0100 Subject: [PATCH 20/60] kl/n01-gw-audit-2 todos --- l1-contracts/contracts/bridge/asset-router/AssetRouterBase.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/contracts/bridge/asset-router/AssetRouterBase.sol b/l1-contracts/contracts/bridge/asset-router/AssetRouterBase.sol index 1a27e825f..250f4ecb3 100644 --- a/l1-contracts/contracts/bridge/asset-router/AssetRouterBase.sol +++ b/l1-contracts/contracts/bridge/asset-router/AssetRouterBase.sol @@ -109,7 +109,7 @@ abstract contract AssetRouterBase is IAssetRouterBase, Ownable2StepUpgradeable, IAssetHandler(assetHandler).bridgeMint(_chainId, _assetId, _transferData); } else { assetHandlerAddress[_assetId] = _nativeTokenVault; - IAssetHandler(_nativeTokenVault).bridgeMint(_chainId, _assetId, _transferData); // ToDo: Maybe it's better to receive amount and receiver here? transferData may have different encoding + IAssetHandler(_nativeTokenVault).bridgeMint(_chainId, _assetId, _transferData); } } From dbbd611aefe5297a46924018d3306087d2b58bda Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 14 Oct 2024 20:54:46 +0100 Subject: [PATCH 21/60] fix: kl/n02-gw-audit typos --- da-contracts/contracts/IL1DAValidator.sol | 2 +- .../contracts/bridge/asset-router/IL2AssetRouter.sol | 10 +++++++++- .../contracts/bridge/asset-router/L1AssetRouter.sol | 2 +- .../contracts/common/libraries/DataEncoding.sol | 6 +++--- .../chain-interfaces/IL1DAValidator.sol | 2 +- .../data-availability/CalldataDAGateway.sol | 2 +- .../state-transition/libraries/PriorityTree.sol | 2 +- 7 files changed, 17 insertions(+), 9 deletions(-) diff --git a/da-contracts/contracts/IL1DAValidator.sol b/da-contracts/contracts/IL1DAValidator.sol index c22e9c557..cb2b640e5 100644 --- a/da-contracts/contracts/IL1DAValidator.sol +++ b/da-contracts/contracts/IL1DAValidator.sol @@ -17,7 +17,7 @@ struct L1DAValidatorOutput { interface IL1DAValidator { /// @notice The function that checks the data availability for the given batch input. /// @param _chainId The chain id of the chain that is being committed. - /// @param _chainId The batch number for which the data availability is being checked. + /// @param _batchNumber The batch number for which the data availability is being checked. /// @param _l2DAValidatorOutputHash The hash of that was returned by the l2DAValidator. /// @param _operatorDAInput The DA input by the operator provided on L1. /// @param _maxBlobsSupported The maximal number of blobs supported by the chain. diff --git a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol index 34ce2ecd1..3c77299bd 100644 --- a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol @@ -18,7 +18,15 @@ interface IL2AssetRouter { function withdrawLegacyBridge(address _l1Receiver, address _l2Token, uint256 _amount, address _sender) external; - /// @dev Used to set the assedAddress for a given assetId. + function finalizeDepositLegacyBridge( + address _l1Sender, + address _l2Receiver, + address _l1Token, + uint256 _amount, + bytes calldata _data + ) external; + + /// @dev Used to set the assetHandlerAddress for a given assetId. /// @dev Will be used by ZK Gateway function setAssetHandlerAddress(uint256 _originChainId, bytes32 _assetId, address _assetAddress) external; } diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index 0c3b2001f..5eca6fd62 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -110,7 +110,7 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { _transferOwnership(_owner); } - /// @notice Sets the L1ERC20Bridge contract address. + /// @notice Sets the NativeTokenVault contract address. /// @dev Should be called only once by the owner. /// @param _nativeTokenVault The address of the native token vault. function setNativeTokenVault(INativeTokenVault _nativeTokenVault) external onlyOwner { diff --git a/l1-contracts/contracts/common/libraries/DataEncoding.sol b/l1-contracts/contracts/common/libraries/DataEncoding.sol index 9df83d67a..87384aee1 100644 --- a/l1-contracts/contracts/common/libraries/DataEncoding.sol +++ b/l1-contracts/contracts/common/libraries/DataEncoding.sol @@ -68,11 +68,11 @@ library DataEncoding { /// @notice Encodes the asset data by combining chain id, asset deployment tracker and asset data. /// @param _chainId The id of the chain token is native to. - /// @param _tokenAaddress The address of token that has to be encoded (asset data is the address itself). + /// @param _tokenAddress The address of token that has to be encoded (asset data is the address itself). /// @param _sender The asset deployment tracker address. /// @return The encoded asset data. - function encodeAssetId(uint256 _chainId, address _tokenAaddress, address _sender) internal pure returns (bytes32) { - return keccak256(abi.encode(_chainId, _sender, _tokenAaddress)); + function encodeAssetId(uint256 _chainId, address _tokenAddress, address _sender) internal pure returns (bytes32) { + return keccak256(abi.encode(_chainId, _sender, _tokenAddress)); } /// @notice Encodes the asset data by combining chain id, NTV as asset deployment tracker and asset data. diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IL1DAValidator.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IL1DAValidator.sol index a4fe56b01..b5ea1b85c 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IL1DAValidator.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IL1DAValidator.sol @@ -23,7 +23,7 @@ struct L1DAValidatorOutput { interface IL1DAValidator { /// @notice The function that checks the data availability for the given batch input. /// @param _chainId The chain id of the chain that is being committed. - /// @param _chainId The batch number for which the data availability is being checked. + /// @param _batchNumber The batch number for which the data availability is being checked. /// @param _l2DAValidatorOutputHash The hash of that was returned by the l2DAValidator. /// @param _operatorDAInput The DA input by the operator provided on L1. /// @param _maxBlobsSupported The maximal number of blobs supported by the chain. diff --git a/l1-contracts/contracts/state-transition/data-availability/CalldataDAGateway.sol b/l1-contracts/contracts/state-transition/data-availability/CalldataDAGateway.sol index 3e069bbd5..41840ddb7 100644 --- a/l1-contracts/contracts/state-transition/data-availability/CalldataDAGateway.sol +++ b/l1-contracts/contracts/state-transition/data-availability/CalldataDAGateway.sol @@ -6,7 +6,7 @@ import {CalldataDA, BLOB_COMMITMENT_SIZE, BLOB_SIZE_BYTES} from "./CalldataDA.so // solhint-disable gas-custom-errors, reason-string -/// @notice Contract that contains the functionality for process the calldata DA. +/// @notice Contract that contains the functionality for processing the calldata DA. /// @dev The expected l2DAValidator that should be used with it `RollupL2DAValidator`. abstract contract CalldataDAGateway is CalldataDA { /// @inheritdoc CalldataDA diff --git a/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol b/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol index 71d6d9df1..c4459ec34 100644 --- a/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol +++ b/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol @@ -27,7 +27,7 @@ library PriorityTree { DynamicIncrementalMerkle.Bytes32PushTree tree; } - /// @notice Returns zero if and only if no operations were processed from the queue + /// @notice Returns zero if and only if no operations were processed from the tree /// @return Index of the oldest priority operation that wasn't processed yet function getFirstUnprocessedPriorityTx(Tree storage _tree) internal view returns (uint256) { return _tree.startIndex + _tree.unprocessedIndex; From 36bb3a0ef2fa66b4e782da725e0a56c4f785b5be Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 14 Oct 2024 21:00:14 +0100 Subject: [PATCH 22/60] kl/n04-gw-audit-2 duplicate onlyL1Ntv in L1N --- l1-contracts/contracts/bridge/L1Nullifier.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 789f122df..2ec02c671 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -204,8 +204,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, /// @dev This function is part of the upgrade process used to nullify chain balances once they are credited to NTV. /// @param _chainId The ID of the ZK chain. /// @param _token The address of the token which was previously deposit to shared bridge. - function nullifyChainBalanceByNTV(uint256 _chainId, address _token) external { - require(msg.sender == address(l1NativeTokenVault), "L1N: not NTV"); + function nullifyChainBalanceByNTV(uint256 _chainId, address _token) external onlyL1NTV { __DEPRECATED_chainBalance[_chainId][_token] = 0; } From 9f6a45657c5737f79c0e4ce1c6fc6555cfe11c42 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 14 Oct 2024 21:23:05 +0100 Subject: [PATCH 23/60] fix: kl/n05-gw-audit-2 immutables --- .../contracts/bridge/asset-router/IL2AssetRouter.sol | 2 +- .../contracts/bridge/asset-router/L2AssetRouter.sol | 10 +++++----- .../contracts/bridge/ntv/L2NativeTokenVault.sol | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol index 34ce2ecd1..766d20ae0 100644 --- a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol @@ -14,7 +14,7 @@ interface IL2AssetRouter { function withdraw(bytes32 _assetId, bytes calldata _transferData) external; - function l1AssetRouter() external view returns (address); + function L1_ASSET_ROUTER() external view returns (address); function withdrawLegacyBridge(address _l1Receiver, address _l2Token, uint256 _amount, address _sender) external; diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 6c122ccc6..2c6da47e3 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -32,13 +32,13 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { bytes32 public immutable BASE_TOKEN_ASSET_ID; /// @dev The address of the L1 asset router counterpart. - address public override l1AssetRouter; + address public immutable override L1_ASSET_ROUTER; /// @notice Checks that the message sender is the L1 Asset Router. modifier onlyAssetRouterCounterpart(uint256 _originChainId) { if (_originChainId == L1_CHAIN_ID) { // Only the L1 Asset Router counterpart can initiate and finalize the deposit. - if (AddressAliasHelper.undoL1ToL2Alias(msg.sender) != l1AssetRouter) { + if (AddressAliasHelper.undoL1ToL2Alias(msg.sender) != L1_ASSET_ROUTER) { revert InvalidCaller(msg.sender); } } else { @@ -51,7 +51,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { modifier onlyAssetRouterCounterpartOrSelf(uint256 _originChainId) { if (_originChainId == L1_CHAIN_ID) { // Only the L1 Asset Router counterpart can initiate and finalize the deposit. - if ((AddressAliasHelper.undoL1ToL2Alias(msg.sender) != l1AssetRouter) && (msg.sender != address(this))) { + if ((AddressAliasHelper.undoL1ToL2Alias(msg.sender) != L1_ASSET_ROUTER) && (msg.sender != address(this))) { revert InvalidCaller(msg.sender); } } @@ -79,7 +79,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { if (_l1AssetRouter == address(0)) { revert EmptyAddress(); } - l1AssetRouter = _l1AssetRouter; + L1_ASSET_ROUTER = _l1AssetRouter; assetHandlerAddress[_baseTokenAssetId] = L2_NATIVE_TOKEN_VAULT_ADDR; BASE_TOKEN_ASSET_ID = _baseTokenAssetId; _disableInitializers(); @@ -278,6 +278,6 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { /// @notice Returns the address of the L1 asset router. /// @dev The old name is kept for backward compatibility. function l1Bridge() external view returns (address) { - return l1AssetRouter; + return L1_ASSET_ROUTER; } } diff --git a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol index e96a6d289..da9016720 100644 --- a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol @@ -34,7 +34,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { IL2SharedBridgeLegacy public immutable L2_LEGACY_SHARED_BRIDGE; /// @dev Bytecode hash of the proxy for tokens deployed by the bridge. - bytes32 internal l2TokenProxyBytecodeHash; + bytes32 internal immutable L2_TOKEN_PROXY_BYTECODE_HASH; /// @notice Initializes the bridge contract for later use. /// @param _l1ChainId The L1 chain id differs between mainnet and testnets. @@ -63,7 +63,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { revert EmptyAddress(); } - l2TokenProxyBytecodeHash = _l2TokenProxyBytecodeHash; + L2_TOKEN_PROXY_BYTECODE_HASH = _l2TokenProxyBytecodeHash; _transferOwnership(_aliasedOwner); if (_contractsDeployedAlready) { @@ -126,7 +126,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { } /// @notice Deploys the beacon proxy for the L2 token, while using ContractDeployer system contract. - /// @dev This function uses raw call to ContractDeployer to make sure that exactly `l2TokenProxyBytecodeHash` is used + /// @dev This function uses raw call to ContractDeployer to make sure that exactly `L2_TOKEN_PROXY_BYTECODE_HASH` is used /// for the code of the proxy. /// @param _salt The salt used for beacon proxy deployment of L2 bridged token. /// @return proxy The beacon proxy, i.e. L2 bridged token. @@ -140,7 +140,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { 0, abi.encodeCall( IContractDeployer.create2, - (_salt, l2TokenProxyBytecodeHash, abi.encode(address(bridgedTokenBeacon), "")) + (_salt, L2_TOKEN_PROXY_BYTECODE_HASH, abi.encode(address(bridgedTokenBeacon), "")) ) ); @@ -185,7 +185,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { L2ContractHelper.computeCreate2Address( address(this), salt, - l2TokenProxyBytecodeHash, + L2_TOKEN_PROXY_BYTECODE_HASH, constructorInputHash ); } From 87408ceca8a1e370c10f0f825779af85d179815c Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 14 Oct 2024 21:34:40 +0100 Subject: [PATCH 24/60] fix: kl/n06-gw-audit-2 unused errors --- da-contracts/contracts/DAContractsErrors.sol | 2 -- l1-contracts/contracts/common/L1ContractErrors.sol | 13 ------------- .../l1/unit/concrete/Executor/Committing.t.sol | 2 +- l2-contracts/contracts/errors/L2ContractErrors.sol | 2 -- 4 files changed, 1 insertion(+), 18 deletions(-) diff --git a/da-contracts/contracts/DAContractsErrors.sol b/da-contracts/contracts/DAContractsErrors.sol index 2116d582d..69227c2f9 100644 --- a/da-contracts/contracts/DAContractsErrors.sol +++ b/da-contracts/contracts/DAContractsErrors.sol @@ -3,8 +3,6 @@ pragma solidity ^0.8.21; // 0x53dee67b error PubdataCommitmentsEmpty(); -// 0x7734c31a -error PubdataCommitmentsTooBig(); // 0x53e6d04d error InvalidPubdataCommitmentsSize(); // 0xafd53e2f diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 48c90d540..fe6dc594f 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -115,13 +115,10 @@ error EmptyBytes32(); error EmptyDeposit(); // error ETHDepositNotSupported(); -// -error FailedToTransferTokens(address tokenContract, address to, uint256 amount); // 0xac4a3f98 error FacetExists(bytes4 selector, address); // 0x79e12cc3 error FacetIsFrozen(bytes4 func); -error FunctionNotSupported(); // 0xc91cf3b1 error GasPerPubdataMismatch(); // 0x6d4a7df8 @@ -138,8 +135,6 @@ error HashedLogIsDefault(); error HashMismatch(bytes32 expected, bytes32 actual); // 0xb615c2b1 error ZKChainLimitReached(); -// -error InsufficientAllowance(uint256 providedAllowance, uint256 requiredAmount); // 0xdd381a4c error IncorrectBridgeHubAddress(address bridgehub); // 0x826fb11e @@ -154,8 +149,6 @@ error InvalidChainId(); error InvalidDelay(); // 0x0af806e0 error InvalidHash(); -// -error InvalidInput(); // 0xc1780bd6 error InvalidLogSender(address sender, uint256 logKey); // 0xd8e9405c @@ -288,8 +281,6 @@ error ProtocolVersionMinorDeltaTooBig(uint256 limit, uint256 proposed); error ProtocolVersionTooSmall(); // 0x53dee67b error PubdataCommitmentsEmpty(); -// 0x7734c31a -error PubdataCommitmentsTooBig(); // 0x959f26fb error PubdataGreaterThanLimit(uint256 limit, uint256 length); // 0x2a4a14df @@ -358,14 +349,10 @@ error UndefinedDiamondCutAction(); error UnexpectedNumberOfFactoryDeps(); // 0x6aa39880 error UnexpectedSystemLog(uint256 logKey); -// -error UnimplementedMessage(string); // 0xf093c2e5 error UpgradeBatchNumberIsNotZero(); // error UnsupportedEncodingVersion(); -// -error UnsupportedPaymasterFlow(); // 0x47b3b145 error ValidateTxnNotEnoughGas(); // 0x626ade30 diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Executor/Committing.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Executor/Committing.t.sol index 5c2d5b65a..48dedb69a 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Executor/Committing.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Executor/Committing.t.sol @@ -11,7 +11,7 @@ import {SystemLogKey} from "contracts/state-transition/chain-interfaces/IExecuto import {POINT_EVALUATION_PRECOMPILE_ADDR} from "contracts/common/Config.sol"; import {L2_PUBDATA_CHUNK_PUBLISHER_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {BLS_MODULUS} from "da-contracts/DAUtils.sol"; -import {TimeNotReached, BatchNumberMismatch, PubdataCommitmentsTooBig, InvalidPubdataCommitmentsSize, PubdataCommitmentsEmpty, L2TimestampTooBig, EmptyBlobVersionHash, CanOnlyProcessOneBatch, TimestampError, LogAlreadyProcessed, InvalidLogSender, UnexpectedSystemLog, HashMismatch, BatchHashMismatch, ValueMismatch, MissingSystemLogs, InvalidPubdataLength, NonEmptyBlobVersionHash, BlobHashCommitmentError} from "contracts/common/L1ContractErrors.sol"; +import {TimeNotReached, BatchNumberMismatch, InvalidPubdataCommitmentsSize, PubdataCommitmentsEmpty, L2TimestampTooBig, EmptyBlobVersionHash, CanOnlyProcessOneBatch, TimestampError, LogAlreadyProcessed, InvalidLogSender, UnexpectedSystemLog, HashMismatch, BatchHashMismatch, ValueMismatch, MissingSystemLogs, InvalidPubdataLength, NonEmptyBlobVersionHash, BlobHashCommitmentError} from "contracts/common/L1ContractErrors.sol"; contract CommittingTest is ExecutorTest { bytes32[] defaultBlobVersionedHashes; diff --git a/l2-contracts/contracts/errors/L2ContractErrors.sol b/l2-contracts/contracts/errors/L2ContractErrors.sol index bb16f38c6..9a555ce88 100644 --- a/l2-contracts/contracts/errors/L2ContractErrors.sol +++ b/l2-contracts/contracts/errors/L2ContractErrors.sol @@ -27,8 +27,6 @@ error NonSequentialVersion(); error Unauthorized(address); // 0x6e128399 error Unimplemented(); -// 0xa4dde386 -error UnimplementedMessage(string message); // 0xff15b069 error UnsupportedPaymasterFlow(); // 0x750b219c From 75e8775c1c4ae04ec6f97d597b6ce8d663a7cd37 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 14 Oct 2024 21:38:38 +0100 Subject: [PATCH 25/60] kl/n07-gw-audit-2 duplicate imports --- l1-contracts/contracts/bridge/L1Nullifier.sol | 3 +-- l1-contracts/contracts/bridgehub/Bridgehub.sol | 2 +- .../contracts/state-transition/chain-deps/facets/Mailbox.sol | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 789f122df..a46272654 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -30,8 +30,7 @@ import {DataEncoding} from "../common/libraries/DataEncoding.sol"; import {IBridgehub} from "../bridgehub/IBridgehub.sol"; import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR, L2_ASSET_ROUTER_ADDR} from "../common/L2ContractAddresses.sol"; -import {DataEncoding} from "../common/libraries/DataEncoding.sol"; -import {Unauthorized, SharedBridgeKey, DepositExists, AddressAlreadySet, InvalidProof, DepositDoesNotExist, SharedBridgeValueNotSet, WithdrawalAlreadyFinalized, L2WithdrawalMessageWrongLength, InvalidSelector, SharedBridgeValueNotSet, ZeroAddress} from "../common/L1ContractErrors.sol"; +import {Unauthorized, SharedBridgeKey, DepositExists, AddressAlreadySet, InvalidProof, DepositDoesNotExist, SharedBridgeValueNotSet, WithdrawalAlreadyFinalized, L2WithdrawalMessageWrongLength, InvalidSelector, ZeroAddress} from "../common/L1ContractErrors.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 8e23a9f3d..6965e694e 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -23,7 +23,7 @@ import {BridgehubL2TransactionRequest, L2Message, L2Log, TxStatus} from "../comm import {AddressAliasHelper} from "../vendor/AddressAliasHelper.sol"; import {IMessageRoot} from "./IMessageRoot.sol"; import {ICTMDeploymentTracker} from "./ICTMDeploymentTracker.sol"; -import {MigrationPaused, AssetIdAlreadyRegistered, ChainAlreadyLive, ChainNotLegacy, CTMNotRegistered, ChainIdNotRegistered, AssetHandlerNotRegistered, ZKChainLimitReached, CTMAlreadyRegistered, CTMNotRegistered, ZeroChainId, ChainIdTooBig, BridgeHubAlreadyRegistered, AddressTooLow, MsgValueMismatch, ZeroAddress, Unauthorized, SharedBridgeNotSet, WrongMagicValue, ChainIdAlreadyExists, ChainIdMismatch, ChainIdCantBeCurrentChain, EmptyAssetId, AssetIdNotSupported, IncorrectBridgeHubAddress} from "../common/L1ContractErrors.sol"; +import {MigrationPaused, AssetIdAlreadyRegistered, ChainAlreadyLive, ChainNotLegacy, CTMNotRegistered, ChainIdNotRegistered, AssetHandlerNotRegistered, ZKChainLimitReached, CTMAlreadyRegistered, ZeroChainId, ChainIdTooBig, BridgeHubAlreadyRegistered, AddressTooLow, MsgValueMismatch, ZeroAddress, Unauthorized, SharedBridgeNotSet, WrongMagicValue, ChainIdAlreadyExists, ChainIdMismatch, ChainIdCantBeCurrentChain, EmptyAssetId, AssetIdNotSupported, IncorrectBridgeHubAddress} from "../common/L1ContractErrors.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol index 48b6dd76d..8a279f052 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol @@ -26,9 +26,7 @@ import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA, L1_GAS_PER_PUBDATA_BYTE, L2_L1_LOGS_T import {L2_BOOTLOADER_ADDRESS, L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR, L2_BRIDGEHUB_ADDR} from "../../../common/L2ContractAddresses.sol"; import {IL1AssetRouter} from "../../../bridge/asset-router/IL1AssetRouter.sol"; -import {IBridgehub} from "../../../bridgehub/IBridgehub.sol"; -import {IChainTypeManager} from "../../IChainTypeManager.sol"; import {MerklePathEmpty, OnlyEraSupported, BatchNotExecuted, HashedLogIsDefault, BaseTokenGasPriceDenominatorNotSet, TransactionNotAllowed, GasPerPubdataMismatch, TooManyFactoryDeps, MsgValueTooLow} from "../../../common/L1ContractErrors.sol"; // While formally the following import is not used, it is needed to inherit documentation from it From f0978bfdaac1ccaabbb1792333f8b8690bb25251 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 15 Oct 2024 11:25:49 +0100 Subject: [PATCH 26/60] fix: kl/l08-2gw --- l1-contracts/contracts/state-transition/ValidatorTimelock.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/contracts/state-transition/ValidatorTimelock.sol b/l1-contracts/contracts/state-transition/ValidatorTimelock.sol index 64cc0bc20..ba121e9ca 100644 --- a/l1-contracts/contracts/state-transition/ValidatorTimelock.sol +++ b/l1-contracts/contracts/state-transition/ValidatorTimelock.sol @@ -183,7 +183,7 @@ contract ValidatorTimelock is IExecutor, Ownable2Step { /// @dev Call the zkChain diamond contract with the same calldata as this contract was called. /// Note: it is called the zkChain diamond contract, not delegatecalled! function _propagateToZKChain(uint256 _chainId) internal { - address contractAddress = chainTypeManager.getZKChain(_chainId); + address contractAddress = chainTypeManager.getHyperchain(_chainId); assembly { // Copy function signature and arguments from calldata at zero position into memory at pointer position calldatacopy(0, 0, calldatasize()) From bbb0d8ca8ff53c53c3743f4152c443d9bfe6d7ef Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Wed, 16 Oct 2024 15:38:33 +0400 Subject: [PATCH 27/60] (fix): still in progress but working --- l1-contracts/.env | 2 + .../DeployZKAndBridgeToL1.s.sol | 235 ++++++++++++++---- 2 files changed, 189 insertions(+), 48 deletions(-) diff --git a/l1-contracts/.env b/l1-contracts/.env index 1baa451ef..e7cfb27ae 100644 --- a/l1-contracts/.env +++ b/l1-contracts/.env @@ -39,9 +39,11 @@ ETH_SENDER_SENDER_OPERATOR_BLOBS_ETH_ADDR=0x000000000000000000000000000000000000 CONTRACTS_SHARED_BRIDGE_UPGRADE_STORAGE_SWITCH=0 CONTRACTS_MAX_NUMBER_OF_ZK_CHAINS=100 L1_CONFIG=/script-config/config-deploy-l1.toml +L2_CONFIG=/script-config/config-deploy-l2-contracts.toml L1_OUTPUT=/script-out/output-deploy-l1.toml TOKENS_CONFIG=/script-config/config-deploy-erc20.toml ZK_TOKEN_CONFIG=/script-config/config-deploy-zk.toml +ZK_TOKEN_OUTPUT=/script-out/output-deploy-zk-token.toml ZK_CHAIN_CONFIG=/script-config/register-zk-chain.toml ZK_CHAIN_OUTPUT=/script-out/output-deploy-zk-chain-era.toml FORCE_DEPLOYMENTS_CONFIG=/script-config/generate-force-deployments-data.toml diff --git a/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol b/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol index 277cf3c0c..2859269fd 100644 --- a/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.24; // solhint-disable no-console +import {Vm} from "forge-std/Vm.sol"; import {Script, console2 as console} from "forge-std/Script.sol"; import {stdToml} from "forge-std/StdToml.sol"; @@ -12,6 +13,14 @@ import {TestnetERC20Token} from "contracts/dev-contracts/TestnetERC20Token.sol"; // solhint-disable no-unused-import import {WETH9} from "contracts/dev-contracts/WETH9.sol"; +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; + +import {FinalizeL1DepositParams} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {L2NativeTokenVault} from "contracts/bridge/ntv/L2NativeTokenVault.sol"; +import {IL1NativeTokenVault} from "contracts/bridge/ntv/IL1NativeTokenVault.sol"; import {Utils} from "./Utils.sol"; import {MintFailed} from "./ZkSyncScriptErrors.sol"; @@ -27,6 +36,12 @@ contract DeployZKScript is Script { uint256 chainId; address l1SharedBridge; address bridgehub; + address l1Nullifier; + address chainAdmin; + address governance; + address deployer; + address owner; + address anotherOwner; } struct TokenDescription { @@ -36,6 +51,7 @@ contract DeployZKScript is Script { uint256 decimals; string implementation; uint256 mint; + bytes32 assetId; } Config internal config; @@ -58,18 +74,9 @@ contract DeployZKScript is Script { string memory root = vm.projectRoot(); // Grab config from output of l1 deployment - string memory path = string.concat(root, vm.envString("L1_OUTPUT")); + string memory path = string.concat(root, vm.envString("TOKENS_CONFIG")); string memory toml = vm.readFile(path); - // Config file must be parsed key by key, otherwise values returned - // are parsed alfabetically and not by key. - // https://book.getfoundry.sh/cheatcodes/parse-toml - config.create2FactoryAddr = vm.parseTomlAddress(toml, "$.create2_factory_addr"); - config.create2FactorySalt = vm.parseTomlBytes32(toml, "$.create2_factory_salt"); - - // Grab config from custom config file - path = string.concat(root, vm.envString("ZK_TOKEN_CONFIG")); - toml = vm.readFile(path); config.additionalAddressesForMinting = vm.parseTomlAddressArray(toml, "$.additional_addresses_for_minting"); // Parse the ZK token configuration @@ -79,12 +86,43 @@ contract DeployZKScript is Script { config.zkToken.decimals = toml.readUint(string.concat(key, ".decimals")); config.zkToken.implementation = toml.readString(string.concat(key, ".implementation")); config.zkToken.mint = toml.readUint(string.concat(key, ".mint")); + + // Grab config from custom config file + path = string.concat(root, vm.envString("ZK_CHAIN_CONFIG")); + toml = vm.readFile(path); + + config.bridgehub = toml.readAddress("$.deployed_addresses.bridgehub.bridgehub_proxy_addr"); + config.l1SharedBridge = toml.readAddress("$.deployed_addresses.bridges.shared_bridge_proxy_addr"); + config.l1Nullifier = toml.readAddress("$.deployed_addresses.bridges.l1_nullifier_proxy_addr"); + config.chainId = toml.readUint("$.chain.chain_chain_id"); + + // Grab config from custom config file + path = string.concat(root, vm.envString("L2_CONFIG")); + toml = vm.readFile(path); + config.anotherOwner = toml.readAddress("$.consensus_registry_owner"); + console.log("Another owner: ", config.anotherOwner); + } + + function initializeAdditionalConfig() internal { + string memory root = vm.projectRoot(); + // string memory path = string.concat(root, vm.envString("ZK_CHAIN_OUTPUT")); + // string memory toml = vm.readFile(path); + // config.chainAdmin = toml.readAddress("$.chain_admin_addr"); + + string memory path = string.concat(root, vm.envString("L1_OUTPUT")); + string memory toml = vm.readFile(path); + config.governance = toml.readAddress("$.deployed_addresses.governance_addr"); + config.deployer = toml.readAddress("$.deployer_addr"); + config.owner = toml.readAddress("$.owner_address"); } function deployZkToken() internal { + uint256 amount = 90000000000000000000; + TokenDescription storage token = config.zkToken; console.log("Deploying token:", token.name); - address tokenAddress = deployErc20({ + vm.startBroadcast(); + address zkTokenAddress = deployErc20({ name: token.name, symbol: token.symbol, decimals: token.decimals, @@ -92,8 +130,107 @@ contract DeployZKScript is Script { mint: token.mint, additionalAddressesForMinting: config.additionalAddressesForMinting }); - console.log("Token deployed at:", tokenAddress); - token.addr = tokenAddress; + console.log("Token deployed at:", zkTokenAddress); + token.addr = zkTokenAddress; + address deployer = msg.sender; + TestnetERC20Token zkToken = TestnetERC20Token(zkTokenAddress); + zkToken.mint(deployer, amount); + uint256 deployerBalance = zkToken.balanceOf(deployer); + console.log("Deployed balance:", deployerBalance); + + L2AssetRouter l2AR = L2AssetRouter(L2_ASSET_ROUTER_ADDR); + L2NativeTokenVault l2NTV = L2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR); + l2NTV.registerToken(zkTokenAddress); + bytes32 zkTokenAssetId = l2NTV.assetId(zkTokenAddress); + config.zkToken.assetId = zkTokenAssetId; + console.log("zkTokenAssetId:", uint256(zkTokenAssetId)); + zkToken.approve(L2_NATIVE_TOKEN_VAULT_ADDR, amount); + vm.stopBroadcast(); + + vm.broadcast(); + l2AR.withdraw(zkTokenAssetId, abi.encode(amount, deployer)); + + uint256 deployerBalanceAfterWithdraw = zkToken.balanceOf(deployer); + + console.log("Deployed balance after withdraw:", deployerBalanceAfterWithdraw); + } + + /// TODO(EVM-748): make that function support non-ETH based chains + function supplyEraWallet(address addr, uint256 amount) public { + initializeConfig(); + + Utils.runL1L2Transaction( + hex"", + Utils.MAX_PRIORITY_TX_GAS, + amount, + new bytes[](0), + addr, + config.chainId, + config.bridgehub, + config.l1SharedBridge + ); + } + + function finalizeZkTokenWithdrawal( + uint256 _chainId, + uint256 _l2BatchNumber, + uint256 _l2MessageIndex, + uint16 _l2TxNumberInBatch, + bytes memory _message, + bytes32[] memory _merkleProof + ) public { + initializeConfig(); + + L1Nullifier l1Nullifier = L1Nullifier(config.l1Nullifier); + + vm.broadcast(); + l1Nullifier.finalizeDeposit( + FinalizeL1DepositParams({ + chainId: _chainId, + l2BatchNumber: _l2BatchNumber, + l2MessageIndex: _l2MessageIndex, + l2Sender: L2_ASSET_ROUTER_ADDR, + l2TxNumberInBatch: _l2TxNumberInBatch, + message: _message, + merkleProof: _merkleProof + }) + ); + } + + function saveL1Address() public { + initializeConfig(); + initializeAdditionalConfig(); + + string memory root = vm.projectRoot(); + string memory path = string.concat(root, vm.envString("ZK_TOKEN_OUTPUT")); + + string memory toml = vm.readFile(path); + + bytes32 zkTokenAssetId = toml.readBytes32("$.ZK.assetId"); + + L1AssetRouter l1AR = L1AssetRouter(config.l1SharedBridge); + console.log("L1 AR address", address(l1AR)); + IL1NativeTokenVault nativeTokenVault = IL1NativeTokenVault(address(l1AR.nativeTokenVault())); + address l1ZKAddress = nativeTokenVault.tokenAddress(zkTokenAssetId); + console.log("L1 ZK address", l1ZKAddress); + TestnetERC20Token l1ZK = TestnetERC20Token(l1ZKAddress); + // config.chainAdmin = address(0x31E624977B531BE0d88f6eF4D6588B1fc80c0762); + address[3] memory addressesForTransfers = [ + // config.anotherOwner, + // config.chainAdmin, + config.governance, + config.deployer, + config.owner + ]; + + uint256 balance = l1ZK.balanceOf(config.deployerAddress); + vm.startBroadcast(config.deployerAddress); + for (uint i = 0; i < addressesForTransfers.length; ++i) { + l1ZK.transfer(addressesForTransfers[i], balance / 10); + } + vm.stopBroadcast(); + string memory tokenInfo = vm.serializeAddress("ZK", "l1Address", l1ZKAddress); + vm.writeToml(tokenInfo, path, ".ZK.l1Address"); } function deployErc20( @@ -104,51 +241,53 @@ contract DeployZKScript is Script { uint256 mint, address[] storage additionalAddressesForMinting ) internal returns (address) { - // bytes memory args; - // // WETH9 constructor has no arguments - // if (keccak256(bytes(implementation)) != keccak256(bytes("WETH9.sol"))) { - // args = abi.encode(name, symbol, decimals); - // } + bytes memory args; + // WETH9 constructor has no arguments + if (keccak256(bytes(implementation)) != keccak256(bytes("WETH9.sol"))) { + args = abi.encode(name, symbol, decimals); + } - // bytes memory bytecode = abi.encodePacked(vm.getCode(implementation), args); + bytes memory bytecode = abi.encodePacked(vm.getCode(implementation), args); - vm.broadcast(); - TestnetERC20Token token = new TestnetERC20Token(name, symbol, uint8(decimals)); - address tokenAddress = address(token); - - // if (mint > 0) { - // vm.broadcast(); - // additionalAddressesForMinting.push(config.deployerAddress); - // uint256 addressMintListLength = additionalAddressesForMinting.length; - // for (uint256 i = 0; i < addressMintListLength; ++i) { - // (bool success, ) = tokenAddress.call( - // abi.encodeWithSignature("mint(address,uint256)", additionalAddressesForMinting[i], mint) - // ); - // if (!success) { - // revert MintFailed(); - // } - // console.log("Minting to:", additionalAddressesForMinting[i]); - // if (!success) { - // revert MintFailed(); - // } - // } - // } + address tokenAddress = address(new TestnetERC20Token(name, symbol, uint8(decimals))); // No salt for testing + + if (mint > 0) { + additionalAddressesForMinting.push(config.deployerAddress); + uint256 addressMintListLength = additionalAddressesForMinting.length; + for (uint256 i = 0; i < addressMintListLength; ++i) { + (bool success, ) = tokenAddress.call( + abi.encodeWithSignature("mint(address,uint256)", additionalAddressesForMinting[i], mint) + ); + if (!success) { + revert MintFailed(); + } + console.log("Minting to:", additionalAddressesForMinting[i]); + if (!success) { + revert MintFailed(); + } + } + } return tokenAddress; } function saveOutput() internal { TokenDescription memory token = config.zkToken; - vm.serializeString(token.symbol, "name", token.name); - vm.serializeString(token.symbol, "symbol", token.symbol); - vm.serializeUint(token.symbol, "decimals", token.decimals); - vm.serializeString(token.symbol, "implementation", token.implementation); - vm.serializeUintToHex(token.symbol, "mint", token.mint); - string memory tokenInfo = vm.serializeAddress(token.symbol, "address", token.addr); + string memory section = token.symbol; - string memory toml = vm.serializeString("root", "tokens", tokenInfo); + // Serialize each attribute directly under the token's symbol (e.g., [ZK]) + vm.serializeString(section, "name", token.name); + vm.serializeString(section, "symbol", token.symbol); + vm.serializeUint(section, "decimals", token.decimals); + vm.serializeString(section, "implementation", token.implementation); + vm.serializeUintToHex(section, "mint", token.mint); + vm.serializeBytes32(section, "assetId", token.assetId); + vm.serializeAddress(token.symbol, "l1Address", address(0)); + + string memory tokenInfo = vm.serializeAddress(token.symbol, "address", token.addr); + string memory toml = vm.serializeString("root", "ZK", tokenInfo); string memory root = vm.projectRoot(); - string memory path = string.concat(root, "/script-out/output-deploy-erc20.toml"); + string memory path = string.concat(root, vm.envString("ZK_TOKEN_OUTPUT")); vm.writeToml(toml, path); } From cb5be0b1d1e0f30dee02de9f62515a5a4dc32832 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 18 Oct 2024 10:43:33 +0100 Subject: [PATCH 28/60] merge issue --- .../contracts/bridge/asset-router/IL2AssetRouter.sol | 8 -------- 1 file changed, 8 deletions(-) diff --git a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol index 3c77299bd..4ac638d47 100644 --- a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol @@ -18,14 +18,6 @@ interface IL2AssetRouter { function withdrawLegacyBridge(address _l1Receiver, address _l2Token, uint256 _amount, address _sender) external; - function finalizeDepositLegacyBridge( - address _l1Sender, - address _l2Receiver, - address _l1Token, - uint256 _amount, - bytes calldata _data - ) external; - /// @dev Used to set the assetHandlerAddress for a given assetId. /// @dev Will be used by ZK Gateway function setAssetHandlerAddress(uint256 _originChainId, bytes32 _assetId, address _assetAddress) external; From 17b74644d1728532a209ff823244a95e14d9cba7 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Mon, 21 Oct 2024 15:00:54 +0400 Subject: [PATCH 29/60] (feat): refactored distribution of base ERC token --- .../contracts/bridge/ntv/NativeTokenVault.sol | 4 ++ .../DeployZKAndBridgeToL1.s.sol | 49 +++++++++-------- .../deploy-scripts/GatewayCTMFromL1.s.sol | 53 ++++++++++++++++++- .../deploy-scripts/GatewayPreparation.s.sol | 34 ++++++++++++ .../deploy-scripts/RegisterZKChain.s.sol | 20 ++++++- 5 files changed, 133 insertions(+), 27 deletions(-) diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 644669f18..69af68b3d 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -333,6 +333,10 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 /// @dev It does not perform any checks for the correctnesss of the token contract. /// @param _nativeToken The address of the token to be registered. function _unsafeRegisterNativeToken(address _nativeToken) internal { + bytes32 oldAssetId = assetId[_nativeToken]; + if (originChainId[oldAssetId] != block.chainid && originChainId[oldAssetId] != 0) { + return; + } bytes32 newAssetId = DataEncoding.encodeNTVAssetId(block.chainid, _nativeToken); ASSET_ROUTER.setAssetHandlerAddressThisChain(bytes32(uint256(uint160(_nativeToken))), address(this)); tokenAddress[newAssetId] = _nativeToken; diff --git a/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol b/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol index 2859269fd..a0388ab9b 100644 --- a/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol @@ -97,10 +97,10 @@ contract DeployZKScript is Script { config.chainId = toml.readUint("$.chain.chain_chain_id"); // Grab config from custom config file - path = string.concat(root, vm.envString("L2_CONFIG")); - toml = vm.readFile(path); - config.anotherOwner = toml.readAddress("$.consensus_registry_owner"); - console.log("Another owner: ", config.anotherOwner); + // path = string.concat(root, vm.envString("L2_CONFIG")); + // toml = vm.readFile(path); + // config.anotherOwner = toml.readAddress("$.consensus_registry_owner"); + // console.log("Another owner: ", config.anotherOwner); } function initializeAdditionalConfig() internal { @@ -111,8 +111,8 @@ contract DeployZKScript is Script { string memory path = string.concat(root, vm.envString("L1_OUTPUT")); string memory toml = vm.readFile(path); - config.governance = toml.readAddress("$.deployed_addresses.governance_addr"); - config.deployer = toml.readAddress("$.deployer_addr"); + // config.governance = toml.readAddress("$.deployed_addresses.governance_addr"); + // config.deployer = toml.readAddress("$.deployer_addr"); config.owner = toml.readAddress("$.owner_address"); } @@ -134,7 +134,7 @@ contract DeployZKScript is Script { token.addr = zkTokenAddress; address deployer = msg.sender; TestnetERC20Token zkToken = TestnetERC20Token(zkTokenAddress); - zkToken.mint(deployer, amount); + zkToken.mint(deployer, amount * 1000000000000); uint256 deployerBalance = zkToken.balanceOf(deployer); console.log("Deployed balance:", deployerBalance); @@ -144,11 +144,11 @@ contract DeployZKScript is Script { bytes32 zkTokenAssetId = l2NTV.assetId(zkTokenAddress); config.zkToken.assetId = zkTokenAssetId; console.log("zkTokenAssetId:", uint256(zkTokenAssetId)); - zkToken.approve(L2_NATIVE_TOKEN_VAULT_ADDR, amount); + zkToken.approve(L2_NATIVE_TOKEN_VAULT_ADDR, amount * 1000000000000); vm.stopBroadcast(); vm.broadcast(); - l2AR.withdraw(zkTokenAssetId, abi.encode(amount, deployer)); + l2AR.withdraw(zkTokenAssetId, abi.encode(amount * 1000000000000, deployer)); uint256 deployerBalanceAfterWithdraw = zkToken.balanceOf(deployer); @@ -214,21 +214,24 @@ contract DeployZKScript is Script { address l1ZKAddress = nativeTokenVault.tokenAddress(zkTokenAssetId); console.log("L1 ZK address", l1ZKAddress); TestnetERC20Token l1ZK = TestnetERC20Token(l1ZKAddress); - // config.chainAdmin = address(0x31E624977B531BE0d88f6eF4D6588B1fc80c0762); - address[3] memory addressesForTransfers = [ - // config.anotherOwner, - // config.chainAdmin, - config.governance, - config.deployer, - config.owner - ]; uint256 balance = l1ZK.balanceOf(config.deployerAddress); - vm.startBroadcast(config.deployerAddress); - for (uint i = 0; i < addressesForTransfers.length; ++i) { - l1ZK.transfer(addressesForTransfers[i], balance / 10); - } - vm.stopBroadcast(); + vm.broadcast(); + l1ZK.transfer(config.owner, balance / 2); + // config.chainAdmin = address(0x31E624977B531BE0d88f6eF4D6588B1fc80c0762); + // address[3] memory addressesForTransfers = [ + // // config.anotherOwner, + // // config.chainAdmin, + // config.governance, + // config.deployer, + // config.owner + // ]; + + // vm.startBroadcast(config.deployerAddress); + // for (uint i = 0; i < addressesForTransfers.length; ++i) { + // l1ZK.transfer(addressesForTransfers[i], balance / 4); + // } + // vm.stopBroadcast(); string memory tokenInfo = vm.serializeAddress("ZK", "l1Address", l1ZKAddress); vm.writeToml(tokenInfo, path, ".ZK.l1Address"); } @@ -283,7 +286,7 @@ contract DeployZKScript is Script { vm.serializeUintToHex(section, "mint", token.mint); vm.serializeBytes32(section, "assetId", token.assetId); vm.serializeAddress(token.symbol, "l1Address", address(0)); - + string memory tokenInfo = vm.serializeAddress(token.symbol, "address", token.addr); string memory toml = vm.serializeString("root", "ZK", tokenInfo); string memory root = vm.projectRoot(); diff --git a/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol b/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol index 6f17c234c..d3c88b6f8 100644 --- a/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol +++ b/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol @@ -7,6 +7,10 @@ import {Script, console2 as console} from "forge-std/Script.sol"; // import {Vm} from "forge-std/Vm.sol"; import {stdToml} from "forge-std/StdToml.sol"; +// It's required to disable lints to force the compiler to compile the contracts +// solhint-disable no-unused-import +import {TestnetERC20Token} from "contracts/dev-contracts/TestnetERC20Token.sol"; + import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; @@ -16,6 +20,8 @@ import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol import {StateTransitionDeployedAddresses, Utils, L2_BRIDGEHUB_ADDRESS} from "./Utils.sol"; import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; import {L2ContractsBytecodesLib} from "./L2ContractsBytecodesLib.sol"; +import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; +import {IL1NativeTokenVault} from "contracts/bridge/ntv/IL1NativeTokenVault.sol"; import {AdminFacet} from "contracts/state-transition/chain-deps/facets/Admin.sol"; import {ExecutorFacet} from "contracts/state-transition/chain-deps/facets/Executor.sol"; @@ -55,6 +61,9 @@ contract GatewayCTMFromL1 is Script { address chainTypeManagerProxy; address sharedBridgeProxy; address governance; + address governanceAddr; + address deployerAddr; + address baseToken; uint256 chainChainId; uint256 eraChainId; uint256 l1ChainId; @@ -93,6 +102,9 @@ contract GatewayCTMFromL1 is Script { console.log("Setting up the Gateway script"); initializeConfig(); + if (config.baseToken != ADDRESS_ONE) { + distributeBaseToken(); + } deployGatewayContracts(); saveOutput(); @@ -137,8 +149,44 @@ contract GatewayCTMFromL1 is Script { genesisRollupLeafIndex: toml.readUint("$.genesis_rollup_leaf_index"), genesisBatchCommitment: toml.readBytes32("$.genesis_batch_commitment"), latestProtocolVersion: toml.readUint("$.latest_protocol_version"), - forceDeploymentsData: toml.readBytes("$.force_deployments_data") + forceDeploymentsData: toml.readBytes("$.force_deployments_data"), + governanceAddr: address(0), + deployerAddr: address(0), + baseToken: address(0) }); + + path = string.concat(root, vm.envString("L1_OUTPUT")); + toml = vm.readFile(path); + + config.governanceAddr = toml.readAddress("$.deployed_addresses.governance_addr"); + config.deployerAddr = toml.readAddress("$.deployer_addr"); + + path = string.concat(root, vm.envString("ZK_CHAIN_CONFIG")); + toml = vm.readFile(path); + config.baseToken = toml.readAddress("$.chain.base_token_addr"); + } + + function distributeBaseToken() internal { + deployerAddress = msg.sender; + console.log("Deployer address: ", deployerAddress); + console.log("Gornance address: ", config.governance); + + L1AssetRouter l1AR = L1AssetRouter(config.sharedBridgeProxy); + console.log("L1 AR address", address(l1AR)); + IL1NativeTokenVault nativeTokenVault = IL1NativeTokenVault(address(l1AR.nativeTokenVault())); + bytes32 baseTokenAssetID = nativeTokenVault.assetId(config.baseToken); + uint256 baseTokenOriginChainId = nativeTokenVault.originChainId(baseTokenAssetID); + + TestnetERC20Token baseToken = TestnetERC20Token(config.baseToken); + uint256 deployerBalance = baseToken.balanceOf(deployerAddress); + console.log("Base token origin id: ", baseTokenOriginChainId); + vm.startBroadcast(); + if (baseTokenOriginChainId == block.chainid) { + baseToken.mint(config.governanceAddr, deployerBalance / 3); + } else { + baseToken.transfer(config.governanceAddr, deployerBalance / 3); + } + vm.stopBroadcast(); } function saveOutput() internal { @@ -202,8 +250,9 @@ contract GatewayCTMFromL1 is Script { /// @dev The sender may not have any privileges function deployGatewayContracts() public { + console.log("Step 1"); output.multicall3 = _deployInternal(L2ContractsBytecodesLib.readMulticall3Bytecode(), hex""); - + console.log("Step 2"); deployGatewayFacets(); output.gatewayStateTransition.verifier = deployGatewayVerifier(); diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol index 212b96055..23fe8bb18 100644 --- a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -7,6 +7,10 @@ import {Script, console2 as console} from "forge-std/Script.sol"; // import {Vm} from "forge-std/Vm.sol"; import {stdToml} from "forge-std/StdToml.sol"; +// It's required to disable lints to force the compiler to compile the contracts +// solhint-disable no-unused-import +import {TestnetERC20Token} from "contracts/dev-contracts/TestnetERC20Token.sol"; + import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.sol"; import {IBridgehub, BridgehubBurnCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; @@ -24,9 +28,13 @@ import {SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION} from "contracts/bridge/a import {CTM_DEPLOYMENT_TRACKER_ENCODING_VERSION} from "contracts/bridgehub/CTMDeploymentTracker.sol"; import {L2AssetRouter, IL2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; +import {IL1NativeTokenVault} from "contracts/bridge/ntv/IL1NativeTokenVault.sol"; import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; import {L2_ASSET_ROUTER_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; +import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; import {FinalizeL1DepositParams} from "contracts/bridge/interfaces/IL1Nullifier.sol"; @@ -246,6 +254,32 @@ contract GatewayPreparation is Script { function migrateChainToGateway(address chainAdmin, address accessControlRestriction, uint256 chainId) public { initializeConfig(); + IBridgehub bridgehubContract = IBridgehub(config.bridgehub); + bytes32 gatewayBaseTokenAssetId = bridgehubContract.baseTokenAssetId(config.gatewayChainId); + bytes32 ethTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, ETH_TOKEN_ADDRESS); + + // Fund chain admin with tokens + if (gatewayBaseTokenAssetId != ethTokenAssetId) { + deployerAddress = msg.sender; + L1AssetRouter l1AR = L1AssetRouter(config.sharedBridgeProxy); + IL1NativeTokenVault nativeTokenVault = IL1NativeTokenVault(address(l1AR.nativeTokenVault())); + address baseTokenAddress = nativeTokenVault.tokenAddress(gatewayBaseTokenAssetId); + uint256 baseTokenOriginChainId = nativeTokenVault.originChainId(gatewayBaseTokenAssetId); + + TestnetERC20Token baseToken = TestnetERC20Token(baseTokenAddress); + uint256 deployerBalance = baseToken.balanceOf(deployerAddress); + console.log("Base token origin id: ", baseTokenOriginChainId); + vm.startBroadcast(); + if (baseTokenOriginChainId == block.chainid) { + baseToken.mint(chainAdmin, deployerBalance / 3); + } else { + baseToken.transfer(chainAdmin, deployerBalance / 3); + } + vm.stopBroadcast(); + } + + console.log("Chain Admin address:", chainAdmin ); + // TODO(EVM-746): Use L2-based chain admin contract address l2ChainAdmin = AddressAliasHelper.applyL1ToL2Alias(chainAdmin); diff --git a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol index b1c8ae292..fbcccf3cb 100644 --- a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol +++ b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol @@ -288,7 +288,8 @@ contract RegisterZKChainScript is Script { function registerAssetIdOnBridgehub() internal { IBridgehub bridgehub = IBridgehub(config.bridgehub); Ownable ownable = Ownable(config.bridgehub); - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, config.baseToken); + // bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, config.baseToken); + bytes32 baseTokenAssetId = 0x83d26252627b75f1b0828354775d22cfcdb838fa9a77a210f2031d4921b32e0e; if (bridgehub.assetIdIsRegistered(baseTokenAssetId)) { console.log("Base token asset id already registered on Bridgehub"); @@ -309,7 +310,22 @@ contract RegisterZKChainScript is Script { function registerTokenOnNTV() internal { INativeTokenVault ntv = INativeTokenVault(config.nativeTokenVault); // Ownable ownable = Ownable(config.nativeTokenVault); - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, config.baseToken); + bytes32 baseTokenAssetId = ntv.assetId(config.baseToken); + uint256 baseTokenOriginChain = ntv.originChainId(baseTokenAssetId); + + // If it hasn't been registered alreadt with ntv + if (baseTokenAssetId == bytes32(0)) { + baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, config.baseToken); + } + // bytes32 calculatedBaseTokenAssetId = DataEncoding.encodeAssetId(baseTokenOriginChain, address(ntv), config.baseToken); + // bytes32 calculatedBaseTokenAssetId = DataEncoding.encodeNTVAssetId(baseTokenOriginChain, config.baseToken); + // bytes32 baseTokenAssetId = 0x83d26252627b75f1b0828354775d22cfcdb838fa9a77a210f2031d4921b32e0e; + console.log("Base token origin chain id: ", baseTokenOriginChain); + console.log("Stored base token asset id: "); + console.logBytes32(baseTokenAssetId); + // console.log("Calculated base token asset id: "); + // console.logBytes32(calculatedBaseTokenAssetId); + // require(baseTokenAssetId == calculatedBaseTokenAssetId, "Mismatch between calculated and stored base token asset id"); config.baseTokenAssetId = baseTokenAssetId; if (ntv.tokenAddress(baseTokenAssetId) != address(0) || config.baseToken == ETH_TOKEN_ADDRESS) { console.log("Token already registered on NTV"); From d5316d93971fe4c396239878ae698eb4d64e1b1f Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Mon, 21 Oct 2024 16:21:05 +0400 Subject: [PATCH 30/60] (fix): formatting --- .../contracts/bridge/ntv/NativeTokenVault.sol | 3 +- .../DeployZKAndBridgeToL1.s.sol | 44 +++---------------- .../deploy-scripts/GatewayCTMFromL1.s.sol | 11 +---- .../deploy-scripts/GatewayPreparation.s.sol | 6 +-- .../deploy-scripts/RegisterZKChain.s.sol | 13 +----- 5 files changed, 14 insertions(+), 63 deletions(-) diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 69af68b3d..91d8bf439 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -333,8 +333,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 /// @dev It does not perform any checks for the correctnesss of the token contract. /// @param _nativeToken The address of the token to be registered. function _unsafeRegisterNativeToken(address _nativeToken) internal { - bytes32 oldAssetId = assetId[_nativeToken]; - if (originChainId[oldAssetId] != block.chainid && originChainId[oldAssetId] != 0) { + if (assetId[_nativeToken] != bytes32(0)) { return; } bytes32 newAssetId = DataEncoding.encodeNTVAssetId(block.chainid, _nativeToken); diff --git a/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol b/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol index a0388ab9b..8f48f8ba9 100644 --- a/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol @@ -57,8 +57,6 @@ contract DeployZKScript is Script { Config internal config; function run() public { - console.log("Deploying ZK Token"); - initializeConfig(); deployZkToken(); saveOutput(); @@ -95,32 +93,21 @@ contract DeployZKScript is Script { config.l1SharedBridge = toml.readAddress("$.deployed_addresses.bridges.shared_bridge_proxy_addr"); config.l1Nullifier = toml.readAddress("$.deployed_addresses.bridges.l1_nullifier_proxy_addr"); config.chainId = toml.readUint("$.chain.chain_chain_id"); - - // Grab config from custom config file - // path = string.concat(root, vm.envString("L2_CONFIG")); - // toml = vm.readFile(path); - // config.anotherOwner = toml.readAddress("$.consensus_registry_owner"); - // console.log("Another owner: ", config.anotherOwner); } function initializeAdditionalConfig() internal { string memory root = vm.projectRoot(); - // string memory path = string.concat(root, vm.envString("ZK_CHAIN_OUTPUT")); - // string memory toml = vm.readFile(path); - // config.chainAdmin = toml.readAddress("$.chain_admin_addr"); - string memory path = string.concat(root, vm.envString("L1_OUTPUT")); string memory toml = vm.readFile(path); - // config.governance = toml.readAddress("$.deployed_addresses.governance_addr"); - // config.deployer = toml.readAddress("$.deployer_addr"); + config.owner = toml.readAddress("$.owner_address"); } function deployZkToken() internal { - uint256 amount = 90000000000000000000; - + uint256 someBigAmount = 100000000000000000000000000000000; TokenDescription storage token = config.zkToken; console.log("Deploying token:", token.name); + vm.startBroadcast(); address zkTokenAddress = deployErc20({ name: token.name, @@ -134,24 +121,21 @@ contract DeployZKScript is Script { token.addr = zkTokenAddress; address deployer = msg.sender; TestnetERC20Token zkToken = TestnetERC20Token(zkTokenAddress); - zkToken.mint(deployer, amount * 1000000000000); + zkToken.mint(deployer, someBigAmount); uint256 deployerBalance = zkToken.balanceOf(deployer); - console.log("Deployed balance:", deployerBalance); - + console.log("Deployer balance:", deployerBalance); L2AssetRouter l2AR = L2AssetRouter(L2_ASSET_ROUTER_ADDR); L2NativeTokenVault l2NTV = L2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR); l2NTV.registerToken(zkTokenAddress); bytes32 zkTokenAssetId = l2NTV.assetId(zkTokenAddress); config.zkToken.assetId = zkTokenAssetId; console.log("zkTokenAssetId:", uint256(zkTokenAssetId)); - zkToken.approve(L2_NATIVE_TOKEN_VAULT_ADDR, amount * 1000000000000); + zkToken.approve(L2_NATIVE_TOKEN_VAULT_ADDR, someBigAmount); vm.stopBroadcast(); vm.broadcast(); - l2AR.withdraw(zkTokenAssetId, abi.encode(amount * 1000000000000, deployer)); - + l2AR.withdraw(zkTokenAssetId, abi.encode(someBigAmount, deployer)); uint256 deployerBalanceAfterWithdraw = zkToken.balanceOf(deployer); - console.log("Deployed balance after withdraw:", deployerBalanceAfterWithdraw); } @@ -218,20 +202,6 @@ contract DeployZKScript is Script { uint256 balance = l1ZK.balanceOf(config.deployerAddress); vm.broadcast(); l1ZK.transfer(config.owner, balance / 2); - // config.chainAdmin = address(0x31E624977B531BE0d88f6eF4D6588B1fc80c0762); - // address[3] memory addressesForTransfers = [ - // // config.anotherOwner, - // // config.chainAdmin, - // config.governance, - // config.deployer, - // config.owner - // ]; - - // vm.startBroadcast(config.deployerAddress); - // for (uint i = 0; i < addressesForTransfers.length; ++i) { - // l1ZK.transfer(addressesForTransfers[i], balance / 4); - // } - // vm.stopBroadcast(); string memory tokenInfo = vm.serializeAddress("ZK", "l1Address", l1ZKAddress); vm.writeToml(tokenInfo, path, ".ZK.l1Address"); } diff --git a/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol b/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol index d3c88b6f8..07b1bfb68 100644 --- a/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol +++ b/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol @@ -168,20 +168,15 @@ contract GatewayCTMFromL1 is Script { function distributeBaseToken() internal { deployerAddress = msg.sender; - console.log("Deployer address: ", deployerAddress); - console.log("Gornance address: ", config.governance); - L1AssetRouter l1AR = L1AssetRouter(config.sharedBridgeProxy); - console.log("L1 AR address", address(l1AR)); IL1NativeTokenVault nativeTokenVault = IL1NativeTokenVault(address(l1AR.nativeTokenVault())); bytes32 baseTokenAssetID = nativeTokenVault.assetId(config.baseToken); uint256 baseTokenOriginChainId = nativeTokenVault.originChainId(baseTokenAssetID); - TestnetERC20Token baseToken = TestnetERC20Token(config.baseToken); uint256 deployerBalance = baseToken.balanceOf(deployerAddress); - console.log("Base token origin id: ", baseTokenOriginChainId); + vm.startBroadcast(); - if (baseTokenOriginChainId == block.chainid) { + if (baseTokenOriginChainId == block.chainid) { baseToken.mint(config.governanceAddr, deployerBalance / 3); } else { baseToken.transfer(config.governanceAddr, deployerBalance / 3); @@ -250,9 +245,7 @@ contract GatewayCTMFromL1 is Script { /// @dev The sender may not have any privileges function deployGatewayContracts() public { - console.log("Step 1"); output.multicall3 = _deployInternal(L2ContractsBytecodesLib.readMulticall3Bytecode(), hex""); - console.log("Step 2"); deployGatewayFacets(); output.gatewayStateTransition.verifier = deployGatewayVerifier(); diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol index 23fe8bb18..ea4b6ebea 100644 --- a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -265,12 +265,12 @@ contract GatewayPreparation is Script { IL1NativeTokenVault nativeTokenVault = IL1NativeTokenVault(address(l1AR.nativeTokenVault())); address baseTokenAddress = nativeTokenVault.tokenAddress(gatewayBaseTokenAssetId); uint256 baseTokenOriginChainId = nativeTokenVault.originChainId(gatewayBaseTokenAssetId); - TestnetERC20Token baseToken = TestnetERC20Token(baseTokenAddress); uint256 deployerBalance = baseToken.balanceOf(deployerAddress); console.log("Base token origin id: ", baseTokenOriginChainId); + vm.startBroadcast(); - if (baseTokenOriginChainId == block.chainid) { + if (baseTokenOriginChainId == block.chainid) { baseToken.mint(chainAdmin, deployerBalance / 3); } else { baseToken.transfer(chainAdmin, deployerBalance / 3); @@ -278,7 +278,7 @@ contract GatewayPreparation is Script { vm.stopBroadcast(); } - console.log("Chain Admin address:", chainAdmin ); + console.log("Chain Admin address:", chainAdmin); // TODO(EVM-746): Use L2-based chain admin contract address l2ChainAdmin = AddressAliasHelper.applyL1ToL2Alias(chainAdmin); diff --git a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol index fbcccf3cb..289ba4f20 100644 --- a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol +++ b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol @@ -309,28 +309,17 @@ contract RegisterZKChainScript is Script { function registerTokenOnNTV() internal { INativeTokenVault ntv = INativeTokenVault(config.nativeTokenVault); - // Ownable ownable = Ownable(config.nativeTokenVault); bytes32 baseTokenAssetId = ntv.assetId(config.baseToken); uint256 baseTokenOriginChain = ntv.originChainId(baseTokenAssetId); - // If it hasn't been registered alreadt with ntv + // If it hasn't been registered already with ntv if (baseTokenAssetId == bytes32(0)) { baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, config.baseToken); } - // bytes32 calculatedBaseTokenAssetId = DataEncoding.encodeAssetId(baseTokenOriginChain, address(ntv), config.baseToken); - // bytes32 calculatedBaseTokenAssetId = DataEncoding.encodeNTVAssetId(baseTokenOriginChain, config.baseToken); - // bytes32 baseTokenAssetId = 0x83d26252627b75f1b0828354775d22cfcdb838fa9a77a210f2031d4921b32e0e; - console.log("Base token origin chain id: ", baseTokenOriginChain); - console.log("Stored base token asset id: "); - console.logBytes32(baseTokenAssetId); - // console.log("Calculated base token asset id: "); - // console.logBytes32(calculatedBaseTokenAssetId); - // require(baseTokenAssetId == calculatedBaseTokenAssetId, "Mismatch between calculated and stored base token asset id"); config.baseTokenAssetId = baseTokenAssetId; if (ntv.tokenAddress(baseTokenAssetId) != address(0) || config.baseToken == ETH_TOKEN_ADDRESS) { console.log("Token already registered on NTV"); } else { - // bytes memory data = abi.encodeCall(ntv.registerToken, (config.baseToken)); vm.broadcast(); ntv.registerToken(config.baseToken); console.log("Token registered on NTV"); From ae5b8c1b78ddb91d84fa2be121d1779d2b36708e Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Tue, 22 Oct 2024 01:24:31 +0400 Subject: [PATCH 31/60] (fix): small fixes --- l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol | 2 +- l1-contracts/deploy-scripts/RegisterZKChain.s.sol | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 91d8bf439..6bdd99950 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -337,10 +337,10 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 return; } bytes32 newAssetId = DataEncoding.encodeNTVAssetId(block.chainid, _nativeToken); - ASSET_ROUTER.setAssetHandlerAddressThisChain(bytes32(uint256(uint160(_nativeToken))), address(this)); tokenAddress[newAssetId] = _nativeToken; assetId[_nativeToken] = newAssetId; originChainId[newAssetId] = block.chainid; + ASSET_ROUTER.setAssetHandlerAddressThisChain(bytes32(uint256(uint160(_nativeToken))), address(this)); } function _handleChainBalanceIncrease( diff --git a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol index 289ba4f20..6551ac821 100644 --- a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol +++ b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol @@ -288,8 +288,13 @@ contract RegisterZKChainScript is Script { function registerAssetIdOnBridgehub() internal { IBridgehub bridgehub = IBridgehub(config.bridgehub); Ownable ownable = Ownable(config.bridgehub); - // bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, config.baseToken); - bytes32 baseTokenAssetId = 0x83d26252627b75f1b0828354775d22cfcdb838fa9a77a210f2031d4921b32e0e; + INativeTokenVault ntv = INativeTokenVault(config.nativeTokenVault); + bytes32 baseTokenAssetId = ntv.assetId(config.baseToken); + uint256 baseTokenOriginChain = ntv.originChainId(baseTokenAssetId); + + if (baseTokenAssetId == bytes32(0)) { + baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, config.baseToken); + } if (bridgehub.assetIdIsRegistered(baseTokenAssetId)) { console.log("Base token asset id already registered on Bridgehub"); From 1adcdf320652268fb453d2776a5ef227eede19d3 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 22 Oct 2024 11:41:27 +0100 Subject: [PATCH 32/60] typos, wrong comments --- l1-contracts/contracts/bridge/L1Nullifier.sol | 4 ++-- l1-contracts/contracts/bridgehub/MessageRoot.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index a4e7ce69c..ff170a9c2 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -408,9 +408,9 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, } isWithdrawalFinalized[chainId][l2BatchNumber][l2MessageIndex] = true; - // Handling special case for withdrawal from ZKsync Era initiated before Shared Bridge. (bytes32 assetId, bytes memory transferData) = _verifyWithdrawal(_finalizeWithdrawalParams); - + + // Handling special case for withdrawal from ZKsync Era initiated before Shared Bridge. if (_isPreSharedBridgeEraEthWithdrawal(chainId, l2BatchNumber)) { // Checks that the withdrawal wasn't finalized already. bool alreadyFinalized = IGetters(ERA_DIAMOND_PROXY).isEthWithdrawalFinalized(l2BatchNumber, l2MessageIndex); diff --git a/l1-contracts/contracts/bridgehub/MessageRoot.sol b/l1-contracts/contracts/bridgehub/MessageRoot.sol index 69613359e..982651a16 100644 --- a/l1-contracts/contracts/bridgehub/MessageRoot.sol +++ b/l1-contracts/contracts/bridgehub/MessageRoot.sol @@ -21,7 +21,7 @@ bytes32 constant CHAIN_TREE_EMPTY_ENTRY_HASH = bytes32( 0x46700b4d40ac5c35af2c22dda2787a91eb567b06c924a8fb8ae9a05b20c08c21 ); -// The single shared tree consists of the roots of chain trees as its. We use hash of "new bytes(96)" as the hash of an empty leaf. +// The single shared tree consists of the roots of chain trees as its leaves. We use hash of "new bytes(96)" as the hash of an empty leaf. bytes32 constant SHARED_ROOT_TREE_EMPTY_HASH = bytes32( 0x46700b4d40ac5c35af2c22dda2787a91eb567b06c924a8fb8ae9a05b20c08c21 ); From 5ddfdfff4f18d2ac8a2904ddb5bdd862b61bff15 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 22 Oct 2024 11:42:44 +0100 Subject: [PATCH 33/60] ShB -> AR --- l1-contracts/contracts/bridgehub/Bridgehub.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 92b31eceb..96bd2888c 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -437,7 +437,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// this assumes that either ether is the base token or /// the msg.sender has approved mintValue allowance for the nativeTokenVault. /// This means this is not ideal for contract calls, as the contract would have to handle token allowance of the base Token. - /// In case allowance is provided to the Shared Bridge, then it will be transferred to NTV. + /// In case allowance is provided to the Asset Router, then it will be transferred to NTV. function requestL2TransactionDirect( L2TransactionRequestDirect calldata _request ) external payable override nonReentrant whenNotPaused onlyL1 returns (bytes32 canonicalTxHash) { From a6b9f41df5daf35860144fdc506ec480610b208a Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 22 Oct 2024 11:50:56 +0100 Subject: [PATCH 34/60] encodeNTVassetId based on bytes32 removed --- l1-contracts/contracts/common/libraries/DataEncoding.sol | 8 -------- 1 file changed, 8 deletions(-) diff --git a/l1-contracts/contracts/common/libraries/DataEncoding.sol b/l1-contracts/contracts/common/libraries/DataEncoding.sol index 9df83d67a..3aa8d3c47 100644 --- a/l1-contracts/contracts/common/libraries/DataEncoding.sol +++ b/l1-contracts/contracts/common/libraries/DataEncoding.sol @@ -75,14 +75,6 @@ library DataEncoding { return keccak256(abi.encode(_chainId, _sender, _tokenAaddress)); } - /// @notice Encodes the asset data by combining chain id, NTV as asset deployment tracker and asset data. - /// @param _chainId The id of the chain token is native to. - /// @param _assetData The asset data that has to be encoded. - /// @return The encoded asset data. - function encodeNTVAssetId(uint256 _chainId, bytes32 _assetData) internal pure returns (bytes32) { - return keccak256(abi.encode(_chainId, L2_NATIVE_TOKEN_VAULT_ADDR, _assetData)); - } - /// @notice Encodes the asset data by combining chain id, NTV as asset deployment tracker and asset data. /// @param _chainId The id of the chain token is native to. /// @param _tokenAddress The address of token that has to be encoded (asset data is the address itself). From 3ddb184685fcba6f8f8c4ec29bb6ce248d3af213 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 22 Oct 2024 12:09:16 +0100 Subject: [PATCH 35/60] remove virtualSettlementLayer address --- l1-contracts/contracts/bridgehub/Bridgehub.sol | 6 ++---- l1-contracts/contracts/common/Config.sol | 5 ----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index bbc296229..901b95091 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -18,7 +18,7 @@ import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; import {DataEncoding} from "../common/libraries/DataEncoding.sol"; import {IZKChain} from "../state-transition/chain-interfaces/IZKChain.sol"; -import {ETH_TOKEN_ADDRESS, TWO_BRIDGES_MAGIC_VALUE, BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER, L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS} from "../common/Config.sol"; +import {ETH_TOKEN_ADDRESS, TWO_BRIDGES_MAGIC_VALUE, BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "../common/Config.sol"; import {BridgehubL2TransactionRequest, L2Message, L2Log, TxStatus} from "../common/Messaging.sol"; import {AddressAliasHelper} from "../vendor/AddressAliasHelper.sol"; import {IMessageRoot} from "./IMessageRoot.sol"; @@ -700,9 +700,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus bridgehubData.ctmData ); bytes memory chainMintData = IZKChain(zkChain).forwardedBridgeBurn( - _settlementChainId == L1_CHAIN_ID - ? L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS - : zkChainMap.get(_settlementChainId), + zkChainMap.get(_settlementChainId), _originalCaller, bridgehubData.chainData ); diff --git a/l1-contracts/contracts/common/Config.sol b/l1-contracts/contracts/common/Config.sol index a1e58f464..beebcd00c 100644 --- a/l1-contracts/contracts/common/Config.sol +++ b/l1-contracts/contracts/common/Config.sol @@ -120,11 +120,6 @@ address constant SETTLEMENT_LAYER_RELAY_SENDER = address(uint160(0x1111111111111 /// @dev The metadata version that is supported by the ZK Chains to prove that an L2->L1 log was included in a batch. uint256 constant SUPPORTED_PROOF_METADATA_VERSION = 1; -/// @dev The virtual address of the L1 settlement layer. -address constant L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS = address( - uint160(uint256(keccak256("L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS")) - 1) -); - struct PriorityTreeCommitment { uint256 nextLeafIndex; uint256 startIndex; From a2d357b19fefc505bf4cc93cd0fb6d0d817844a1 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 22 Oct 2024 12:22:40 +0100 Subject: [PATCH 36/60] setAssetHandlerAddress on BH renamed --- l1-contracts/contracts/bridgehub/Bridgehub.sol | 3 +-- l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol | 4 ++-- l1-contracts/contracts/bridgehub/IBridgehub.sol | 2 +- .../l1/unit/concrete/Governance/PermanentRestriction.t.sol | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 204844e57..d50faa487 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -297,7 +297,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @dev Used to set the assetAddress for a given assetInfo. /// @param _additionalData the additional data to identify the asset /// @param _assetAddress the asset handler address - function setAssetHandlerAddress(bytes32 _additionalData, address _assetAddress) external { + function setCTMAssetAddress(bytes32 _additionalData, address _assetAddress) external { // It is a simplified version of the logic used by the AssetRouter to manage asset handlers. // CTM's assetId is `keccak256(abi.encode(L1_CHAIN_ID, l1CtmDeployer, ctmAddress))`. // And the l1CtmDeployer is considered the deployment tracker for the CTM asset. @@ -320,7 +320,6 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus bytes32 ctmAssetId = keccak256(abi.encode(L1_CHAIN_ID, sender, _additionalData)); ctmAssetIdToAddress[ctmAssetId] = _assetAddress; - ctmAssetIdFromAddress[_assetAddress] = ctmAssetId; emit AssetRegistered(ctmAssetId, _assetAddress, _additionalData, msg.sender); } diff --git a/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol b/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol index 6ffee2482..8acc6948d 100644 --- a/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol +++ b/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol @@ -63,7 +63,7 @@ contract CTMDeploymentTracker is ICTMDeploymentTracker, ReentrancyGuard, Ownable require(BRIDGE_HUB.chainTypeManagerIsRegistered(_ctmAddress), "CTMDT: ctm not registered"); L1_ASSET_ROUTER.setAssetHandlerAddressThisChain(bytes32(uint256(uint160(_ctmAddress))), address(BRIDGE_HUB)); - BRIDGE_HUB.setAssetHandlerAddress(bytes32(uint256(uint160(_ctmAddress))), _ctmAddress); + BRIDGE_HUB.setCTMAssetAddress(bytes32(uint256(uint160(_ctmAddress))), _ctmAddress); } /// @notice The function responsible for registering the L2 counterpart of an CTM asset on the L2 Bridgehub. @@ -128,7 +128,7 @@ contract CTMDeploymentTracker is ICTMDeploymentTracker, ReentrancyGuard, Ownable address _ctmL2Address ) internal pure returns (L2TransactionRequestTwoBridgesInner memory request) { bytes memory l2TxCalldata = abi.encodeCall( - IBridgehub.setAssetHandlerAddress, + IBridgehub.setCTMAssetAddress, (bytes32(uint256(uint160(_ctmL1Address))), _ctmL2Address) ); diff --git a/l1-contracts/contracts/bridgehub/IBridgehub.sol b/l1-contracts/contracts/bridgehub/IBridgehub.sol index f5732b98e..188633fdf 100644 --- a/l1-contracts/contracts/bridgehub/IBridgehub.sol +++ b/l1-contracts/contracts/bridgehub/IBridgehub.sol @@ -221,7 +221,7 @@ interface IBridgehub is IAssetHandler, IL1AssetHandler { function ctmAssetIdToAddress(bytes32 _assetInfo) external view returns (address); - function setAssetHandlerAddress(bytes32 _additionalData, address _assetAddress) external; + function setCTMAssetAddress(bytes32 _additionalData, address _assetAddress) external; function L1_CHAIN_ID() external view returns (uint256); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index bcfe6ae2c..0904f8d5b 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -362,7 +362,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { // ctm deployer address is 0 in this test vm.startPrank(address(0)); - bridgehub.setAssetHandlerAddress( + bridgehub.setCTMAssetAddress( bytes32(uint256(uint160(address(chainContractAddress)))), address(chainContractAddress) ); From d73916805b517f4fa7668ec67030b5fb587b9a4f Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Tue, 22 Oct 2024 16:57:37 +0400 Subject: [PATCH 37/60] (fix): revert with error if duplicate asset id registration is attempted --- l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 6bdd99950..34bb695e8 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -92,6 +92,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 revert TokenNotSupported(WETH_TOKEN); } require(_nativeToken.code.length > 0, "NTV: empty token"); + require(assetId[_nativeToken] == bytes32(0), "NTV: asset id already registered"); _unsafeRegisterNativeToken(_nativeToken); } @@ -333,9 +334,6 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 /// @dev It does not perform any checks for the correctnesss of the token contract. /// @param _nativeToken The address of the token to be registered. function _unsafeRegisterNativeToken(address _nativeToken) internal { - if (assetId[_nativeToken] != bytes32(0)) { - return; - } bytes32 newAssetId = DataEncoding.encodeNTVAssetId(block.chainid, _nativeToken); tokenAddress[newAssetId] = _nativeToken; assetId[_nativeToken] = newAssetId; From 7f515e08c99e7ddee9f9a6c38ee230f7f87a0cb1 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Tue, 22 Oct 2024 18:22:45 +0400 Subject: [PATCH 38/60] (fix): cleanup the deploy zk script --- .../DeployZKAndBridgeToL1.s.sol | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol b/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol index 8f48f8ba9..853267cbe 100644 --- a/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol @@ -113,7 +113,6 @@ contract DeployZKScript is Script { name: token.name, symbol: token.symbol, decimals: token.decimals, - implementation: token.implementation, mint: token.mint, additionalAddressesForMinting: config.additionalAddressesForMinting }); @@ -153,6 +152,18 @@ contract DeployZKScript is Script { config.bridgehub, config.l1SharedBridge ); + // bytes32 l2TxHash = Utils.runGovernanceL1L2DirectTransaction( + // Utils.bytesToUint256(vm.rpc("eth_gasPrice", "[]")), + // msg.sender, + // "", + // "", + // Utils.MAX_PRIORITY_TX_GAS, + // new bytes[](0), + // addr, + // config.chainId, + // config.bridgehub, + // config.l1SharedBridge + // ); } function finalizeZkTokenWithdrawal( @@ -210,18 +221,9 @@ contract DeployZKScript is Script { string memory name, string memory symbol, uint256 decimals, - string memory implementation, uint256 mint, address[] storage additionalAddressesForMinting ) internal returns (address) { - bytes memory args; - // WETH9 constructor has no arguments - if (keccak256(bytes(implementation)) != keccak256(bytes("WETH9.sol"))) { - args = abi.encode(name, symbol, decimals); - } - - bytes memory bytecode = abi.encodePacked(vm.getCode(implementation), args); - address tokenAddress = address(new TestnetERC20Token(name, symbol, uint8(decimals))); // No salt for testing if (mint > 0) { From bc22869179dd9ce23e21d655b3b4b1154abe30b9 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Tue, 22 Oct 2024 19:17:50 +0400 Subject: [PATCH 39/60] (fix): update scripts --- l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol | 6 +++--- l1-contracts/deploy-scripts/GatewayPreparation.s.sol | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol b/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol index 07b1bfb68..1ca7daf88 100644 --- a/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol +++ b/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol @@ -168,18 +168,18 @@ contract GatewayCTMFromL1 is Script { function distributeBaseToken() internal { deployerAddress = msg.sender; + uint256 amountForDistribution = 1000000000000000000000; L1AssetRouter l1AR = L1AssetRouter(config.sharedBridgeProxy); IL1NativeTokenVault nativeTokenVault = IL1NativeTokenVault(address(l1AR.nativeTokenVault())); bytes32 baseTokenAssetID = nativeTokenVault.assetId(config.baseToken); uint256 baseTokenOriginChainId = nativeTokenVault.originChainId(baseTokenAssetID); TestnetERC20Token baseToken = TestnetERC20Token(config.baseToken); - uint256 deployerBalance = baseToken.balanceOf(deployerAddress); vm.startBroadcast(); if (baseTokenOriginChainId == block.chainid) { - baseToken.mint(config.governanceAddr, deployerBalance / 3); + baseToken.mint(config.governanceAddr, amountForDistribution); } else { - baseToken.transfer(config.governanceAddr, deployerBalance / 3); + baseToken.transfer(config.governanceAddr, amountForDistribution); } vm.stopBroadcast(); } diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol index ea4b6ebea..50d8be502 100644 --- a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -261,6 +261,7 @@ contract GatewayPreparation is Script { // Fund chain admin with tokens if (gatewayBaseTokenAssetId != ethTokenAssetId) { deployerAddress = msg.sender; + uint256 amountForDistribution = 1000000000000000000000; L1AssetRouter l1AR = L1AssetRouter(config.sharedBridgeProxy); IL1NativeTokenVault nativeTokenVault = IL1NativeTokenVault(address(l1AR.nativeTokenVault())); address baseTokenAddress = nativeTokenVault.tokenAddress(gatewayBaseTokenAssetId); @@ -271,9 +272,9 @@ contract GatewayPreparation is Script { vm.startBroadcast(); if (baseTokenOriginChainId == block.chainid) { - baseToken.mint(chainAdmin, deployerBalance / 3); + baseToken.mint(chainAdmin, amountForDistribution); } else { - baseToken.transfer(chainAdmin, deployerBalance / 3); + baseToken.transfer(chainAdmin, amountForDistribution); } vm.stopBroadcast(); } From 167dafc20e2331661f647bdfc69adfa612e3068f Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Tue, 22 Oct 2024 19:45:02 +0400 Subject: [PATCH 40/60] (fix): distribution amounts --- l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol | 2 +- l1-contracts/deploy-scripts/GatewayPreparation.s.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol b/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol index 1ca7daf88..569823481 100644 --- a/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol +++ b/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol @@ -168,7 +168,7 @@ contract GatewayCTMFromL1 is Script { function distributeBaseToken() internal { deployerAddress = msg.sender; - uint256 amountForDistribution = 1000000000000000000000; + uint256 amountForDistribution = 100000000000000000000; L1AssetRouter l1AR = L1AssetRouter(config.sharedBridgeProxy); IL1NativeTokenVault nativeTokenVault = IL1NativeTokenVault(address(l1AR.nativeTokenVault())); bytes32 baseTokenAssetID = nativeTokenVault.assetId(config.baseToken); diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol index 50d8be502..3c9d6d7c5 100644 --- a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -261,7 +261,7 @@ contract GatewayPreparation is Script { // Fund chain admin with tokens if (gatewayBaseTokenAssetId != ethTokenAssetId) { deployerAddress = msg.sender; - uint256 amountForDistribution = 1000000000000000000000; + uint256 amountForDistribution = 100000000000000000000; L1AssetRouter l1AR = L1AssetRouter(config.sharedBridgeProxy); IL1NativeTokenVault nativeTokenVault = IL1NativeTokenVault(address(l1AR.nativeTokenVault())); address baseTokenAddress = nativeTokenVault.tokenAddress(gatewayBaseTokenAssetId); From 1dbbf05293dc604dd9fa8f40d8ad6b7ab2838d7b Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Wed, 23 Oct 2024 16:55:19 +0400 Subject: [PATCH 41/60] (fix): add script to fund chain governor --- .../DeployZKAndBridgeToL1.s.sol | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol b/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol index 853267cbe..5e0a87681 100644 --- a/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployZKAndBridgeToL1.s.sol @@ -42,6 +42,7 @@ contract DeployZKScript is Script { address deployer; address owner; address anotherOwner; + address chainGovernor; } struct TokenDescription { @@ -93,6 +94,7 @@ contract DeployZKScript is Script { config.l1SharedBridge = toml.readAddress("$.deployed_addresses.bridges.shared_bridge_proxy_addr"); config.l1Nullifier = toml.readAddress("$.deployed_addresses.bridges.l1_nullifier_proxy_addr"); config.chainId = toml.readUint("$.chain.chain_chain_id"); + config.chainGovernor = toml.readAddress("$.owner_address"); } function initializeAdditionalConfig() internal { @@ -152,18 +154,6 @@ contract DeployZKScript is Script { config.bridgehub, config.l1SharedBridge ); - // bytes32 l2TxHash = Utils.runGovernanceL1L2DirectTransaction( - // Utils.bytesToUint256(vm.rpc("eth_gasPrice", "[]")), - // msg.sender, - // "", - // "", - // Utils.MAX_PRIORITY_TX_GAS, - // new bytes[](0), - // addr, - // config.chainId, - // config.bridgehub, - // config.l1SharedBridge - // ); } function finalizeZkTokenWithdrawal( @@ -217,6 +207,22 @@ contract DeployZKScript is Script { vm.writeToml(tokenInfo, path, ".ZK.l1Address"); } + function fundChainGovernor() public { + initializeConfig(); + + string memory root = vm.projectRoot(); + string memory path = string.concat(root, vm.envString("ZK_TOKEN_OUTPUT")); + string memory toml = vm.readFile(path); + + address l1ZKAddress = toml.readAddress("$.ZK.l1Address.l1Address"); + console.log("L1 ZK address: ", l1ZKAddress); + console.log("Address of governor: ", config.chainGovernor); + TestnetERC20Token l1ZK = TestnetERC20Token(l1ZKAddress); + uint256 balance = l1ZK.balanceOf(config.deployerAddress); + vm.broadcast(); + l1ZK.transfer(config.chainGovernor, balance / 10); + } + function deployErc20( string memory name, string memory symbol, From bf9251ad6cbdc006014a8dd8952c0a66589187b9 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 23 Oct 2024 16:56:20 +0200 Subject: [PATCH 42/60] respond to comments --- l1-contracts/contracts/common/L1ContractErrors.sol | 2 -- .../contracts/governance/PermanentRestriction.sol | 13 +++++++------ .../concrete/Governance/PermanentRestriction.t.sol | 2 +- .../TransactionValidator/ValidateL1L2Tx.t.sol | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 84850f1ad..1b8643651 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -236,8 +236,6 @@ error NonSequentialBatch(); error NonSequentialVersion(); // 0x4ef79e5a error NonZeroAddress(address); -// 0xdd629f86 -error NotEnoughGas(); // 0xdd7e3621 error NotInitializedReentrancyGuard(); // 0xdf17e316 diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 8ed735036..4402c8824 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -215,9 +215,6 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St // - Query the Bridgehub for the Hyperchain with the given `chainId`. // - We compare the corresponding addresses - // Note, that we do use assembly here to ensure that the function does not panic in case of - // either incorrect `_chain` address or in case the returndata is too large - (uint256 chainId, bool chainIdQuerySuccess) = _getChainIdUnffallibleCall(_chain); if (!chainIdQuerySuccess) { @@ -238,13 +235,17 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St return admin == msg.sender; } - /// @notice Tries to call `IGetters.getChainId()` function on the `_potentialChainAddress`. + /// @notice Tries to call `IGetters.getChainId()` function on the `_chain`. /// It ensures that the returndata is of correct format and if not, it returns false. /// @param _chain The address of the potential chain - /// @return Returns a tuple of of the chainId and whether the call was successful. + /// @return chainId The chainId of the chain. + /// @return success Whether the call was successful. /// If the second item is `false`, the caller should ignore the first value. function _getChainIdUnffallibleCall(address _chain) internal view returns (uint256 chainId, bool success) { bytes4 selector = IGetters.getChainId.selector; + + // Note, that we do use assembly here to ensure that the function does not panic in case of + // either incorrect `_chain` address or in case the returndata is too large assembly { // We use scratch space here, so it is safe mstore(0, selector) @@ -272,7 +273,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice Tries to get the new admin from the migration. /// @param _call The call data. - /// @return Returns a tuple of of the new admin and whether the transaction is indeed the migration. + /// @return Returns a tuple of the new admin and whether the transaction is indeed the migration. /// If the second item is `false`, the caller should ignore the first value. /// @dev If any other error is returned, it is assumed to be out of gas or some other unexpected /// error that should be bubbled up by the caller. diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index 55fa830e0..af92383c8 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -9,7 +9,7 @@ import {ChainTypeManager} from "contracts/state-transition/ChainTypeManager.sol" import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol"; import {PermanentRestriction} from "contracts/governance/PermanentRestriction.sol"; import {IPermanentRestriction} from "contracts/governance/IPermanentRestriction.sol"; -import {NotAllowed, NotEnoughGas, UnsupportedEncodingVersion, InvalidSelector, ZeroAddress, UnallowedImplementation, RemovingPermanentRestriction, CallNotAllowed} from "contracts/common/L1ContractErrors.sol"; +import {NotAllowed, UnsupportedEncodingVersion, InvalidSelector, ZeroAddress, UnallowedImplementation, RemovingPermanentRestriction, CallNotAllowed} from "contracts/common/L1ContractErrors.sol"; import {Call} from "contracts/governance/Common.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {VerifierParams, FeeParams, PubdataPricingMode} from "contracts/state-transition/chain-deps/ZKChainStorage.sol"; diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/libraries/TransactionValidator/ValidateL1L2Tx.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/libraries/TransactionValidator/ValidateL1L2Tx.t.sol index 8016b62f4..337e6f16e 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/libraries/TransactionValidator/ValidateL1L2Tx.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/libraries/TransactionValidator/ValidateL1L2Tx.t.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.24; import {TransactionValidatorSharedTest} from "./_TransactionValidator_Shared.t.sol"; import {L2CanonicalTransaction} from "contracts/common/Messaging.sol"; -import {PubdataGreaterThanLimit, TxnBodyGasLimitNotEnoughGas, ValidateTxnNotEnoughGas, NotEnoughGas, TooMuchGas, InvalidPubdataLength} from "contracts/common/L1ContractErrors.sol"; +import {PubdataGreaterThanLimit, TxnBodyGasLimitNotEnoughGas, ValidateTxnNotEnoughGas, TooMuchGas, InvalidPubdataLength} from "contracts/common/L1ContractErrors.sol"; contract ValidateL1L2TxTest is TransactionValidatorSharedTest { function test_BasicRequestL1L2() public pure { From 64cceff4556da3d41377102328c4c54f889cf80b Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 23 Oct 2024 16:59:09 +0200 Subject: [PATCH 43/60] fix suggestion --- l1-contracts/contracts/governance/restriction/IRestriction.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/contracts/governance/restriction/IRestriction.sol b/l1-contracts/contracts/governance/restriction/IRestriction.sol index 1ef59408a..9124d1f67 100644 --- a/l1-contracts/contracts/governance/restriction/IRestriction.sol +++ b/l1-contracts/contracts/governance/restriction/IRestriction.sol @@ -18,5 +18,5 @@ interface IRestriction { /// @notice Ensures that the invoker has the required role to call the function. /// @param _call The call data. /// @param _invoker The address of the invoker. - function validateCall(Call calldata _call, address _invoker) external virtual view; + function validateCall(Call calldata _call, address _invoker) external view; } From 6630d51da2ac98048a1ae8b468807d54bb6c810e Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 13:37:18 +0400 Subject: [PATCH 44/60] (fix): build issues --- .../contracts/bridge/BridgedStandardERC20.sol | 6 ++++-- l1-contracts/contracts/bridge/L1Nullifier.sol | 2 +- .../bridge/asset-router/L1AssetRouter.sol | 10 ++++----- .../bridge/asset-router/L2AssetRouter.sol | 9 ++++---- .../bridge/ntv/L2NativeTokenVault.sol | 21 +++++++++++++++++-- .../test/L2NativeTokenVaultDev.sol | 2 +- .../state-transition/ValidatorTimelock.sol | 2 +- .../libraries/PriorityTree.sol | 2 +- 8 files changed, 37 insertions(+), 17 deletions(-) diff --git a/l1-contracts/contracts/bridge/BridgedStandardERC20.sol b/l1-contracts/contracts/bridge/BridgedStandardERC20.sol index 36b93a506..9aa0d4995 100644 --- a/l1-contracts/contracts/bridge/BridgedStandardERC20.sol +++ b/l1-contracts/contracts/bridge/BridgedStandardERC20.sol @@ -85,9 +85,11 @@ contract BridgedStandardERC20 is ERC20PermitUpgradeable, IBridgedStandardToken, nativeTokenVault = msg.sender; + bytes memory nameBytes; + bytes memory symbolBytes; + bytes memory decimalsBytes; // We parse the data exactly as they were created on the L1 bridge - (uint256, bytes memory nameBytes, bytes memory symbolBytes, bytes memory decimalsBytes) = DataEncoding - .decodeTokenData(_data); + (, nameBytes, symbolBytes, decimalsBytes) = DataEncoding.decodeTokenData(_data); ERC20Getters memory getters; string memory decodedName; diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 98edf5832..55c380bb2 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -391,7 +391,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, isWithdrawalFinalized[chainId][l2BatchNumber][l2MessageIndex] = true; (bytes32 assetId, bytes memory transferData) = _verifyWithdrawal(_finalizeWithdrawalParams); - + // Handling special case for withdrawal from ZKsync Era initiated before Shared Bridge. if (_isPreSharedBridgeEraEthWithdrawal(chainId, l2BatchNumber)) { // Checks that the withdrawal wasn't finalized already. diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index 3b902a8be..eea12555c 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -376,15 +376,15 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { // And if there is not enough allowance for the NTV bool weCanTransfer = false; if (l1Token.allowance(address(legacyBridge), address(this)) >= _amount) { - _originalCaller = address(legacyBridge); - _weCanTransfer = true; + _originalCaller = address(legacyBridge); + weCanTransfer = true; } else if ( l1Token.allowance(_originalCaller, address(this)) >= _amount && l1Token.allowance(_originalCaller, address(nativeTokenVault)) < _amount ) { - _weCanTransfer = true; - } - if (_weCanTransfer) { + weCanTransfer = true; + } + if (weCanTransfer) { // slither-disable-next-line arbitrary-send-erc20 l1Token.safeTransferFrom(_originalCaller, address(nativeTokenVault), _amount); return true; diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 9aed56a78..10f9d6297 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -69,7 +69,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. + /// @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, @@ -144,10 +144,11 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { Internal & Helpers //////////////////////////////////////////////////////////////*/ - /// @inheritdoc AssetRouterBase - function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) { + /// @notice Ensures the token is registered with NTV. + /// @param _token The address of the token that has to be registered. + function _ensureTokenRegisteredWithNTV(address _token) internal { IL2NativeTokenVault nativeTokenVault = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR); - nativeTokenVault.ensureTokenIsRegistered(_token); + nativeTokenVault.registerToken(_token); } /// @notice Initiates a withdrawal by burning funds on the contract and sending the message to L1 diff --git a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol index 8187429f9..edf3795da 100644 --- a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol @@ -37,7 +37,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { bytes32 internal immutable L2_TOKEN_PROXY_BYTECODE_HASH; /// @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. + /// @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. @@ -129,6 +129,23 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { } } + /// @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; + } + /// @notice Deploys the beacon proxy for the L2 token, while using ContractDeployer system contract. /// @dev This function uses raw call to ContractDeployer to make sure that exactly `L2_TOKEN_PROXY_BYTECODE_HASH` is used /// for the code of the proxy. @@ -191,7 +208,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { return L2_LEGACY_SHARED_BRIDGE.l2TokenAddress(_nonNativeToken); } else { bytes32 constructorInputHash = keccak256(abi.encode(address(bridgedTokenBeacon), "")); - bytes32 salt = _getCreate2Salt(_tokenOriginChainId, _l1Token); + bytes32 salt = _getCreate2Salt(_originChainId, _nonNativeToken); return L2ContractHelper.computeCreate2Address( address(this), diff --git a/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol b/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol index 46960489c..896197fea 100644 --- a/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol +++ b/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol @@ -59,7 +59,7 @@ contract L2NativeTokenVaultDev is L2NativeTokenVault { tokenBeacon.transferOwnership(owner()); bridgedTokenBeacon = IBeacon(address(tokenBeacon)); - emit L2TokenBeaconUpdated(address(bridgedTokenBeacon), l2TokenProxyBytecodeHash); + emit L2TokenBeaconUpdated(address(bridgedTokenBeacon), L2_TOKEN_PROXY_BYTECODE_HASH); } function test() external pure { diff --git a/l1-contracts/contracts/state-transition/ValidatorTimelock.sol b/l1-contracts/contracts/state-transition/ValidatorTimelock.sol index 1a64ae48e..190ab12b3 100644 --- a/l1-contracts/contracts/state-transition/ValidatorTimelock.sol +++ b/l1-contracts/contracts/state-transition/ValidatorTimelock.sol @@ -179,7 +179,7 @@ contract ValidatorTimelock is IExecutor, Ownable2Step { /// @dev Call the zkChain diamond contract with the same calldata as this contract was called. /// Note: it is called the zkChain diamond contract, not delegatecalled! function _propagateToZKChain(uint256 _chainId) internal { - address contractAddress = chainTypeManager.getHyperchain(_chainId); + address contractAddress = chainTypeManager.getZKChain(_chainId); assembly { // Copy function signature and arguments from calldata at zero position into memory at pointer position calldatacopy(0, 0, calldatasize()) diff --git a/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol b/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol index 3874d140a..8ebc98633 100644 --- a/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol +++ b/l1-contracts/contracts/state-transition/libraries/PriorityTree.sol @@ -98,7 +98,7 @@ library PriorityTree { } /// @notice Reinitialize the tree from a commitment on L1. - function l1Reinit(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.tree._nextLeafIndex >= _commitment.nextLeafIndex, "PT: invalid next leaf index"); From 21f6e34fd0a39e1a46d6ecb41543ef98af0b519e Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 13:58:14 +0400 Subject: [PATCH 45/60] (fix): slither & build --- .../contracts/bridge/BridgedStandardERC20.sol | 1 + .../contracts/bridge/L1ERC20Bridge.sol | 2 ++ .../contracts/bridge/ntv/INativeTokenVault.sol | 4 ++++ .../contracts/bridge/ntv/NativeTokenVault.sol | 18 +++++++++++++++--- l1-contracts/contracts/bridgehub/Bridgehub.sol | 1 + .../contracts/common/L1ContractErrors.sol | 2 ++ 6 files changed, 25 insertions(+), 3 deletions(-) diff --git a/l1-contracts/contracts/bridge/BridgedStandardERC20.sol b/l1-contracts/contracts/bridge/BridgedStandardERC20.sol index 9aa0d4995..d72b1c84e 100644 --- a/l1-contracts/contracts/bridge/BridgedStandardERC20.sol +++ b/l1-contracts/contracts/bridge/BridgedStandardERC20.sol @@ -89,6 +89,7 @@ contract BridgedStandardERC20 is ERC20PermitUpgradeable, IBridgedStandardToken, bytes memory symbolBytes; bytes memory decimalsBytes; // We parse the data exactly as they were created on the L1 bridge + // slither-disable-next-line unused-return (, nameBytes, symbolBytes, decimalsBytes) = DataEncoding.decodeTokenData(_data); ERC20Getters memory getters; diff --git a/l1-contracts/contracts/bridge/L1ERC20Bridge.sol b/l1-contracts/contracts/bridge/L1ERC20Bridge.sol index 9b29b71e0..4badffddb 100644 --- a/l1-contracts/contracts/bridge/L1ERC20Bridge.sol +++ b/l1-contracts/contracts/bridge/L1ERC20Bridge.sol @@ -204,6 +204,7 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard { _refundRecipient: _refundRecipient }); // clearing approval + // slither-disable-next-line unused-return IERC20(_l1Token).approve(address(L1_ASSET_ROUTER), 0); depositAmount[msg.sender][_l1Token][l2TxHash] = _amount; emit DepositInitiated({ @@ -224,6 +225,7 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard { function _approveFundsToAssetRouter(address _from, IERC20 _token, uint256 _amount) internal returns (uint256) { uint256 balanceBefore = _token.balanceOf(address(this)); _token.safeTransferFrom(_from, address(this), _amount); + // slither-disable-next-line unused-return _token.approve(address(L1_ASSET_ROUTER), _amount); uint256 balanceAfter = _token.balanceOf(address(this)); diff --git a/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol index 0cd78cf54..c471bec42 100644 --- a/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol @@ -25,6 +25,10 @@ interface INativeTokenVault { /// @notice No access control is ok, since the bridging of tokens should be permissionless. This requires permissionless registration. function registerToken(address _l1Token) external; + /// @notice Ensures that the native token is registered with the NTV. + /// @dev This function is used to ensure that the token is registered with the NTV. + function ensureTokenIsRegistered(address _nativeToken) external; + /// @notice Used to get the assetId of a token function getAssetId(uint256 _chainId, address _tokenAddress) external view returns (bytes32); diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 4149356f3..225841ba3 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -52,6 +52,9 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 /// @dev A mapping assetId => tokenAddress mapping(bytes32 assetId => address tokenAddress) public tokenAddress; + /// @dev A mapping tokenAddress => assetId + mapping(address tokenAddress => bytes32 assetId) public assetId; + /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. @@ -92,6 +95,13 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 _unsafeRegisterNativeToken(_nativeToken); } + /// @inheritdoc INativeTokenVault + function ensureTokenIsRegistered(address _nativeToken) public { + if (assetId[_nativeToken] == bytes32(0)) { + _registerToken(_nativeToken); + } + } + /*////////////////////////////////////////////////////////////// FINISH TRANSACTION FUNCTIONS //////////////////////////////////////////////////////////////*/ @@ -331,10 +341,11 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 /// @dev It does not perform any checks for the correctnesss of the token contract. /// @param _nativeToken The address of the token to be registered. function _unsafeRegisterNativeToken(address _nativeToken) internal { - bytes32 assetId = DataEncoding.encodeNTVAssetId(block.chainid, _nativeToken); + bytes32 newAssetId = DataEncoding.encodeNTVAssetId(block.chainid, _nativeToken); ASSET_ROUTER.setAssetHandlerAddressThisChain(bytes32(uint256(uint160(_nativeToken))), address(this)); - tokenAddress[assetId] = _nativeToken; - originChainId[assetId] = block.chainid; + tokenAddress[newAssetId] = _nativeToken; + assetId[_nativeToken] = newAssetId; + originChainId[newAssetId] = block.chainid; } function _handleChainBalanceIncrease( @@ -383,6 +394,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 /// @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; diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 23f371ac3..fa0305c1a 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -316,6 +316,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus bytes32 ctmAssetId = keccak256(abi.encode(L1_CHAIN_ID, sender, _additionalData)); ctmAssetIdToAddress[ctmAssetId] = _assetAddress; + ctmAssetIdFromAddress[_assetAddress] = ctmAssetId; emit AssetRegistered(ctmAssetId, _assetAddress, _additionalData, msg.sender); } diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 300b01f3a..f7a996161 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -351,6 +351,8 @@ error UndefinedDiamondCutAction(); error UnexpectedNumberOfFactoryDeps(); // 0x6aa39880 error UnexpectedSystemLog(uint256 logKey); +// 0xa4dde386 +error UnimplementedMessage(string); // 0xf093c2e5 error UpgradeBatchNumberIsNotZero(); // From 681c22f45cbb20e694da9b4316b43bb6e196ea87 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 14:15:23 +0400 Subject: [PATCH 46/60] (fix): foundry test --- .../concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol index 0131721a0..a53b065d3 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol @@ -60,7 +60,7 @@ contract L1AssetRouterFailTest is L1AssetRouterTest { } function test_nullifyChainBalanceByNTV_wrongCaller() public { - vm.expectRevert("L1N: not NTV"); + vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, address(this))); l1Nullifier.nullifyChainBalanceByNTV(chainId, address(token)); } From 77973da8565c23bef2d453770d2eda548733eef0 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 15:43:38 +0400 Subject: [PATCH 47/60] (fix): formatting + removal of unused function from Permanent Restriction --- .../dev-contracts/DummyRestriction.sol | 2 +- .../governance/AccessControlRestriction.sol | 2 +- .../contracts/governance/ChainAdmin.sol | 8 ++-- .../contracts/governance/L2AdminFactory.sol | 8 ++-- .../governance/PermanentRestriction.sol | 38 +------------------ .../governance/restriction/Restriction.sol | 2 +- .../restriction/RestrictionValidator.sol | 6 +-- .../Governance/PermanentRestriction.t.sol | 2 +- .../unit/L2AdminFactory/L2AdminFactory.t.sol | 2 +- 9 files changed, 17 insertions(+), 53 deletions(-) diff --git a/l1-contracts/contracts/dev-contracts/DummyRestriction.sol b/l1-contracts/contracts/dev-contracts/DummyRestriction.sol index b4f0025b6..8ce2680db 100644 --- a/l1-contracts/contracts/dev-contracts/DummyRestriction.sol +++ b/l1-contracts/contracts/dev-contracts/DummyRestriction.sol @@ -29,7 +29,7 @@ contract DummyRestriction is IRestriction { /// @notice Ensures that the invoker has the required role to call the function. /// @param _call The call data. /// @param _invoker The address of the invoker. - function validateCall(Call calldata _call, address _invoker) external virtual view { + function validateCall(Call calldata _call, address _invoker) external view virtual { // nothing } } diff --git a/l1-contracts/contracts/governance/AccessControlRestriction.sol b/l1-contracts/contracts/governance/AccessControlRestriction.sol index 263d5bd7d..6052d1e6a 100644 --- a/l1-contracts/contracts/governance/AccessControlRestriction.sol +++ b/l1-contracts/contracts/governance/AccessControlRestriction.sol @@ -60,7 +60,7 @@ contract AccessControlRestriction is Restriction, IAccessControlRestriction, Acc } /// @inheritdoc Restriction - function validateCall(Call calldata _call, address _invoker) external override view { + function validateCall(Call calldata _call, address _invoker) external view override { // Note, that since `DEFAULT_ADMIN_ROLE` is 0 and the default storage value for the // `requiredRoles` and `requiredRolesForFallback` is 0, the default admin is by default a required // role for all the functions. diff --git a/l1-contracts/contracts/governance/ChainAdmin.sol b/l1-contracts/contracts/governance/ChainAdmin.sol index 1ee948832..25a30c498 100644 --- a/l1-contracts/contracts/governance/ChainAdmin.sol +++ b/l1-contracts/contracts/governance/ChainAdmin.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.24; // solhint-disable gas-length-in-loops -import {ZeroAddress, NoCallsProvided, OnlySelfAllowed, RestrictionWasNotPresent, RestrictionWasAlreadyPresent} from "../common/L1ContractErrors.sol"; +import {NoCallsProvided, OnlySelfAllowed, RestrictionWasNotPresent, RestrictionWasAlreadyPresent} from "../common/L1ContractErrors.sol"; import {IChainAdmin} from "./IChainAdmin.sol"; import {Restriction} from "./restriction/Restriction.sol"; import {RestrictionValidator} from "./restriction/RestrictionValidator.sol"; @@ -16,8 +16,8 @@ import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @notice The contract is designed to hold the `admin` role in ZKSync Chain (State Transition) contracts. -/// @dev Note, that it does not implement any form of access control by default, but instead utilizes -/// so called "restrictions": contracts that implement the `IRestriction` interface and ensure that +/// @dev Note, that it does not implement any form of access control by default, but instead utilizes +/// so called "restrictions": contracts that implement the `IRestriction` interface and ensure that /// particular restrictions are ensured for the contract, including access control, security invariants, etc. contract ChainAdmin is IChainAdmin, ReentrancyGuard { using EnumerableSet for EnumerableSet.AddressSet; @@ -123,7 +123,7 @@ contract ChainAdmin is IChainAdmin, ReentrancyGuard { /// @param _restriction The address of the restriction contract to be added. function _addRestriction(address _restriction) private { RestrictionValidator.validateRestriction(_restriction); - + if (!activeRestrictions.add(_restriction)) { revert RestrictionWasAlreadyPresent(_restriction); } diff --git a/l1-contracts/contracts/governance/L2AdminFactory.sol b/l1-contracts/contracts/governance/L2AdminFactory.sol index 3ed6e3b04..c8196c616 100644 --- a/l1-contracts/contracts/governance/L2AdminFactory.sol +++ b/l1-contracts/contracts/governance/L2AdminFactory.sol @@ -32,20 +32,20 @@ contract L2AdminFactory { /// @return admin The address of the deployed admin contract. // solhint-disable-next-line gas-calldata-parameters function deployAdmin(address[] memory _additionalRestrictions, bytes32 _salt) external returns (address admin) { - // Even though the chain admin will likely perform similar checks, + // Even though the chain admin will likely perform similar checks, // we keep those here just in case, since it is not expensive, while allowing to fail fast. _validateRestrctions(_additionalRestrictions); uint256 cachedRequired = requiredRestrictions.length; uint256 cachedAdditional = _additionalRestrictions.length; address[] memory restrictions = new address[](cachedRequired + cachedAdditional); - + unchecked { for (uint256 i = 0; i < cachedRequired; ++i) { restrictions[i] = requiredRestrictions[i]; } for (uint256 i = 0; i < cachedAdditional; ++i) { restrictions[cachedRequired + i] = _additionalRestrictions[i]; - } + } } admin = address(new ChainAdmin{salt: _salt}(restrictions)); @@ -57,7 +57,7 @@ contract L2AdminFactory { function _validateRestrctions(address[] memory _restrictions) internal view { unchecked { uint256 length = _restrictions.length; - for(uint256 i = 0; i < length; ++i) { + for (uint256 i = 0; i < length; ++i) { RestrictionValidator.validateRestriction(_restrictions[i]); } } diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 647d247df..befbc3140 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -7,7 +7,7 @@ import {UnsupportedEncodingVersion, CallNotAllowed, ChainZeroAddress, NotAHyperc import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "../bridgehub/IBridgehub.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; import {L2ContractHelper} from "../common/libraries/L2ContractHelper.sol"; -import {NEW_ENCODING_VERSION, IAssetRouterBase} from "../bridge/asset-router/IAssetRouterBase.sol"; +import {NEW_ENCODING_VERSION} from "../bridge/asset-router/IAssetRouterBase.sol"; import {Call} from "./Common.sol"; import {Restriction} from "./restriction/Restriction.sol"; @@ -261,42 +261,6 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste } } - /// @notice Tries to call `IGetters.getChainId()` function on the `_chain`. - /// It ensures that the returndata is of correct format and if not, it returns false. - /// @param _chain The address of the potential chain - /// @return chainId The chainId of the chain. - /// @return success Whether the call was successful. - /// If the second item is `false`, the caller should ignore the first value. - function _getChainIdUnffallibleCall(address _chain) internal view returns (uint256 chainId, bool success) { - bytes4 selector = IGetters.getChainId.selector; - - // Note, that we do use assembly here to ensure that the function does not panic in case of - // either incorrect `_chain` address or in case the returndata is too large - assembly { - // We use scratch space here, so it is safe - mstore(0, selector) - success := staticcall( - gas(), - _chain, - 0, - 4, - 0, - 0 - ) - - let isReturndataSizeCorrect := eq(returndatasize(), 32) - - success := and(success, isReturndataSizeCorrect) - - if success { - // We use scratch space here, so it is safe - returndatacopy(0, 0, 32) - - chainId := mload(0) - } - } - } - /// @notice Tries to get the new admin from the migration. /// @param _call The call data. /// @dev This function reverts if the provided call was not a migration call. diff --git a/l1-contracts/contracts/governance/restriction/Restriction.sol b/l1-contracts/contracts/governance/restriction/Restriction.sol index 48c199b43..e516b9b0e 100644 --- a/l1-contracts/contracts/governance/restriction/Restriction.sol +++ b/l1-contracts/contracts/governance/restriction/Restriction.sol @@ -18,5 +18,5 @@ abstract contract Restriction is IRestriction { /// @notice Ensures that the invoker has the required role to call the function. /// @param _call The call data. /// @param _invoker The address of the invoker. - function validateCall(Call calldata _call, address _invoker) external virtual view; + function validateCall(Call calldata _call, address _invoker) external view virtual; } diff --git a/l1-contracts/contracts/governance/restriction/RestrictionValidator.sol b/l1-contracts/contracts/governance/restriction/RestrictionValidator.sol index 247b65c56..e0d110947 100644 --- a/l1-contracts/contracts/governance/restriction/RestrictionValidator.sol +++ b/l1-contracts/contracts/governance/restriction/RestrictionValidator.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.24; import {NotARestriction} from "../../common/L1ContractErrors.sol"; -import { IRestriction, RESTRICTION_MAGIC } from "./IRestriction.sol"; +import {IRestriction, RESTRICTION_MAGIC} from "./IRestriction.sol"; /// @title Restriction validator /// @author Matter Labs @@ -12,8 +12,8 @@ import { IRestriction, RESTRICTION_MAGIC } from "./IRestriction.sol"; library RestrictionValidator { /// @notice Ensures that the provided address implements the restriction interface /// @dev Note that it *can not guarantee* that the corresponding address indeed implements - /// the interface completely or that it is implemented correctly. It is mainly used to - /// ensure that invalid restrictions can not be accidentally added. + /// the interface completely or that it is implemented correctly. It is mainly used to + /// ensure that invalid restrictions can not be accidentally added. function validateRestriction(address _restriction) internal view { if (IRestriction(_restriction).getSupportsRestrictionMagic() != RESTRICTION_MAGIC) { revert NotARestriction(_restriction); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index 04089ad25..51267c38a 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -386,4 +386,4 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { }); vm.stopPrank(); } -} \ No newline at end of file +} diff --git a/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol b/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol index 2ea778b48..95bcb8304 100644 --- a/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol +++ b/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol @@ -16,7 +16,7 @@ contract L2AdminFactoryTest is Test { address validRestriction2; address invalidRestriction; - + function setUp() public { validRestriction1 = address(new DummyRestriction(true)); validRestriction2 = address(new DummyRestriction(true)); From 1751d90373daa50b253efe51161a81226cf80024 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 16:15:55 +0400 Subject: [PATCH 48/60] (feat): add scripts and small changes required for ZK as base token --- l1-contracts/.env | 3 + .../contracts/bridge/L2SharedBridgeLegacy.sol | 68 ++-- .../bridge/asset-router/IL2AssetRouter.sol | 8 + .../bridge/asset-router/L2AssetRouter.sol | 39 ++- .../interfaces/IL2SharedBridgeLegacy.sol | 13 +- .../bridge/ntv/INativeTokenVault.sol | 3 + .../contracts/bridge/ntv/NativeTokenVault.sol | 3 +- .../deploy-scripts/RegisterZKChain.s.sol | 306 +++++++++++++++--- 8 files changed, 375 insertions(+), 68 deletions(-) diff --git a/l1-contracts/.env b/l1-contracts/.env index 25ec2b87f..fc433ae7a 100644 --- a/l1-contracts/.env +++ b/l1-contracts/.env @@ -40,7 +40,10 @@ CONTRACTS_SHARED_BRIDGE_UPGRADE_STORAGE_SWITCH=0 CONTRACTS_MAX_NUMBER_OF_ZK_CHAINS=100 L1_CONFIG=/script-config/config-deploy-l1.toml L1_OUTPUT=/script-out/output-deploy-l1.toml +L2_CONFIG=/script-config/config-deploy-l2-contracts.toml TOKENS_CONFIG=/script-config/config-deploy-erc20.toml +ZK_TOKEN_CONFIG=/script-config/config-deploy-zk.toml +ZK_TOKEN_OUTPUT=/script-out/output-deploy-zk-token.toml ZK_CHAIN_CONFIG=/script-config/register-zk-chain.toml ZK_CHAIN_OUTPUT=/script-out/output-deploy-zk-chain-era.toml FORCE_DEPLOYMENTS_CONFIG=/script-config/generate-force-deployments-data.toml diff --git a/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol b/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol index 61e6141c2..4ae901593 100644 --- a/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol +++ b/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol @@ -10,22 +10,21 @@ import {BridgedStandardERC20} from "./BridgedStandardERC20.sol"; import {DEPLOYER_SYSTEM_CONTRACT, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "../common/L2ContractAddresses.sol"; import {SystemContractsCaller} from "../common/libraries/SystemContractsCaller.sol"; import {L2ContractHelper, IContractDeployer} from "../common/libraries/L2ContractHelper.sol"; +import {AddressAliasHelper} from "../vendor/AddressAliasHelper.sol"; import {IL2AssetRouter} from "./asset-router/IL2AssetRouter.sol"; import {IL2NativeTokenVault} from "./ntv/IL2NativeTokenVault.sol"; import {IL2SharedBridgeLegacy} from "./interfaces/IL2SharedBridgeLegacy.sol"; -import {ZeroAddress, EmptyBytes32, Unauthorized, AmountMustBeGreaterThanZero, DeployFailed} from "../common/L1ContractErrors.sol"; +import {InvalidCaller, ZeroAddress, EmptyBytes32, Unauthorized, AmountMustBeGreaterThanZero, DeployFailed} from "../common/L1ContractErrors.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @notice The "default" bridge implementation for the ERC20 tokens. Note, that it does not /// support any custom token logic, i.e. rebase tokens' functionality is not supported. +/// @dev Note, that this contract should be compatible with its previous version as it will be +/// the primary bridge to be used during migration. contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { - /// @dev Contract is expected to be used as proxy implementation. - /// @dev Disable the initialization to prevent Parity hack. - uint256 public immutable ERA_CHAIN_ID; - /// @dev The address of the L1 shared bridge counterpart. address public override l1SharedBridge; @@ -41,6 +40,7 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { /// @dev The address of the legacy L1 erc20 bridge counterpart. /// This is non-zero only on Era, and should not be renamed for backward compatibility with the SDKs. + // slither-disable-next-line uninitialized-state address public override l1Bridge; modifier onlyNTV() { @@ -57,19 +57,16 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { _; } - constructor(uint256 _eraChainId) { - ERA_CHAIN_ID = _eraChainId; + constructor() { _disableInitializers(); } /// @notice Initializes the bridge contract for later use. Expected to be used in the proxy. /// @param _l1SharedBridge The address of the L1 Bridge contract. - /// @param _l1Bridge The address of the legacy L1 Bridge contract. /// @param _l2TokenProxyBytecodeHash The bytecode hash of the proxy for tokens deployed by the bridge. /// @param _aliasedOwner The address of the governor contract. function initialize( address _l1SharedBridge, - address _l1Bridge, bytes32 _l2TokenProxyBytecodeHash, address _aliasedOwner ) external reinitializer(2) { @@ -87,17 +84,14 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { l1SharedBridge = _l1SharedBridge; - if (block.chainid != ERA_CHAIN_ID) { + // The following statement is true only in freshly deployed environments. However, + // for those environments we do not need to deploy this contract at all. + // This check is primarily for local testing purposes. + if (l2TokenProxyBytecodeHash == bytes32(0) && address(l2TokenBeacon) == address(0)) { address l2StandardToken = address(new BridgedStandardERC20{salt: bytes32(0)}()); l2TokenBeacon = new UpgradeableBeacon{salt: bytes32(0)}(l2StandardToken); l2TokenProxyBytecodeHash = _l2TokenProxyBytecodeHash; l2TokenBeacon.transferOwnership(_aliasedOwner); - } else { - if (_l1Bridge == address(0)) { - revert ZeroAddress(); - } - l1Bridge = _l1Bridge; - // l2StandardToken and l2TokenBeacon are already deployed on ERA, and stored in the proxy } } @@ -113,6 +107,44 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { IL2AssetRouter(L2_ASSET_ROUTER_ADDR).withdrawLegacyBridge(_l1Receiver, _l2Token, _amount, msg.sender); } + /// @notice Finalize the deposit and mint funds + /// @param _l1Sender The account address that initiated the deposit on L1 + /// @param _l2Receiver The account address that would receive minted ether + /// @param _l1Token The address of the token that was locked on the L1 + /// @param _amount Total amount of tokens deposited from L1 + /// @param _data The additional data that user can pass with the deposit + function finalizeDeposit( + address _l1Sender, + address _l2Receiver, + address _l1Token, + uint256 _amount, + bytes calldata _data + ) external { + // Only the L1 bridge counterpart can initiate and finalize the deposit. + if ( + AddressAliasHelper.undoL1ToL2Alias(msg.sender) != l1Bridge && + AddressAliasHelper.undoL1ToL2Alias(msg.sender) != l1SharedBridge + ) { + revert InvalidCaller(msg.sender); + } + + IL2AssetRouter(L2_ASSET_ROUTER_ADDR).finalizeDepositLegacyBridge({ + _l1Sender: _l1Sender, + _l2Receiver: _l2Receiver, + _l1Token: _l1Token, + _amount: _amount, + _data: _data + }); + + address l2Token = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).l2TokenAddress(_l1Token); + + if (l1TokenAddress[l2Token] == address(0)) { + l1TokenAddress[l2Token] = _l1Token; + } + + emit FinalizeDeposit(_l1Sender, _l2Receiver, l2Token, _amount); + } + /// @return Address of an L2 token counterpart function l2TokenAddress(address _l1Token) public view override returns (address) { address token = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).l2TokenAddress(_l1Token); @@ -158,8 +190,8 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { proxy = abi.decode(returndata, (address)); } - function sendMessageToL1(bytes calldata _message) external override onlyAssetRouter { + function sendMessageToL1(bytes calldata _message) external override onlyAssetRouter returns (bytes32) { // slither-disable-next-line unused-return - L2ContractHelper.sendMessageToL1(_message); + return L2ContractHelper.sendMessageToL1(_message); } } diff --git a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol index 591215935..4ba6a7da9 100644 --- a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol @@ -18,6 +18,14 @@ interface IL2AssetRouter { function withdrawLegacyBridge(address _l1Receiver, address _l2Token, uint256 _amount, address _sender) external; + function finalizeDepositLegacyBridge( + address _l1Sender, + address _l2Receiver, + address _l1Token, + uint256 _amount, + bytes calldata _data + ) external; + /// @dev Used to set the assetHandlerAddress for a given assetId. /// @dev Will be used by ZK Gateway function setAssetHandlerAddress(uint256 _originChainId, bytes32 _assetId, address _assetAddress) external; diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 10f9d6297..54e9a5ce8 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -144,11 +144,13 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { Internal & Helpers //////////////////////////////////////////////////////////////*/ - /// @notice Ensures the token is registered with NTV. - /// @param _token The address of the token that has to be registered. - function _ensureTokenRegisteredWithNTV(address _token) internal { + /// @notice Ensures that token is registered with native token vault. + /// @dev Only used when deposit is made with legacy data encoding format. + /// @param _token The L2 token address which should be registered with native token vault. + /// @return assetId The asset ID of the token provided. + function _ensureTokenRegisteredWithNTV(address _token) internal returns (bytes32 assetId) { IL2NativeTokenVault nativeTokenVault = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR); - nativeTokenVault.registerToken(_token); + nativeTokenVault.ensureTokenIsRegistered(_token); } /// @notice Initiates a withdrawal by burning funds on the contract and sending the message to L1 @@ -235,6 +237,35 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { this.finalizeDeposit(L1_CHAIN_ID, assetId, data); } + function finalizeDepositLegacyBridge( + address _l1Sender, + address _l2Receiver, + address _l1Token, + uint256 _amount, + bytes calldata _data + ) external onlyLegacyBridge { + _translateLegacyFinalizeDeposit({ + _l1Sender: _l1Sender, + _l2Receiver: _l2Receiver, + _l1Token: _l1Token, + _amount: _amount, + _data: _data + }); + } + + function _translateLegacyFinalizeDeposit( + address _l1Sender, + address _l2Receiver, + address _l1Token, + uint256 _amount, + bytes calldata _data + ) internal { + bytes32 assetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, _l1Token); + // solhint-disable-next-line func-named-parameters + bytes memory data = DataEncoding.encodeBridgeMintData(_l1Sender, _l2Receiver, _l1Token, _amount, _data); + this.finalizeDeposit(L1_CHAIN_ID, assetId, data); + } + /// @notice Initiates a withdrawal by burning funds on the contract and sending the message to L1 /// where tokens would be unlocked /// @dev A compatibility method to support legacy functionality for the SDK. diff --git a/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol b/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol index 00a762447..71c7a46c5 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol @@ -2,9 +2,20 @@ pragma solidity ^0.8.20; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; + /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev interface IL2SharedBridgeLegacy { + event FinalizeDeposit( + address indexed l1Sender, + address indexed l2Receiver, + address indexed l2Token, + uint256 amount + ); + + function l2TokenBeacon() external returns (UpgradeableBeacon); + function withdraw(address _l1Receiver, address _l2Token, uint256 _amount) external; function l1TokenAddress(address _l2Token) external view returns (address); @@ -17,5 +28,5 @@ interface IL2SharedBridgeLegacy { function deployBeaconProxy(bytes32 _salt) external returns (address); - function sendMessageToL1(bytes calldata _message) external; + function sendMessageToL1(bytes calldata _message) external returns (bytes32); } diff --git a/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol index c471bec42..9d1b112f1 100644 --- a/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol @@ -38,6 +38,9 @@ interface INativeTokenVault { /// @notice Used to get the token address of an assetId function tokenAddress(bytes32 assetId) external view returns (address); + /// @notice Used to get the assetId of a token + function assetId(address token) external view returns (bytes32); + /// @notice Used to get the expected bridged token address corresponding to its native counterpart function calculateCreate2TokenAddress(uint256 _originChainId, address _originToken) external view returns (address); } diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 225841ba3..753fbdf59 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -92,6 +92,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 revert TokenNotSupported(WETH_TOKEN); } require(_nativeToken.code.length > 0, "NTV: empty token"); + require(assetId[_nativeToken] == bytes32(0), "NTV: asset id already registered"); _unsafeRegisterNativeToken(_nativeToken); } @@ -342,10 +343,10 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 /// @param _nativeToken The address of the token to be registered. function _unsafeRegisterNativeToken(address _nativeToken) internal { bytes32 newAssetId = DataEncoding.encodeNTVAssetId(block.chainid, _nativeToken); - ASSET_ROUTER.setAssetHandlerAddressThisChain(bytes32(uint256(uint160(_nativeToken))), address(this)); tokenAddress[newAssetId] = _nativeToken; assetId[_nativeToken] = newAssetId; originChainId[newAssetId] = block.chainid; + ASSET_ROUTER.setAssetHandlerAddressThisChain(bytes32(uint256(uint160(_nativeToken))), address(this)); } function _handleChainBalanceIncrease( diff --git a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol index 63c230741..6551ac821 100644 --- a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol +++ b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol @@ -7,6 +7,7 @@ import {Script, console2 as console} from "forge-std/Script.sol"; import {Vm} from "forge-std/Vm.sol"; import {stdToml} from "forge-std/StdToml.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts-v4/proxy/transparent/ProxyAdmin.sol"; import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; @@ -15,53 +16,99 @@ import {Governance} from "contracts/governance/Governance.sol"; import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; import {AccessControlRestriction} from "contracts/governance/AccessControlRestriction.sol"; import {Utils} from "./Utils.sol"; +import {L2ContractsBytecodesLib} from "./L2ContractsBytecodesLib.sol"; import {PubdataPricingMode} from "contracts/state-transition/chain-deps/ZKChainStorage.sol"; +import {IL1NativeTokenVault} from "contracts/bridge/ntv/IL1NativeTokenVault.sol"; import {INativeTokenVault} from "contracts/bridge/ntv/INativeTokenVault.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {L1NullifierDev} from "contracts/dev-contracts/L1NullifierDev.sol"; +import {L2SharedBridgeLegacy} from "contracts/bridge/L2SharedBridgeLegacy.sol"; +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; +// solhint-disable-next-line gas-struct-packing +struct Config { + address deployerAddress; + address ownerAddress; + uint256 chainChainId; + bool validiumMode; + uint256 bridgehubCreateNewChainSalt; + address validatorSenderOperatorCommitEth; + address validatorSenderOperatorBlobsEth; + address baseToken; + bytes32 baseTokenAssetId; + uint128 baseTokenGasPriceMultiplierNominator; + uint128 baseTokenGasPriceMultiplierDenominator; + address bridgehub; + // TODO(EVM-744): maybe rename to asset router + address sharedBridgeProxy; + address nativeTokenVault; + address chainTypeManagerProxy; + address validatorTimelock; + bytes diamondCutData; + bytes forceDeployments; + address governanceSecurityCouncilAddress; + uint256 governanceMinDelay; + address l1Nullifier; +} + contract RegisterZKChainScript is Script { using stdToml for string; address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; bytes32 internal constant STATE_TRANSITION_NEW_CHAIN_HASH = keccak256("NewZKChain(uint256,address)"); - // solhint-disable-next-line gas-struct-packing - struct Config { - address deployerAddress; - address ownerAddress; - uint256 chainChainId; - bool validiumMode; - uint256 bridgehubCreateNewChainSalt; - address validatorSenderOperatorCommitEth; - address validatorSenderOperatorBlobsEth; - address baseToken; - bytes32 baseTokenAssetId; - uint128 baseTokenGasPriceMultiplierNominator; - uint128 baseTokenGasPriceMultiplierDenominator; - address bridgehub; - address nativeTokenVault; - address stateTransitionProxy; - address validatorTimelock; - bytes diamondCutData; - bytes forceDeployments; - address governanceSecurityCouncilAddress; - uint256 governanceMinDelay; - address newDiamondProxy; + struct Output { address governance; + address diamondProxy; address chainAdmin; + address l2LegacySharedBridge; + address accessControlRestrictionAddress; + address chainProxyAdmin; + } + + struct LegacySharedBridgeParams { + bytes implementationConstructorParams; + address implementationAddress; + bytes proxyConstructorParams; + address proxyAddress; } + LegacySharedBridgeParams internal legacySharedBridgeParams; + Config internal config; + Output internal output; function run() public { console.log("Deploying ZKChain"); initializeConfig(); + // TODO: some chains may not want to have a legacy shared bridge + runInner("/script-out/output-register-zk-chain.toml", true); + } + + function runForTest() public { + console.log("Deploying ZKChain"); + + initializeConfigTest(); + // TODO: Yes, it is the same as for prod since it is never read from down the line + runInner(vm.envString("ZK_CHAIN_OUT"), false); + } + + function runInner(string memory outputPath, bool initializeL2LegacyBridge) internal { + string memory root = vm.projectRoot(); + outputPath = string.concat(root, outputPath); + + if (initializeL2LegacyBridge) { + // This must be run before the chain is deployed + setUpLegacySharedBridgeParams(); + } deployGovernance(); deployChainAdmin(); + deployChainProxyAddress(); checkTokenAddress(); registerAssetIdOnBridgehub(); registerTokenOnNTV(); @@ -70,10 +117,61 @@ contract RegisterZKChainScript is Script { configureZkSyncStateTransition(); setPendingAdmin(); - saveOutput(); + if (initializeL2LegacyBridge) { + deployLegacySharedBridge(); + } + + saveOutput(outputPath); } function initializeConfig() internal { + // Grab config from output of l1 deployment + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/script-config/register-zk-chain.toml"); + string memory toml = vm.readFile(path); + + config.deployerAddress = msg.sender; + + // Config file must be parsed key by key, otherwise values returned + // are parsed alfabetically and not by key. + // https://book.getfoundry.sh/cheatcodes/parse-toml + + config.bridgehub = toml.readAddress("$.deployed_addresses.bridgehub.bridgehub_proxy_addr"); + config.chainTypeManagerProxy = toml.readAddress( + "$.deployed_addresses.state_transition.chain_type_manager_proxy_addr" + ); + config.validatorTimelock = toml.readAddress("$.deployed_addresses.validator_timelock_addr"); + // config.bridgehubGovernance = toml.readAddress("$.deployed_addresses.governance_addr"); + config.nativeTokenVault = toml.readAddress("$.deployed_addresses.native_token_vault_addr"); + config.sharedBridgeProxy = toml.readAddress("$.deployed_addresses.bridges.shared_bridge_proxy_addr"); + config.l1Nullifier = toml.readAddress("$.deployed_addresses.bridges.l1_nullifier_proxy_addr"); + + config.diamondCutData = toml.readBytes("$.contracts_config.diamond_cut_data"); + config.forceDeployments = toml.readBytes("$.contracts_config.force_deployments_data"); + + config.ownerAddress = toml.readAddress("$.owner_address"); + + config.chainChainId = toml.readUint("$.chain.chain_chain_id"); + config.baseTokenGasPriceMultiplierNominator = uint128( + toml.readUint("$.chain.base_token_gas_price_multiplier_nominator") + ); + config.baseTokenGasPriceMultiplierDenominator = uint128( + toml.readUint("$.chain.base_token_gas_price_multiplier_denominator") + ); + config.baseToken = toml.readAddress("$.chain.base_token_addr"); + config.governanceSecurityCouncilAddress = toml.readAddress("$.chain.governance_security_council_address"); + config.governanceMinDelay = uint256(toml.readUint("$.chain.governance_min_delay")); + config.bridgehubCreateNewChainSalt = toml.readUint("$.chain.bridgehub_create_new_chain_salt"); + config.validiumMode = toml.readBool("$.chain.validium_mode"); + config.validatorSenderOperatorCommitEth = toml.readAddress("$.chain.validator_sender_operator_commit_eth"); + config.validatorSenderOperatorBlobsEth = toml.readAddress("$.chain.validator_sender_operator_blobs_eth"); + } + + function getConfig() public view returns (Config memory) { + return config; + } + + function initializeConfigTest() internal { // Grab config from output of l1 deployment string memory root = vm.projectRoot(); string memory path = string.concat(root, vm.envString("L1_OUTPUT")); //"/script-config/register-zkChain.toml"); @@ -86,14 +184,19 @@ contract RegisterZKChainScript is Script { // https://book.getfoundry.sh/cheatcodes/parse-toml config.bridgehub = toml.readAddress("$.deployed_addresses.bridgehub.bridgehub_proxy_addr"); - config.stateTransitionProxy = toml.readAddress( + // TODO(EVM-744): name of the key is a bit inconsistent + config.chainTypeManagerProxy = toml.readAddress( "$.deployed_addresses.state_transition.state_transition_proxy_addr" ); config.validatorTimelock = toml.readAddress("$.deployed_addresses.validator_timelock_addr"); // config.bridgehubGovernance = toml.readAddress("$.deployed_addresses.governance_addr"); config.nativeTokenVault = toml.readAddress("$.deployed_addresses.native_token_vault_addr"); + config.sharedBridgeProxy = toml.readAddress("$.deployed_addresses.bridges.shared_bridge_proxy_addr"); + config.l1Nullifier = toml.readAddress("$.deployed_addresses.bridges.l1_nullifier_proxy_addr"); + config.diamondCutData = toml.readBytes("$.contracts_config.diamond_cut_data"); config.forceDeployments = toml.readBytes("$.contracts_config.force_deployments_data"); + path = string.concat(root, vm.envString("ZK_CHAIN_CONFIG")); toml = vm.readFile(path); @@ -115,6 +218,10 @@ contract RegisterZKChainScript is Script { config.governanceSecurityCouncilAddress = toml.readAddress("$.chain.governance_security_council_address"); } + function getOwnerAddress() public view returns (address) { + return config.ownerAddress; + } + function checkTokenAddress() internal view { if (config.baseToken == address(0)) { revert("Token address is not set"); @@ -132,10 +239,62 @@ contract RegisterZKChainScript is Script { console.log("Using base token address:", config.baseToken); } + function setUpLegacySharedBridgeParams() internal { + bytes memory implementationConstructorParams = hex""; + + address legacyBridgeImplementationAddress = L2ContractHelper.computeCreate2Address( + msg.sender, + "", + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readL2LegacySharedBridgeBytecode()), + keccak256(implementationConstructorParams) + ); + + bytes memory proxyInitializationParams = abi.encodeCall( + L2SharedBridgeLegacy.initialize, + ( + config.sharedBridgeProxy, + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readBeaconProxyBytecode()), + // This is not exactly correct, this should be ecosystem governance and not chain governance + msg.sender + ) + ); + + bytes memory proxyConstructorParams = abi.encode( + legacyBridgeImplementationAddress, + // In real production, this would be aliased ecosystem governance. + // But in real production we also do not initialize legacy shared bridge + msg.sender, + proxyInitializationParams + ); + + address proxyAddress = L2ContractHelper.computeCreate2Address( + msg.sender, + "", + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readTransparentUpgradeableProxyBytecode()), + keccak256(proxyConstructorParams) + ); + + vm.broadcast(); + L1NullifierDev(config.l1Nullifier).setL2LegacySharedBridge(config.chainChainId, proxyAddress); + + legacySharedBridgeParams = LegacySharedBridgeParams({ + implementationConstructorParams: implementationConstructorParams, + implementationAddress: legacyBridgeImplementationAddress, + proxyConstructorParams: proxyConstructorParams, + proxyAddress: proxyAddress + }); + } + function registerAssetIdOnBridgehub() internal { IBridgehub bridgehub = IBridgehub(config.bridgehub); Ownable ownable = Ownable(config.bridgehub); - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, config.baseToken); + INativeTokenVault ntv = INativeTokenVault(config.nativeTokenVault); + bytes32 baseTokenAssetId = ntv.assetId(config.baseToken); + uint256 baseTokenOriginChain = ntv.originChainId(baseTokenAssetId); + + if (baseTokenAssetId == bytes32(0)) { + baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, config.baseToken); + } if (bridgehub.assetIdIsRegistered(baseTokenAssetId)) { console.log("Base token asset id already registered on Bridgehub"); @@ -155,13 +314,18 @@ contract RegisterZKChainScript is Script { function registerTokenOnNTV() internal { INativeTokenVault ntv = INativeTokenVault(config.nativeTokenVault); - // Ownable ownable = Ownable(config.nativeTokenVault); - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, config.baseToken); + bytes32 baseTokenAssetId = ntv.assetId(config.baseToken); + uint256 baseTokenOriginChain = ntv.originChainId(baseTokenAssetId); + + // If it hasn't been registered already with ntv + if (baseTokenAssetId == bytes32(0)) { + baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, config.baseToken); + } config.baseTokenAssetId = baseTokenAssetId; if (ntv.tokenAddress(baseTokenAssetId) != address(0) || config.baseToken == ETH_TOKEN_ADDRESS) { console.log("Token already registered on NTV"); } else { - // bytes memory data = abi.encodeCall(ntv.registerToken, (config.baseToken)); + vm.broadcast(); ntv.registerToken(config.baseToken); console.log("Token registered on NTV"); } @@ -175,19 +339,20 @@ contract RegisterZKChainScript is Script { config.governanceMinDelay ); console.log("Governance deployed at:", address(governance)); - config.governance = address(governance); + output.governance = address(governance); } function deployChainAdmin() internal { vm.broadcast(); AccessControlRestriction restriction = new AccessControlRestriction(0, config.ownerAddress); + output.accessControlRestrictionAddress = address(restriction); address[] memory restrictions = new address[](1); restrictions[0] = address(restriction); vm.broadcast(); ChainAdmin chainAdmin = new ChainAdmin(restrictions); - config.chainAdmin = address(chainAdmin); + output.chainAdmin = address(chainAdmin); } function registerZKChain() internal { @@ -199,12 +364,12 @@ contract RegisterZKChainScript is Script { bridgehub.createNewChain, ( config.chainChainId, - config.stateTransitionProxy, + config.chainTypeManagerProxy, config.baseTokenAssetId, config.bridgehubCreateNewChainSalt, msg.sender, abi.encode(config.diamondCutData, config.forceDeployments), - new bytes[](0) + getFactoryDeps() ) ); Utils.executeUpgrade({ @@ -230,7 +395,7 @@ contract RegisterZKChainScript is Script { if (diamondProxyAddress == address(0)) { revert("Diamond proxy address not found"); } - config.newDiamondProxy = diamondProxyAddress; + output.diamondProxy = diamondProxyAddress; console.log("ZKChain diamond proxy deployed at:", diamondProxyAddress); } @@ -246,7 +411,7 @@ contract RegisterZKChainScript is Script { } function configureZkSyncStateTransition() internal { - IZKChain zkChain = IZKChain(config.newDiamondProxy); + IZKChain zkChain = IZKChain(output.diamondProxy); vm.startBroadcast(msg.sender); zkChain.setTokenMultiplier( @@ -263,21 +428,74 @@ contract RegisterZKChainScript is Script { } function setPendingAdmin() internal { - IZKChain zkChain = IZKChain(config.newDiamondProxy); + IZKChain zkChain = IZKChain(output.diamondProxy); vm.startBroadcast(msg.sender); - zkChain.setPendingAdmin(config.chainAdmin); + zkChain.setPendingAdmin(output.chainAdmin); + vm.stopBroadcast(); + console.log("Owner for ", output.diamondProxy, "set to", output.chainAdmin); + } + + function deployChainProxyAddress() internal { + vm.startBroadcast(); + ProxyAdmin proxyAdmin = new ProxyAdmin(); + proxyAdmin.transferOwnership(output.chainAdmin); vm.stopBroadcast(); - console.log("Owner for ", config.newDiamondProxy, "set to", config.chainAdmin); + console.log("Transparent Proxy Admin deployed at:", address(proxyAdmin)); + output.chainProxyAdmin = address(proxyAdmin); } - function saveOutput() internal { - vm.serializeAddress("root", "diamond_proxy_addr", config.newDiamondProxy); - vm.serializeAddress("root", "chain_admin_addr", config.chainAdmin); - string memory toml = vm.serializeAddress("root", "governance_addr", config.governance); + function deployLegacySharedBridge() internal { + bytes[] memory emptyDeps = new bytes[](0); + address correctLegacyBridgeImplAddr = Utils.deployThroughL1({ + bytecode: L2ContractsBytecodesLib.readL2LegacySharedBridgeBytecode(), + constructorargs: legacySharedBridgeParams.implementationConstructorParams, + create2salt: "", + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: emptyDeps, + chainId: config.chainChainId, + bridgehubAddress: config.bridgehub, + l1SharedBridgeProxy: config.sharedBridgeProxy + }); + + address correctProxyAddress = Utils.deployThroughL1({ + bytecode: L2ContractsBytecodesLib.readTransparentUpgradeableProxyBytecode(), + constructorargs: legacySharedBridgeParams.proxyConstructorParams, + create2salt: "", + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: emptyDeps, + chainId: config.chainChainId, + bridgehubAddress: config.bridgehub, + l1SharedBridgeProxy: config.sharedBridgeProxy + }); + + require( + correctLegacyBridgeImplAddr == legacySharedBridgeParams.implementationAddress, + "Legacy bridge implementation address mismatch" + ); + require(correctProxyAddress == legacySharedBridgeParams.proxyAddress, "Legacy bridge proxy address mismatch"); + + output.l2LegacySharedBridge = correctProxyAddress; + } + + function getFactoryDeps() internal view returns (bytes[] memory) { + bytes[] memory factoryDeps = new bytes[](3); + factoryDeps[0] = L2ContractsBytecodesLib.readBeaconProxyBytecode(); + factoryDeps[1] = L2ContractsBytecodesLib.readStandardERC20Bytecode(); + factoryDeps[2] = L2ContractsBytecodesLib.readUpgradeableBeaconBytecode(); + return factoryDeps; + } + + function saveOutput(string memory outputPath) internal { + vm.serializeAddress("root", "diamond_proxy_addr", output.diamondProxy); + vm.serializeAddress("root", "chain_admin_addr", output.chainAdmin); + vm.serializeAddress("root", "l2_legacy_shared_bridge_addr", output.l2LegacySharedBridge); + vm.serializeAddress("root", "access_control_restriction_addr", output.accessControlRestrictionAddress); + vm.serializeAddress("root", "chain_proxy_admin_addr", output.chainProxyAdmin); + + string memory toml = vm.serializeAddress("root", "governance_addr", output.governance); string memory root = vm.projectRoot(); - string memory path = string.concat(root, "/script-out/output-register-zkChain.toml"); - vm.writeToml(toml, path); - console.log("Output saved at:", path); + vm.writeToml(toml, outputPath); + console.log("Output saved at:", outputPath); } } From c04834b639c009db7ecc3e977eb7419b86cc5ce3 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 16:52:06 +0400 Subject: [PATCH 49/60] (fix): merge related issues --- .../bridge/asset-router/L2AssetRouter.sol | 41 +------- .../bridge/ntv/INativeTokenVault.sol | 3 - .../contracts/common/L1ContractErrors.sol | 2 - .../governance/PermanentRestriction.sol | 79 ++++++--------- .../chain-deps/GatewayCTMDeployer.sol | 2 +- .../_SharedL2ContractDeployer.sol | 2 +- .../Governance/PermanentRestriction.t.sol | 95 +++++++------------ .../GatewayCTMDeployer.t.sol | 2 +- 8 files changed, 68 insertions(+), 158 deletions(-) diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 045bf4693..04d92e3ac 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -130,16 +130,6 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { emit DepositFinalizedAssetRouter(L1_CHAIN_ID, _assetId, _transferData); } - /*////////////////////////////////////////////////////////////// - Internal & Helpers - //////////////////////////////////////////////////////////////*/ - - /// @inheritdoc AssetRouterBase - function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) { - IL2NativeTokenVault nativeTokenVault = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR); - nativeTokenVault.ensureTokenIsRegistered(_token); - } - /*////////////////////////////////////////////////////////////// LEGACY FUNCTIONS //////////////////////////////////////////////////////////////*/ @@ -171,7 +161,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { /// @dev Only used when deposit is made with legacy data encoding format. /// @param _token The L2 token address which should be registered with native token vault. /// @return assetId The asset ID of the token provided. - function _ensureTokenRegisteredWithNTV(address _token) internal returns (bytes32 assetId) { + function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) { IL2NativeTokenVault nativeTokenVault = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR); nativeTokenVault.ensureTokenIsRegistered(_token); } @@ -294,35 +284,6 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { this.finalizeDeposit(L1_CHAIN_ID, assetId, data); } - function finalizeDepositLegacyBridge( - address _l1Sender, - address _l2Receiver, - address _l1Token, - uint256 _amount, - bytes calldata _data - ) external onlyLegacyBridge { - _translateLegacyFinalizeDeposit({ - _l1Sender: _l1Sender, - _l2Receiver: _l2Receiver, - _l1Token: _l1Token, - _amount: _amount, - _data: _data - }); - } - - function _translateLegacyFinalizeDeposit( - address _l1Sender, - address _l2Receiver, - address _l1Token, - uint256 _amount, - bytes calldata _data - ) internal { - bytes32 assetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, _l1Token); - // solhint-disable-next-line func-named-parameters - bytes memory data = DataEncoding.encodeBridgeMintData(_l1Sender, _l2Receiver, _l1Token, _amount, _data); - this.finalizeDeposit(L1_CHAIN_ID, assetId, data); - } - /// @notice Initiates a withdrawal by burning funds on the contract and sending the message to L1 /// where tokens would be unlocked /// @dev A compatibility method to support legacy functionality for the SDK. diff --git a/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol index 90bfb712e..12718bd6f 100644 --- a/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol @@ -33,9 +33,6 @@ interface INativeTokenVault { /// @dev This function is used to ensure that the token is registered with the NTV. function ensureTokenIsRegistered(address _nativeToken) external; - /// @notice Used to get the assetId of a token - function getAssetId(uint256 _chainId, address _tokenAddress) external view returns (bytes32); - /// @notice Used to get the the ERC20 data for a token function getERC20Getters(address _token, uint256 _originChainId) external view returns (bytes memory); diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 90319113a..05d6e06fb 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -408,8 +408,6 @@ error NotBridgehub(address addr); error InvalidAddress(address expected, address actual); // 0xfa5cd00f error NotAllowed(address addr); -// 0x64846fe4 -error NotARestriction(address addr); // 0xccdd18d2 error BytecodeAlreadyPublished(bytes32 bytecodeHash); // 0x25d8333c diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index dbb8b0384..61b2c4344 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.24; -import {CallNotAllowed, RemovingPermanentRestriction, ZeroAddress, UnallowedImplementation, AlreadyWhitelisted, NotAllowed} from "../common/L1ContractErrors.sol"; +import {UnsupportedEncodingVersion, CallNotAllowed, ChainZeroAddress, NotAHyperchain, NotAnAdmin, RemovingPermanentRestriction, ZeroAddress, UnallowedImplementation, AlreadyWhitelisted, NotAllowed, NotBridgehub, InvalidSelector, InvalidAddress, NotEnoughGas} from "../common/L1ContractErrors.sol"; import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "../bridgehub/IBridgehub.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; @@ -19,6 +19,11 @@ import {IAdmin} from "../state-transition/chain-interfaces/IAdmin.sol"; import {IPermanentRestriction} from "./IPermanentRestriction.sol"; +/// @dev We use try-catch to test whether some of the conditions should be checked. +/// To avoid attacks based on the 63/64 gas limitations, we ensure that each such call +/// has at least this amount. +uint256 constant MIN_GAS_FOR_FALLABLE_CALL = 5_000_000; + /// @title PermanentRestriction contract /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev @@ -135,6 +140,8 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste if (!allowedL2Admins[admin]) { revert NotAllowed(admin); } + } catch { + // It was not the migration call, so we do nothing } } @@ -222,7 +229,7 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// admin of the chain. function tryCompareAdminOfAChain(address _chain, address _potentialAdmin) external view { if (_chain == address(0)) { - return false; + revert ChainZeroAddress(); } // Unfortunately there is no easy way to double check that indeed the `_chain` is a ZkSyncHyperchain. @@ -231,27 +238,27 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste // - Query the Bridgehub for the Hyperchain with the given `chainId`. // - We compare the corresponding addresses - // Note, that we do use assembly here to ensure that the function does not panic in case of - // either incorrect `_chain` address or in case the returndata is too large - - (uint256 chainId, bool chainIdQuerySuccess) = _getChainIdUnffallibleCall(_chain); - - if (!chainIdQuerySuccess) { - // It is not a hyperchain, so we can return `false` here. - return false; + // Note, that we do not use an explicit call here to ensure that the function does not panic in case of + // incorrect `_chain` address. + (bool success, bytes memory data) = _chain.staticcall(abi.encodeWithSelector(IGetters.getChainId.selector)); + if (!success || data.length < 32) { + revert NotAHyperchain(_chain); } + // Can not fail + uint256 chainId = abi.decode(data, (uint256)); + // Note, that here it is important to use the legacy `getHyperchain` function, so that the contract // is compatible with the legacy ones. if (BRIDGE_HUB.getHyperchain(chainId) != _chain) { - // It is not a hyperchain, so we can return `false` here. - return false; + revert NotAHyperchain(_chain); } - // Now, the chain is known to be a hyperchain, so it must implement the corresponding interface + // Now, the chain is known to be a hyperchain, so it should implement the corresponding interface address admin = IZKChain(_chain).getAdmin(); - - return admin == msg.sender; + if (admin != _potentialAdmin) { + revert NotAnAdmin(admin, _potentialAdmin); + } } /// @notice Tries to call `IGetters.getChainId()` function on the `_potentialChainAddress`. @@ -283,73 +290,45 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @notice Tries to get the new admin from the migration. /// @param _call The call data. - /// @return Returns a tuple of of the new admin and whether the transaction is indeed the migration. - /// If the second item is `false`, the caller should ignore the first value. - /// @dev If any other error is returned, it is assumed to be out of gas or some other unexpected - /// error that should be bubbled up by the caller. - function _getNewAdminFromMigration(Call calldata _call) internal view returns (address, bool) { + /// @dev This function reverts if the provided call was not a migration call. + function tryGetNewAdminFromMigration(Call calldata _call) external view returns (address) { if (_call.target != address(BRIDGE_HUB)) { - return (address(0), false); - } - - if (_call.data.length < 4) { - return (address(0), false); + revert NotBridgehub(_call.target); } if (bytes4(_call.data[:4]) != IBridgehub.requestL2TransactionTwoBridges.selector) { - return (address(0), false); + revert InvalidSelector(bytes4(_call.data[:4])); } address sharedBridge = BRIDGE_HUB.sharedBridge(); - // Assuming that correctly encoded calldata is provided, the following line must never fail, - // since the correct selector was checked before. L2TransactionRequestTwoBridgesOuter memory request = abi.decode( _call.data[4:], (L2TransactionRequestTwoBridgesOuter) ); if (request.secondBridgeAddress != sharedBridge) { - return (address(0), false); + revert InvalidAddress(sharedBridge, request.secondBridgeAddress); } bytes memory secondBridgeData = request.secondBridgeCalldata; - if (secondBridgeData.length == 0) { - return (address(0), false); - } - if (secondBridgeData[0] != NEW_ENCODING_VERSION) { - return (address(0), false); + revert UnsupportedEncodingVersion(); } bytes memory encodedData = new bytes(secondBridgeData.length - 1); assembly { mcopy(add(encodedData, 0x20), add(secondBridgeData, 0x21), mload(encodedData)) } - // From now on, we know that the used encoding version is `NEW_ENCODING_VERSION` that is - // supported only in the new protocol version with Gateway support, so we can assume - // that the methods like e.g. Bridgehub.ctmAssetIdToAddress must exist. - - // This is the format of the `secondBridgeData` under the `NEW_ENCODING_VERSION`. - // If it fails, it would mean that the data is not correct and the call would eventually fail anyway. (bytes32 chainAssetId, bytes memory bridgehubData) = abi.decode(encodedData, (bytes32, bytes)); - // We will just check that the chainAssetId is a valid chainAssetId. // For now, for simplicity, we do not check that the admin is exactly the admin // of this chain. address ctmAddress = BRIDGE_HUB.ctmAssetIdToAddress(chainAssetId); if (ctmAddress == address(0)) { - return (address(0), false); - } - - // Almost certainly it will be Bridgehub, but we add this check just in case we have circumstances - // that require us to use a different asset handler. - address assetHandlerAddress = IAssetRouterBase(sharedBridge).assetHandlerAddress(chainAssetId); - if (assetHandlerAddress != address(BRIDGE_HUB)) { - return (address(0), false); + revert ZeroAddress(); } - // The asset handler of CTM is the bridgehub and so the following decoding should work BridgehubBurnCTMAssetData memory burnData = abi.decode(bridgehubData, (BridgehubBurnCTMAssetData)); (address l2Admin, ) = abi.decode(burnData.ctmData, (address, bytes)); diff --git a/l1-contracts/contracts/state-transition/chain-deps/GatewayCTMDeployer.sol b/l1-contracts/contracts/state-transition/chain-deps/GatewayCTMDeployer.sol index 09b2c7c3b..dbfdf4ba8 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/GatewayCTMDeployer.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/GatewayCTMDeployer.sol @@ -176,7 +176,7 @@ contract GatewayCTMDeployer { }); _deployVerifier(salt, _config.testnetVerifier, contracts); - ValidatorTimelock timelock = new ValidatorTimelock{salt: salt}(address(this), 0, eraChainId); + ValidatorTimelock timelock = new ValidatorTimelock{salt: salt}(address(this), 0); contracts.stateTransition.validatorTimelock = address(timelock); _deployCTM(salt, _config, contracts); diff --git a/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractDeployer.sol b/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractDeployer.sol index 4e72c7d25..b0c0ac6f7 100644 --- a/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractDeployer.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractDeployer.sol @@ -117,7 +117,7 @@ abstract contract SharedL2ContractDeployer is Test, DeployUtils { vm.prank(ownerWallet); l2Bridgehub.addChainTypeManager(address(addresses.stateTransition.chainTypeManagerProxy)); vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1CTMDeployer)); - l2Bridgehub.setAssetHandlerAddress( + l2Bridgehub.setCTMAssetAddress( bytes32(uint256(uint160(l1CTM))), address(addresses.stateTransition.chainTypeManagerProxy) ); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index 525f89f60..04089ad25 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -7,9 +7,9 @@ import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "co import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {ChainTypeManager} from "contracts/state-transition/ChainTypeManager.sol"; import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol"; -import {PermanentRestriction} from "contracts/governance/PermanentRestriction.sol"; +import {PermanentRestriction, MIN_GAS_FOR_FALLABLE_CALL} from "contracts/governance/PermanentRestriction.sol"; import {IPermanentRestriction} from "contracts/governance/IPermanentRestriction.sol"; -import {NotAllowed, NotEnoughGas, UnsupportedEncodingVersion, InvalidSelector, ZeroAddress, UnallowedImplementation, RemovingPermanentRestriction, CallNotAllowed} from "contracts/common/L1ContractErrors.sol"; +import {NotAllowed, NotEnoughGas, InvalidAddress, UnsupportedEncodingVersion, InvalidSelector, NotBridgehub, ZeroAddress, ChainZeroAddress, NotAnAdmin, UnallowedImplementation, RemovingPermanentRestriction, CallNotAllowed} from "contracts/common/L1ContractErrors.sol"; import {Call} from "contracts/governance/Common.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {VerifierParams, FeeParams, PubdataPricingMode} from "contracts/state-transition/chain-deps/ZKChainStorage.sol"; @@ -26,25 +26,11 @@ import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; -import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; -import {IERC20Metadata} from "@openzeppelin/contracts-v4/token/ERC20/extensions/IERC20Metadata.sol"; - -contract TestPermanentRestriction is PermanentRestriction { - constructor(IBridgehub _bridgehub, address _l2AdminFactory) PermanentRestriction(_bridgehub, _l2AdminFactory) {} - - function isAdminOfAChain(address _chain) external view returns (bool) { - return _isAdminOfAChain(_chain); - } - - function getNewAdminFromMigration(Call calldata _call) external view returns (address, bool) { - return _getNewAdminFromMigration(_call); - } -} contract PermanentRestrictionTest is ChainTypeManagerTest { ChainAdmin internal chainAdmin; AccessControlRestriction internal restriction; - TestPermanentRestriction internal permRestriction; + PermanentRestriction internal permRestriction; address constant L2_FACTORY_ADDR = address(0); @@ -69,19 +55,19 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { IBridgehub _bridgehub, address _l2AdminFactory, address _owner - ) internal returns (TestPermanentRestriction proxy, TestPermanentRestriction impl) { - impl = new TestPermanentRestriction(_bridgehub, _l2AdminFactory); + ) internal returns (PermanentRestriction proxy, PermanentRestriction impl) { + impl = new PermanentRestriction(_bridgehub, _l2AdminFactory); TransparentUpgradeableProxy tup = new TransparentUpgradeableProxy( address(impl), address(uint160(1)), abi.encodeCall(PermanentRestriction.initialize, (_owner)) ); - proxy = TestPermanentRestriction(address(tup)); + proxy = PermanentRestriction(address(tup)); } function test_ownerAsAddressZero() public { - TestPermanentRestriction impl = new TestPermanentRestriction(bridgehub, L2_FACTORY_ADDR); + PermanentRestriction impl = new PermanentRestriction(bridgehub, L2_FACTORY_ADDR); vm.expectRevert(ZeroAddress.selector); new TransparentUpgradeableProxy( address(impl), @@ -114,27 +100,23 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { permRestriction.setSelectorShouldBeValidated(selector, true); } - function isAddressAdmin(address chainAddr, address _potentialAdmin) internal returns (bool) { - // The permanent restriction compares it only against the msg.sender, - // so we have to use `prank` to test the function - vm.prank(_potentialAdmin); - return permRestriction.isAdminOfAChain(chainAddr); - } - function test_tryCompareAdminOfAChainIsAddressZero() public { - assertFalse(isAddressAdmin(address(0), owner)); + vm.expectRevert(ChainZeroAddress.selector); + permRestriction.tryCompareAdminOfAChain(address(0), owner); } function test_tryCompareAdminOfAChainNotAHyperchain() public { - assertFalse(isAddressAdmin(makeAddr("random"), owner)); + vm.expectRevert(); + permRestriction.tryCompareAdminOfAChain(makeAddr("random"), owner); } function test_tryCompareAdminOfAChainNotAnAdmin() public { - assertFalse(isAddressAdmin(hyperchain, owner)); + vm.expectRevert(abi.encodeWithSelector(NotAnAdmin.selector, IZKChain(hyperchain).getAdmin(), owner)); + permRestriction.tryCompareAdminOfAChain(hyperchain, owner); } function test_tryCompareAdminOfAChain() public { - assertTrue(isAddressAdmin(hyperchain, newChainAdmin)); + permRestriction.tryCompareAdminOfAChain(hyperchain, newChainAdmin); } function test_validateCallTooShortData() public { @@ -293,49 +275,47 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { call.data = abi.encodeCall(Bridgehub.requestL2TransactionTwoBridges, (outer)); } - function assertInvalidMigrationCall(Call memory call) public { - (address newAdmin, bool migration) = permRestriction.getNewAdminFromMigration(call); - assertFalse(migration); - assertEq(newAdmin, address(0)); - } - function test_tryGetNewAdminFromMigrationRevertWhenInvalidSelector() public { Call memory call = _encodeMigraationCall(false, true, true, true, true, address(0)); - assertInvalidMigrationCall(call); + vm.expectRevert(abi.encodeWithSelector(NotBridgehub.selector, address(0))); + permRestriction.tryGetNewAdminFromMigration(call); } function test_tryGetNewAdminFromMigrationRevertWhenNotBridgehub() public { Call memory call = _encodeMigraationCall(true, false, true, true, true, address(0)); - assertInvalidMigrationCall(call); + vm.expectRevert(abi.encodeWithSelector(InvalidSelector.selector, bytes4(0))); + permRestriction.tryGetNewAdminFromMigration(call); } function test_tryGetNewAdminFromMigrationRevertWhenNotSharedBridge() public { Call memory call = _encodeMigraationCall(true, true, false, true, true, address(0)); - assertInvalidMigrationCall(call); + vm.expectRevert(abi.encodeWithSelector(InvalidAddress.selector, address(sharedBridge), address(0))); + permRestriction.tryGetNewAdminFromMigration(call); } function test_tryGetNewAdminFromMigrationRevertWhenIncorrectEncoding() public { Call memory call = _encodeMigraationCall(true, true, true, false, true, address(0)); - assertInvalidMigrationCall(call); + vm.expectRevert(abi.encodeWithSelector(UnsupportedEncodingVersion.selector)); + permRestriction.tryGetNewAdminFromMigration(call); } function test_tryGetNewAdminFromMigrationRevertWhenIncorrectAssetId() public { Call memory call = _encodeMigraationCall(true, true, true, true, false, address(0)); - assertInvalidMigrationCall(call); + vm.expectRevert(abi.encodeWithSelector(ZeroAddress.selector)); + permRestriction.tryGetNewAdminFromMigration(call); } function test_tryGetNewAdminFromMigrationShouldWorkCorrectly() public { address l2Addr = makeAddr("l2Addr"); Call memory call = _encodeMigraationCall(true, true, true, true, true, l2Addr); - (address newAdmin, bool migration) = permRestriction.getNewAdminFromMigration(call); - assertTrue(migration); - assertEq(newAdmin, l2Addr); + address result = permRestriction.tryGetNewAdminFromMigration(call); + assertEq(result, l2Addr); } function test_validateMigrationToL2RevertNotAllowed() public { @@ -363,6 +343,14 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { permRestriction.validateCall(call, owner); } + function test_validateNotEnoughGas() public { + address l2Addr = makeAddr("l2Addr"); + Call memory call = _encodeMigraationCall(true, true, true, true, true, l2Addr); + + vm.expectRevert(abi.encodeWithSelector(NotEnoughGas.selector)); + permRestriction.validateCall{gas: MIN_GAS_FOR_FALLABLE_CALL}(call, address(0)); + } + function createNewChainBridgehub() internal { bytes[] memory factoryDeps = new bytes[](0); vm.stopPrank(); @@ -386,19 +374,6 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { abi.encodeWithSelector(IL1AssetRouter.L1_NULLIFIER.selector), abi.encode(l1Nullifier) ); - vm.mockCall( - address(sharedBridge), - abi.encodeWithSelector(IAssetRouterBase.assetHandlerAddress.selector), - abi.encode(bridgehub) - ); - vm.mockCall( - address(bridgehub), - abi.encodeWithSelector(Bridgehub.baseToken.selector, chainId), - abi.encode(baseToken) - ); - vm.mockCall(address(baseToken), abi.encodeWithSelector(IERC20Metadata.name.selector), abi.encode("TestToken")); - vm.mockCall(address(baseToken), abi.encodeWithSelector(IERC20Metadata.symbol.selector), abi.encode("TT")); - vm.startPrank(governor); bridgehub.createNewChain({ _chainId: chainId, @@ -411,4 +386,4 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { }); vm.stopPrank(); } -} +} \ No newline at end of file diff --git a/l1-contracts/test/foundry/l2/unit/GatewayCTMDeployer/GatewayCTMDeployer.t.sol b/l1-contracts/test/foundry/l2/unit/GatewayCTMDeployer/GatewayCTMDeployer.t.sol index e04432c9d..f016b6559 100644 --- a/l1-contracts/test/foundry/l2/unit/GatewayCTMDeployer/GatewayCTMDeployer.t.sol +++ b/l1-contracts/test/foundry/l2/unit/GatewayCTMDeployer/GatewayCTMDeployer.t.sol @@ -73,7 +73,7 @@ contract GatewayCTMDeployerTest is Test { new TestnetVerifier(); new Verifier(); - new ValidatorTimelock(address(0), 0, 0); + new ValidatorTimelock(address(0), 0); // This call will likely fail due to various checks, but we just need to get the bytecode published try new TransparentUpgradeableProxy(address(0), address(0), hex"") {} catch {} From ba27094876513850d3d3554f1d3f87a5299787e8 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 16:56:21 +0400 Subject: [PATCH 50/60] (fix): formatting --- l1-contracts/contracts/governance/PermanentRestriction.sol | 2 +- .../l1/unit/concrete/Governance/PermanentRestriction.t.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 61b2c4344..fe2e24e6d 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -7,7 +7,7 @@ import {UnsupportedEncodingVersion, CallNotAllowed, ChainZeroAddress, NotAHyperc import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "../bridgehub/IBridgehub.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; import {L2ContractHelper} from "../common/libraries/L2ContractHelper.sol"; -import {NEW_ENCODING_VERSION, IAssetRouterBase} from "../bridge/asset-router/IAssetRouterBase.sol"; +import {NEW_ENCODING_VERSION} from "../bridge/asset-router/IAssetRouterBase.sol"; import {Call} from "./Common.sol"; import {Restriction} from "./restriction/Restriction.sol"; diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index 04089ad25..51267c38a 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -386,4 +386,4 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { }); vm.stopPrank(); } -} \ No newline at end of file +} From 36f9f2130bb76cbb93993d72f1f83198eb7cf1e6 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 17:03:43 +0400 Subject: [PATCH 51/60] (fix): add back errors removed by merge --- l1-contracts/contracts/common/L1ContractErrors.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 10e89fdcc..40b42517e 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -149,6 +149,8 @@ error InvalidSelector(bytes4 func); error InvalidTxType(uint256 txType); // 0x0214acb6 error InvalidUpgradeTxn(UpgradeTxVerifyParam); +// 0x2554babc +error InvalidAddress(address expected, address actual); // 0xfb5c22e6 error L2TimestampTooBig(); // 0xd2c011d6 @@ -197,6 +199,8 @@ error NonSequentialVersion(); error NotEnoughGas(); // 0xdd7e3621 error NotInitializedReentrancyGuard(); +// 0x10f30e75 +error NotBridgehub(address addr); // 0xdf17e316 error NotWhitelisted(address); // 0xf3ed9dfa From abe4790cdcac3d66cca981dcf21a2aa8ffd06c09 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 17:09:03 +0400 Subject: [PATCH 52/60] (fix): lint --- l1-contracts/contracts/bridge/L1Nullifier.sol | 2 +- l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 8999fcf98..7be97e544 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -29,7 +29,7 @@ import {IBridgehub} from "../bridgehub/IBridgehub.sol"; import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR, L2_ASSET_ROUTER_ADDR} from "../common/L2ContractAddresses.sol"; import {DataEncoding} from "../common/libraries/DataEncoding.sol"; import {Unauthorized, SharedBridgeKey, DepositExists, AddressAlreadySet, InvalidProof, DepositDoesNotExist, SharedBridgeValueNotSet, WithdrawalAlreadyFinalized, L2WithdrawalMessageWrongLength, InvalidSelector, SharedBridgeValueNotSet, ZeroAddress} from "../common/L1ContractErrors.sol"; -import {WrongL2Sender, NotNTV, NativeTokenVaultAlreadySet, EthTransferFailed, WrongMsgLength} from "./L1BridgeContractErrors.sol"; +import {WrongL2Sender, NativeTokenVaultAlreadySet, EthTransferFailed, WrongMsgLength} from "./L1BridgeContractErrors.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index e26ec47ed..cb1ff5c22 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -19,7 +19,7 @@ import {DataEncoding} from "../../common/libraries/DataEncoding.sol"; import {BridgedStandardERC20} from "../BridgedStandardERC20.sol"; import {BridgeHelper} from "../BridgeHelper.sol"; -import {DeployingBridgedTokenForNativeToken, EmptyDeposit, Unauthorized, TokensWithFeesNotSupported, TokenNotSupported, NonEmptyMsgValue, ValueMismatch, AddressMismatch, AssetIdMismatch, AmountMustBeGreaterThanZero, ZeroAddress} from "../../common/L1ContractErrors.sol"; +import {AssetIdAlreadyRegistered, DeployingBridgedTokenForNativeToken, EmptyDeposit, Unauthorized, TokensWithFeesNotSupported, TokenNotSupported, NonEmptyMsgValue, ValueMismatch, AddressMismatch, AssetIdMismatch, AmountMustBeGreaterThanZero, ZeroAddress} from "../../common/L1ContractErrors.sol"; import {EmptyToken} from "../L1BridgeContractErrors.sol"; /// @author Matter Labs @@ -93,7 +93,9 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 if (_nativeToken.code.length == 0) { revert EmptyToken(); } - require(assetId[_nativeToken] == bytes32(0), "NTV: asset id already registered"); + if (assetId[_nativeToken] != bytes32(0)) { + revert AssetIdAlreadyRegistered(); + } _unsafeRegisterNativeToken(_nativeToken); } From 22837cba414f801d548aba37109d14b2633eca17 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 17:19:25 +0400 Subject: [PATCH 53/60] (fix): missing bracket --- l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol b/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol index 194e2ebc7..aaf51357a 100644 --- a/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol +++ b/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol @@ -258,7 +258,7 @@ contract GatewayCTMFromL1 is Script { protocolVersion: config.latestProtocolVersion }); } - + function distributeBaseToken() internal { deployerAddress = msg.sender; uint256 amountForDistribution = 100000000000000000000; @@ -275,6 +275,7 @@ contract GatewayCTMFromL1 is Script { baseToken.transfer(config.governanceAddr, amountForDistribution); } vm.stopBroadcast(); + } function saveOutput() internal { vm.serializeAddress( From f47695fd661388a37402dd2b3786d77e59f9e5a2 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 17:32:35 +0400 Subject: [PATCH 54/60] (fix): unused errors --- l1-contracts/contracts/common/L1ContractErrors.sol | 8 -------- l2-contracts/contracts/errors/L2ContractErrors.sol | 2 -- 2 files changed, 10 deletions(-) diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 40b42517e..efb80d184 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -101,10 +101,6 @@ error EmptyDeposit(); error ETHDepositNotSupported(); // 0xac4a3f98 error FacetExists(bytes4 selector, address); -// 0x79e12cc3 -error FacetIsFrozen(bytes4 func); -// 0xd37a223a -error FunctionNotSupported(); // 0xc91cf3b1 error GasPerPubdataMismatch(); // 0x6d4a7df8 @@ -129,8 +125,6 @@ error InsufficientChainBalance(); error InvalidCaller(address); // 0x4fbe5dba error InvalidDelay(); -// 0x0af806e0 -error InvalidHash(); // 0xc1780bd6 error InvalidLogSender(address sender, uint256 logKey); // 0xd8e9405c @@ -293,8 +287,6 @@ error Unauthorized(address caller); error UndefinedDiamondCutAction(); // 0x6aa39880 error UnexpectedSystemLog(uint256 logKey); -// 0xa4dde386 -error UnimplementedMessage(string); // 0xf093c2e5 error UpgradeBatchNumberIsNotZero(); // 0x084a1449 diff --git a/l2-contracts/contracts/errors/L2ContractErrors.sol b/l2-contracts/contracts/errors/L2ContractErrors.sol index 21c1366d1..332c1b8b7 100644 --- a/l2-contracts/contracts/errors/L2ContractErrors.sol +++ b/l2-contracts/contracts/errors/L2ContractErrors.sol @@ -26,8 +26,6 @@ error InvalidInput(); error NonSequentialVersion(); // 0x8e4a23d6 error Unauthorized(address); -// 0x6e128399 -error Unimplemented(); // 0xff15b069 error UnsupportedPaymasterFlow(); // 0x750b219c From 7177fc4d8c4cb62a7b46e8fbe516d0547963468b Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 17:39:55 +0400 Subject: [PATCH 55/60] (fix): linting --- l1-contracts/contracts/bridge/L1BridgeContractErrors.sol | 3 --- .../concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1BridgeContractErrors.sol b/l1-contracts/contracts/bridge/L1BridgeContractErrors.sol index 4db576573..d72cf85a2 100644 --- a/l1-contracts/contracts/bridge/L1BridgeContractErrors.sol +++ b/l1-contracts/contracts/bridge/L1BridgeContractErrors.sol @@ -2,9 +2,6 @@ pragma solidity ^0.8.21; -// 0xe4efb466 -error NotNTV(); - // 0x6d963f88 error EthTransferFailed(); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol index 4a7686c2f..2990e6695 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol @@ -24,7 +24,7 @@ import {IGetters} from "contracts/state-transition/chain-interfaces/IGetters.sol import {AddressAlreadyUsed, WithdrawFailed, Unauthorized, AssetIdNotSupported, SharedBridgeKey, SharedBridgeValueNotSet, L2WithdrawalMessageWrongLength, InsufficientChainBalance, ZeroAddress, ValueMismatch, NonEmptyMsgValue, DepositExists, ValueMismatch, NonEmptyMsgValue, TokenNotSupported, EmptyDeposit, InvalidProof, NoFundsTransferred, DepositDoesNotExist, WithdrawalAlreadyFinalized, InvalidSelector, TokensWithFeesNotSupported} from "contracts/common/L1ContractErrors.sol"; import {StdStorage, stdStorage} from "forge-std/Test.sol"; import {DepositNotSet} from "test/foundry/L1TestsErrors.sol"; -import {WrongCounterpart, EthTransferFailed, NotNTV, EmptyToken, NativeTokenVaultAlreadySet, ZeroAmountToTransfer, WrongAmountTransferred, ClaimFailedDepositFailed} from "contracts/bridge/L1BridgeContractErrors.sol"; +import {WrongCounterpart, EthTransferFailed, EmptyToken, NativeTokenVaultAlreadySet, ZeroAmountToTransfer, WrongAmountTransferred, ClaimFailedDepositFailed} from "contracts/bridge/L1BridgeContractErrors.sol"; /// We are testing all the specified revert and require cases. contract L1AssetRouterFailTest is L1AssetRouterTest { @@ -63,7 +63,7 @@ contract L1AssetRouterFailTest is L1AssetRouterTest { } function test_nullifyChainBalanceByNTV_wrongCaller() public { - vm.expectRevert(NotNTV.selector); + vm.expectRevert(); l1Nullifier.nullifyChainBalanceByNTV(chainId, address(token)); } From 13fbe9c53ccf09d042a753f4555af5da8e848768 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 18:30:00 +0400 Subject: [PATCH 56/60] (fix): foundry test --- .../_SharedL2ContractL1DeployerUtils.sol | 1 - .../Governance/PermanentRestriction.t.sol | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractL1DeployerUtils.sol b/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractL1DeployerUtils.sol index a363c0cd0..c5076c3c1 100644 --- a/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractL1DeployerUtils.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractL1DeployerUtils.sol @@ -89,7 +89,6 @@ contract SharedL2ContractL1DeployerUtils is DeployUtils { ); vm.etch(L2_ASSET_ROUTER_ADDR, assetRouter.code); - stdstore.target(address(L2_ASSET_ROUTER_ADDR)).sig("l1AssetRouter()").checked_write(_args.l1AssetRouter); stdstore .target(L2_ASSET_ROUTER_ADDR) diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index 51267c38a..074f8024c 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -1,6 +1,9 @@ pragma solidity 0.8.24; +import {Script, console2 as console} from "forge-std/Script.sol"; + import "@openzeppelin/contracts-v4/utils/Strings.sol"; +import {IERC20Metadata} from "@openzeppelin/contracts-v4/token/ERC20/extensions/IERC20Metadata.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; @@ -23,6 +26,8 @@ import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.s import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; +import {INativeTokenVault} from "contracts/bridge/ntv/INativeTokenVault.sol"; import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; @@ -374,6 +379,18 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { abi.encodeWithSelector(IL1AssetRouter.L1_NULLIFIER.selector), abi.encode(l1Nullifier) ); + vm.mockCall( + address(sharedBridge), + abi.encodeWithSelector(IAssetRouterBase.assetHandlerAddress.selector), + abi.encode(sharedBridge) + ); + vm.mockCall( + address(sharedBridge), + abi.encodeWithSelector(INativeTokenVault.tokenAddress.selector), + abi.encode(baseToken) + ); + vm.mockCall(address(baseToken), abi.encodeWithSelector(IERC20Metadata.name.selector), abi.encode("TestToken")); + vm.mockCall(address(baseToken), abi.encodeWithSelector(IERC20Metadata.symbol.selector), abi.encode("TT")); vm.startPrank(governor); bridgehub.createNewChain({ _chainId: chainId, From 966ee4de65aefd6e8d3dbd8c6a91f0bf46327e0c Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 18:59:36 +0400 Subject: [PATCH 57/60] (fix): zkfoundry test --- l1-contracts/deploy-scripts/GatewayCTMDeployerHelper.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/deploy-scripts/GatewayCTMDeployerHelper.sol b/l1-contracts/deploy-scripts/GatewayCTMDeployerHelper.sol index 50af31f88..5bb7f7585 100644 --- a/l1-contracts/deploy-scripts/GatewayCTMDeployerHelper.sol +++ b/l1-contracts/deploy-scripts/GatewayCTMDeployerHelper.sol @@ -66,7 +66,7 @@ library GatewayCTMDeployerHelper { contracts.stateTransition.validatorTimelock = _deployInternal( "ValidatorTimelock", "ValidatorTimelock.sol", - abi.encode(ctmDeployerAddress, 0, eraChainId), + abi.encode(ctmDeployerAddress, 0), innerConfig ); From ea174081b5f1520e2818c2c905d8fb2ea713ef5b Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 19:26:32 +0400 Subject: [PATCH 58/60] (fix): incorrect merge --- .../bridge/ntv/L2NativeTokenVault.sol | 2 - .../contracts/bridge/ntv/NativeTokenVault.sol | 2 +- .../governance/PermanentRestriction.sol | 127 +++++++++--------- .../Governance/PermanentRestriction.t.sol | 108 ++++++++------- 4 files changed, 124 insertions(+), 115 deletions(-) diff --git a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol index cc6682ce0..379a67daf 100644 --- a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol @@ -204,8 +204,6 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { uint256 _originChainId, address _nonNativeToken ) public view virtual override(INativeTokenVault, NativeTokenVault) returns (address) { - bytes32 constructorInputHash = keccak256(abi.encode(address(bridgedTokenBeacon), "")); - bytes32 salt = _getCreate2Salt(_originChainId, _nonNativeToken); if (address(L2_LEGACY_SHARED_BRIDGE) != address(0)) { return L2_LEGACY_SHARED_BRIDGE.l2TokenAddress(_nonNativeToken); } else { diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index cb1ff5c22..1ee41fdf6 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -174,7 +174,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 //////////////////////////////////////////////////////////////*/ /// @inheritdoc IAssetHandler - /// @notice Allows bridgehub to acquire mintValue for L1->L2 and L2->L1 transactions. + /// @notice Allows bridgehub to acquire mintValue for L1->L2 transactions. /// @dev In case of native token vault _data is the tuple of _depositAmount and _receiver. function bridgeBurn( uint256 _chainId, diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 425fec23a..1e8b6d948 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -2,12 +2,12 @@ pragma solidity 0.8.24; -import {UnsupportedEncodingVersion, CallNotAllowed, ChainZeroAddress, NotAHyperchain, NotAnAdmin, RemovingPermanentRestriction, ZeroAddress, UnallowedImplementation, AlreadyWhitelisted, NotAllowed, NotBridgehub, InvalidSelector, InvalidAddress, NotEnoughGas} from "../common/L1ContractErrors.sol"; +import {CallNotAllowed, RemovingPermanentRestriction, ZeroAddress, UnallowedImplementation, AlreadyWhitelisted, NotAllowed} from "../common/L1ContractErrors.sol"; import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "../bridgehub/IBridgehub.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; import {L2ContractHelper} from "../common/libraries/L2ContractHelper.sol"; -import {NEW_ENCODING_VERSION} from "../bridge/asset-router/IAssetRouterBase.sol"; +import {NEW_ENCODING_VERSION, IAssetRouterBase} from "../bridge/asset-router/IAssetRouterBase.sol"; import {Call} from "./Common.sol"; import {Restriction} from "./restriction/Restriction.sol"; @@ -19,18 +19,13 @@ import {IAdmin} from "../state-transition/chain-interfaces/IAdmin.sol"; import {IPermanentRestriction} from "./IPermanentRestriction.sol"; -/// @dev We use try-catch to test whether some of the conditions should be checked. -/// To avoid attacks based on the 63/64 gas limitations, we ensure that each such call -/// has at least this amount. -uint256 constant MIN_GAS_FOR_FALLABLE_CALL = 5_000_000; - /// @title PermanentRestriction contract /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @notice This contract should be used by chains that wish to guarantee that certain security /// properties are preserved forever. /// @dev To be deployed as a transparent upgradable proxy, owned by a trusted decentralized governance. -/// @dev One of the instances of such contract is enough to ensure that a ZkSyncHyperchain is a rollup forever. +/// @dev Once of the instances of such contract is to ensure that a ZkSyncHyperchain is a rollup forever. contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2StepUpgradeable { /// @notice The address of the Bridgehub contract. IBridgehub public immutable BRIDGE_HUB; @@ -48,13 +43,12 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste mapping(bytes allowedCalldata => bool isAllowed) public allowedCalls; /// @notice The mapping of the validated selectors. - mapping(bytes4 selector => bool isValidated) public selectorsToValidate; + mapping(bytes4 selector => bool isValidated) public validatedSelectors; /// @notice The mapping of whitelisted L2 admins. mapping(address adminAddress => bool isWhitelisted) public allowedL2Admins; constructor(IBridgehub _bridgehub, address _l2AdminFactory) { - _disableInitializers(); BRIDGE_HUB = _bridgehub; L2_ADMIN_FACTORY = _l2AdminFactory; } @@ -73,7 +67,7 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @notice Allows a certain `ChainAdmin` implementation to be used as an admin. /// @param _implementationHash The hash of the implementation code. /// @param _isAllowed The flag that indicates if the implementation is allowed. - function setAllowedAdminImplementation(bytes32 _implementationHash, bool _isAllowed) external onlyOwner { + function allowAdminImplementation(bytes32 _implementationHash, bool _isAllowed) external onlyOwner { allowedAdminImplementations[_implementationHash] = _isAllowed; emit AdminImplementationAllowed(_implementationHash, _isAllowed); @@ -91,8 +85,8 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @notice Allows a certain selector to be validated. /// @param _selector The selector of the function. /// @param _isValidated The flag that indicates if the selector is validated. - function setSelectorShouldBeValidated(bytes4 _selector, bool _isValidated) external onlyOwner { - selectorsToValidate[_selector] = _isValidated; + function setSelectorIsValidated(bytes4 _selector, bool _isValidated) external onlyOwner { + validatedSelectors[_selector] = _isValidated; emit SelectorValidationChanged(_selector, _isValidated); } @@ -133,20 +127,18 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @param _call The call data. /// @dev Note that we do not need to validate the migration to the L1 layer as the admin /// is not changed in this case. - function _validateMigrationToL2(Call calldata _call) private view { - _ensureEnoughGas(); - try this.tryGetNewAdminFromMigration(_call) returns (address admin) { + function _validateMigrationToL2(Call calldata _call) internal view { + (address admin, bool isMigration) = _getNewAdminFromMigration(_call); + if (isMigration) { if (!allowedL2Admins[admin]) { revert NotAllowed(admin); } - } catch { - // It was not the migration call, so we do nothing } } /// @notice Validates the call as the chain admin /// @param _call The call data. - function _validateAsChainAdmin(Call calldata _call) private view { + function _validateAsChainAdmin(Call calldata _call) internal view { if (!_isAdminOfAChain(_call.target)) { // We only validate calls related to being an admin of a chain return; @@ -165,7 +157,7 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste return; } - if (!selectorsToValidate[selector]) { + if (!validatedSelectors[selector]) { // The selector is not validated, any data is allowed. return; } @@ -178,7 +170,7 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @notice Validates the correctness of the new admin. /// @param _call The call data. /// @dev Ensures that the admin has a whitelisted implementation and does not remove this restriction. - function _validateNewAdmin(Call calldata _call) private view { + function _validateNewAdmin(Call calldata _call) internal view { address newChainAdmin = abi.decode(_call.data[4:], (address)); bytes32 implementationCodeHash = newChainAdmin.codehash; @@ -197,7 +189,7 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @notice Validates the removal of the restriction. /// @param _call The call data. /// @dev Ensures that this restriction is not removed. - function _validateRemoveRestriction(Call calldata _call) private view { + function _validateRemoveRestriction(Call calldata _call) internal view { if (_call.target != msg.sender) { return; } @@ -215,20 +207,9 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @notice Checks if the `msg.sender` is an admin of a certain ZkSyncHyperchain. /// @param _chain The address of the chain. - function _isAdminOfAChain(address _chain) private view returns (bool) { - _ensureEnoughGas(); - (bool success, ) = address(this).staticcall(abi.encodeCall(this.tryCompareAdminOfAChain, (_chain, msg.sender))); - return success; - } - - /// @notice Tries to compare the admin of a chain with the potential admin. - /// @param _chain The address of the chain. - /// @param _potentialAdmin The address of the potential admin. - /// @dev This function reverts if the `_chain` is not a ZkSyncHyperchain or the `_potentialAdmin` is not the - /// admin of the chain. - function tryCompareAdminOfAChain(address _chain, address _potentialAdmin) external view { + function _isAdminOfAChain(address _chain) internal view returns (bool) { if (_chain == address(0)) { - revert ChainZeroAddress(); + return false; } // Unfortunately there is no easy way to double check that indeed the `_chain` is a ZkSyncHyperchain. @@ -237,27 +218,27 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste // - Query the Bridgehub for the Hyperchain with the given `chainId`. // - We compare the corresponding addresses - // Note, that we do not use an explicit call here to ensure that the function does not panic in case of - // incorrect `_chain` address. - (bool success, bytes memory data) = _chain.staticcall(abi.encodeWithSelector(IGetters.getChainId.selector)); - if (!success || data.length < 32) { - revert NotAHyperchain(_chain); - } + // Note, that we do use assembly here to ensure that the function does not panic in case of + // either incorrect `_chain` address or in case the returndata is too large + + (uint256 chainId, bool chainIdQuerySuccess) = _getChainIdUnffallibleCall(_chain); - // Can not fail - uint256 chainId = abi.decode(data, (uint256)); + if (!chainIdQuerySuccess) { + // It is not a hyperchain, so we can return `false` here. + return false; + } // Note, that here it is important to use the legacy `getHyperchain` function, so that the contract // is compatible with the legacy ones. if (BRIDGE_HUB.getHyperchain(chainId) != _chain) { - revert NotAHyperchain(_chain); + // It is not a hyperchain, so we can return `false` here. + return false; } - // Now, the chain is known to be a hyperchain, so it should implement the corresponding interface + // Now, the chain is known to be a hyperchain, so it must implement the corresponding interface address admin = IZKChain(_chain).getAdmin(); - if (admin != _potentialAdmin) { - revert NotAnAdmin(admin, _potentialAdmin); - } + + return admin == msg.sender; } /// @notice Tries to call `IGetters.getChainId()` function on the `_potentialChainAddress`. @@ -289,54 +270,76 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @notice Tries to get the new admin from the migration. /// @param _call The call data. - /// @dev This function reverts if the provided call was not a migration call. - function tryGetNewAdminFromMigration(Call calldata _call) external view returns (address) { + /// @return Returns a tuple of of the new admin and whether the transaction is indeed the migration. + /// If the second item is `false`, the caller should ignore the first value. + /// @dev If any other error is returned, it is assumed to be out of gas or some other unexpected + /// error that should be bubbled up by the caller. + function _getNewAdminFromMigration(Call calldata _call) internal view returns (address, bool) { if (_call.target != address(BRIDGE_HUB)) { - revert NotBridgehub(_call.target); + return (address(0), false); + } + + if (_call.data.length < 4) { + return (address(0), false); } if (bytes4(_call.data[:4]) != IBridgehub.requestL2TransactionTwoBridges.selector) { - revert InvalidSelector(bytes4(_call.data[:4])); + return (address(0), false); } address sharedBridge = BRIDGE_HUB.sharedBridge(); + // Assuming that correctly encoded calldata is provided, the following line must never fail, + // since the correct selector was checked before. L2TransactionRequestTwoBridgesOuter memory request = abi.decode( _call.data[4:], (L2TransactionRequestTwoBridgesOuter) ); if (request.secondBridgeAddress != sharedBridge) { - revert InvalidAddress(sharedBridge, request.secondBridgeAddress); + return (address(0), false); } bytes memory secondBridgeData = request.secondBridgeCalldata; + if (secondBridgeData.length == 0) { + return (address(0), false); + } + if (secondBridgeData[0] != NEW_ENCODING_VERSION) { - revert UnsupportedEncodingVersion(); + return (address(0), false); } bytes memory encodedData = new bytes(secondBridgeData.length - 1); assembly { mcopy(add(encodedData, 0x20), add(secondBridgeData, 0x21), mload(encodedData)) } + // From now on, we know that the used encoding version is `NEW_ENCODING_VERSION` that is + // supported only in the new protocol version with Gateway support, so we can assume + // that the methods like e.g. Bridgehub.ctmAssetIdToAddress must exist. + + // This is the format of the `secondBridgeData` under the `NEW_ENCODING_VERSION`. + // If it fails, it would mean that the data is not correct and the call would eventually fail anyway. (bytes32 chainAssetId, bytes memory bridgehubData) = abi.decode(encodedData, (bytes32, bytes)); + // We will just check that the chainAssetId is a valid chainAssetId. // For now, for simplicity, we do not check that the admin is exactly the admin // of this chain. address ctmAddress = BRIDGE_HUB.ctmAssetIdToAddress(chainAssetId); if (ctmAddress == address(0)) { - revert ZeroAddress(); + return (address(0), false); } + // Almost certainly it will be Bridgehub, but we add this check just in case we have circumstances + // that require us to use a different asset handler. + address assetHandlerAddress = IAssetRouterBase(sharedBridge).assetHandlerAddress(chainAssetId); + if (assetHandlerAddress != address(BRIDGE_HUB)) { + return (address(0), false); + } + + // The asset handler of CTM is the bridgehub and so the following decoding should work BridgehubBurnCTMAssetData memory burnData = abi.decode(bridgehubData, (BridgehubBurnCTMAssetData)); (address l2Admin, ) = abi.decode(burnData.ctmData, (address, bytes)); - return l2Admin; - } - - function _ensureEnoughGas() private view { - if (gasleft() < MIN_GAS_FOR_FALLABLE_CALL) { - revert NotEnoughGas(); - } + return (l2Admin, true); } } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index 074f8024c..6644b2bef 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -1,18 +1,15 @@ pragma solidity 0.8.24; -import {Script, console2 as console} from "forge-std/Script.sol"; - import "@openzeppelin/contracts-v4/utils/Strings.sol"; -import {IERC20Metadata} from "@openzeppelin/contracts-v4/token/ERC20/extensions/IERC20Metadata.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {ChainTypeManager} from "contracts/state-transition/ChainTypeManager.sol"; import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol"; -import {PermanentRestriction, MIN_GAS_FOR_FALLABLE_CALL} from "contracts/governance/PermanentRestriction.sol"; +import {PermanentRestriction} from "contracts/governance/PermanentRestriction.sol"; import {IPermanentRestriction} from "contracts/governance/IPermanentRestriction.sol"; -import {NotAllowed, NotEnoughGas, InvalidAddress, UnsupportedEncodingVersion, InvalidSelector, NotBridgehub, ZeroAddress, ChainZeroAddress, NotAnAdmin, UnallowedImplementation, RemovingPermanentRestriction, CallNotAllowed} from "contracts/common/L1ContractErrors.sol"; +import {NotAllowed, NotEnoughGas, UnsupportedEncodingVersion, InvalidSelector, ZeroAddress, UnallowedImplementation, RemovingPermanentRestriction, CallNotAllowed} from "contracts/common/L1ContractErrors.sol"; import {Call} from "contracts/governance/Common.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {VerifierParams, FeeParams, PubdataPricingMode} from "contracts/state-transition/chain-deps/ZKChainStorage.sol"; @@ -26,16 +23,28 @@ import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.s import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; -import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; -import {INativeTokenVault} from "contracts/bridge/ntv/INativeTokenVault.sol"; import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; +import {IERC20Metadata} from "@openzeppelin/contracts-v4/token/ERC20/extensions/IERC20Metadata.sol"; + +contract TestPermanentRestriction is PermanentRestriction { + constructor(IBridgehub _bridgehub, address _l2AdminFactory) PermanentRestriction(_bridgehub, _l2AdminFactory) {} + + function isAdminOfAChain(address _chain) external view returns (bool) { + return _isAdminOfAChain(_chain); + } + + function getNewAdminFromMigration(Call calldata _call) external view returns (address, bool) { + return _getNewAdminFromMigration(_call); + } +} contract PermanentRestrictionTest is ChainTypeManagerTest { ChainAdmin internal chainAdmin; AccessControlRestriction internal restriction; - PermanentRestriction internal permRestriction; + TestPermanentRestriction internal permRestriction; address constant L2_FACTORY_ADDR = address(0); @@ -60,19 +69,19 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { IBridgehub _bridgehub, address _l2AdminFactory, address _owner - ) internal returns (PermanentRestriction proxy, PermanentRestriction impl) { - impl = new PermanentRestriction(_bridgehub, _l2AdminFactory); + ) internal returns (TestPermanentRestriction proxy, TestPermanentRestriction impl) { + impl = new TestPermanentRestriction(_bridgehub, _l2AdminFactory); TransparentUpgradeableProxy tup = new TransparentUpgradeableProxy( address(impl), address(uint160(1)), abi.encodeCall(PermanentRestriction.initialize, (_owner)) ); - proxy = PermanentRestriction(address(tup)); + proxy = TestPermanentRestriction(address(tup)); } function test_ownerAsAddressZero() public { - PermanentRestriction impl = new PermanentRestriction(bridgehub, L2_FACTORY_ADDR); + TestPermanentRestriction impl = new TestPermanentRestriction(bridgehub, L2_FACTORY_ADDR); vm.expectRevert(ZeroAddress.selector); new TransparentUpgradeableProxy( address(impl), @@ -81,12 +90,12 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { ); } - function test_setAllowedAdminImplementation(bytes32 implementationHash) public { + function test_allowAdminImplementation(bytes32 implementationHash) public { vm.expectEmit(true, false, false, true); emit IPermanentRestriction.AdminImplementationAllowed(implementationHash, true); vm.prank(owner); - permRestriction.setAllowedAdminImplementation(implementationHash, true); + permRestriction.allowAdminImplementation(implementationHash, true); } function test_setAllowedData(bytes memory data) public { @@ -97,31 +106,35 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { permRestriction.setAllowedData(data, true); } - function test_setSelectorShouldBeValidated(bytes4 selector) public { + function test_setSelectorIsValidated(bytes4 selector) public { vm.expectEmit(true, false, false, true); emit IPermanentRestriction.SelectorValidationChanged(selector, true); vm.prank(owner); - permRestriction.setSelectorShouldBeValidated(selector, true); + permRestriction.setSelectorIsValidated(selector, true); + } + + function isAddressAdmin(address chainAddr, address _potentialAdmin) internal returns (bool) { + // The permanent restriction compares it only against the msg.sender, + // so we have to use `prank` to test the function + vm.prank(_potentialAdmin); + return permRestriction.isAdminOfAChain(chainAddr); } function test_tryCompareAdminOfAChainIsAddressZero() public { - vm.expectRevert(ChainZeroAddress.selector); - permRestriction.tryCompareAdminOfAChain(address(0), owner); + assertFalse(isAddressAdmin(address(0), owner)); } function test_tryCompareAdminOfAChainNotAHyperchain() public { - vm.expectRevert(); - permRestriction.tryCompareAdminOfAChain(makeAddr("random"), owner); + assertFalse(isAddressAdmin(makeAddr("random"), owner)); } function test_tryCompareAdminOfAChainNotAnAdmin() public { - vm.expectRevert(abi.encodeWithSelector(NotAnAdmin.selector, IZKChain(hyperchain).getAdmin(), owner)); - permRestriction.tryCompareAdminOfAChain(hyperchain, owner); + assertFalse(isAddressAdmin(hyperchain, owner)); } function test_tryCompareAdminOfAChain() public { - permRestriction.tryCompareAdminOfAChain(hyperchain, newChainAdmin); + assertTrue(isAddressAdmin(hyperchain, newChainAdmin)); } function test_validateCallTooShortData() public { @@ -148,7 +161,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_validateCallSetPendingAdminRemovingPermanentRestriction() public { vm.prank(owner); - permRestriction.setAllowedAdminImplementation(address(chainAdmin).codehash, true); + permRestriction.allowAdminImplementation(address(chainAdmin).codehash, true); Call memory call = Call({ target: hyperchain, @@ -165,7 +178,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_validateCallSetPendingAdmin() public { vm.prank(owner); - permRestriction.setAllowedAdminImplementation(address(chainAdmin).codehash, true); + permRestriction.allowAdminImplementation(address(chainAdmin).codehash, true); vm.prank(address(chainAdmin)); chainAdmin.addRestriction(address(permRestriction)); @@ -195,7 +208,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_validateCallCallNotAllowed() public { vm.prank(owner); - permRestriction.setSelectorShouldBeValidated(IAdmin.acceptAdmin.selector, true); + permRestriction.setSelectorIsValidated(IAdmin.acceptAdmin.selector, true); Call memory call = Call({ target: hyperchain, value: 0, @@ -211,7 +224,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_validateCall() public { vm.prank(owner); - permRestriction.setSelectorShouldBeValidated(IAdmin.acceptAdmin.selector, true); + permRestriction.setSelectorIsValidated(IAdmin.acceptAdmin.selector, true); Call memory call = Call({ target: hyperchain, value: 0, @@ -280,47 +293,49 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { call.data = abi.encodeCall(Bridgehub.requestL2TransactionTwoBridges, (outer)); } + function assertInvalidMigrationCall(Call memory call) public { + (address newAdmin, bool migration) = permRestriction.getNewAdminFromMigration(call); + assertFalse(migration); + assertEq(newAdmin, address(0)); + } + function test_tryGetNewAdminFromMigrationRevertWhenInvalidSelector() public { Call memory call = _encodeMigraationCall(false, true, true, true, true, address(0)); - vm.expectRevert(abi.encodeWithSelector(NotBridgehub.selector, address(0))); - permRestriction.tryGetNewAdminFromMigration(call); + assertInvalidMigrationCall(call); } function test_tryGetNewAdminFromMigrationRevertWhenNotBridgehub() public { Call memory call = _encodeMigraationCall(true, false, true, true, true, address(0)); - vm.expectRevert(abi.encodeWithSelector(InvalidSelector.selector, bytes4(0))); - permRestriction.tryGetNewAdminFromMigration(call); + assertInvalidMigrationCall(call); } function test_tryGetNewAdminFromMigrationRevertWhenNotSharedBridge() public { Call memory call = _encodeMigraationCall(true, true, false, true, true, address(0)); - vm.expectRevert(abi.encodeWithSelector(InvalidAddress.selector, address(sharedBridge), address(0))); - permRestriction.tryGetNewAdminFromMigration(call); + assertInvalidMigrationCall(call); } function test_tryGetNewAdminFromMigrationRevertWhenIncorrectEncoding() public { Call memory call = _encodeMigraationCall(true, true, true, false, true, address(0)); - vm.expectRevert(abi.encodeWithSelector(UnsupportedEncodingVersion.selector)); - permRestriction.tryGetNewAdminFromMigration(call); + assertInvalidMigrationCall(call); } function test_tryGetNewAdminFromMigrationRevertWhenIncorrectAssetId() public { Call memory call = _encodeMigraationCall(true, true, true, true, false, address(0)); - vm.expectRevert(abi.encodeWithSelector(ZeroAddress.selector)); - permRestriction.tryGetNewAdminFromMigration(call); + assertInvalidMigrationCall(call); } function test_tryGetNewAdminFromMigrationShouldWorkCorrectly() public { address l2Addr = makeAddr("l2Addr"); Call memory call = _encodeMigraationCall(true, true, true, true, true, l2Addr); - address result = permRestriction.tryGetNewAdminFromMigration(call); - assertEq(result, l2Addr); + (address newAdmin, bool migration) = permRestriction.getNewAdminFromMigration(call); + assertTrue(migration); + assertEq(newAdmin, l2Addr); } function test_validateMigrationToL2RevertNotAllowed() public { @@ -348,14 +363,6 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { permRestriction.validateCall(call, owner); } - function test_validateNotEnoughGas() public { - address l2Addr = makeAddr("l2Addr"); - Call memory call = _encodeMigraationCall(true, true, true, true, true, l2Addr); - - vm.expectRevert(abi.encodeWithSelector(NotEnoughGas.selector)); - permRestriction.validateCall{gas: MIN_GAS_FOR_FALLABLE_CALL}(call, address(0)); - } - function createNewChainBridgehub() internal { bytes[] memory factoryDeps = new bytes[](0); vm.stopPrank(); @@ -382,15 +389,16 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { vm.mockCall( address(sharedBridge), abi.encodeWithSelector(IAssetRouterBase.assetHandlerAddress.selector), - abi.encode(sharedBridge) + abi.encode(bridgehub) ); vm.mockCall( - address(sharedBridge), - abi.encodeWithSelector(INativeTokenVault.tokenAddress.selector), + address(bridgehub), + abi.encodeWithSelector(Bridgehub.baseToken.selector, chainId), abi.encode(baseToken) ); vm.mockCall(address(baseToken), abi.encodeWithSelector(IERC20Metadata.name.selector), abi.encode("TestToken")); vm.mockCall(address(baseToken), abi.encodeWithSelector(IERC20Metadata.symbol.selector), abi.encode("TT")); + vm.startPrank(governor); bridgehub.createNewChain({ _chainId: chainId, From 10fe1d29648d93730979647bc544a293607a9c4e Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 25 Oct 2024 19:30:33 +0400 Subject: [PATCH 59/60] (fix): remove unused errors --- l1-contracts/contracts/common/L1ContractErrors.sol | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index efb80d184..5aa1c0a50 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -15,12 +15,6 @@ error RestrictionWasNotPresent(address restriction); error RestrictionWasAlreadyPresent(address restriction); // 0x3331e9c0 error CallNotAllowed(bytes call); -// 0x59e1b0d2 -error ChainZeroAddress(); -// 0xff4bbdf1 -error NotAHyperchain(address chainAddress); -// 0xa3decdf3 -error NotAnAdmin(address expected, address actual); // 0xf6fd7071 error RemovingPermanentRestriction(); // 0xfcb9b2e1 @@ -143,8 +137,6 @@ error InvalidSelector(bytes4 func); error InvalidTxType(uint256 txType); // 0x0214acb6 error InvalidUpgradeTxn(UpgradeTxVerifyParam); -// 0x2554babc -error InvalidAddress(address expected, address actual); // 0xfb5c22e6 error L2TimestampTooBig(); // 0xd2c011d6 @@ -193,8 +185,6 @@ error NonSequentialVersion(); error NotEnoughGas(); // 0xdd7e3621 error NotInitializedReentrancyGuard(); -// 0x10f30e75 -error NotBridgehub(address addr); // 0xdf17e316 error NotWhitelisted(address); // 0xf3ed9dfa From 343f6716b08049c68c06c0712ed7b635ecc71834 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Sat, 26 Oct 2024 14:47:17 +0400 Subject: [PATCH 60/60] (fix): add back Ns from OZ audit --- .../governance/PermanentRestriction.sol | 21 ++++++++++--------- .../Governance/PermanentRestriction.t.sol | 16 +++++++------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 1e8b6d948..30b586086 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -43,12 +43,13 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste mapping(bytes allowedCalldata => bool isAllowed) public allowedCalls; /// @notice The mapping of the validated selectors. - mapping(bytes4 selector => bool isValidated) public validatedSelectors; + mapping(bytes4 selector => bool isValidated) public selectorsToValidate; /// @notice The mapping of whitelisted L2 admins. mapping(address adminAddress => bool isWhitelisted) public allowedL2Admins; constructor(IBridgehub _bridgehub, address _l2AdminFactory) { + _disableInitializers(); BRIDGE_HUB = _bridgehub; L2_ADMIN_FACTORY = _l2AdminFactory; } @@ -67,7 +68,7 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @notice Allows a certain `ChainAdmin` implementation to be used as an admin. /// @param _implementationHash The hash of the implementation code. /// @param _isAllowed The flag that indicates if the implementation is allowed. - function allowAdminImplementation(bytes32 _implementationHash, bool _isAllowed) external onlyOwner { + function setAllowedAdminImplementation(bytes32 _implementationHash, bool _isAllowed) external onlyOwner { allowedAdminImplementations[_implementationHash] = _isAllowed; emit AdminImplementationAllowed(_implementationHash, _isAllowed); @@ -85,8 +86,8 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @notice Allows a certain selector to be validated. /// @param _selector The selector of the function. /// @param _isValidated The flag that indicates if the selector is validated. - function setSelectorIsValidated(bytes4 _selector, bool _isValidated) external onlyOwner { - validatedSelectors[_selector] = _isValidated; + function setSelectorShouldBeValidated(bytes4 _selector, bool _isValidated) external onlyOwner { + selectorsToValidate[_selector] = _isValidated; emit SelectorValidationChanged(_selector, _isValidated); } @@ -127,7 +128,7 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @param _call The call data. /// @dev Note that we do not need to validate the migration to the L1 layer as the admin /// is not changed in this case. - function _validateMigrationToL2(Call calldata _call) internal view { + function _validateMigrationToL2(Call calldata _call) private view { (address admin, bool isMigration) = _getNewAdminFromMigration(_call); if (isMigration) { if (!allowedL2Admins[admin]) { @@ -138,7 +139,7 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @notice Validates the call as the chain admin /// @param _call The call data. - function _validateAsChainAdmin(Call calldata _call) internal view { + function _validateAsChainAdmin(Call calldata _call) private view { if (!_isAdminOfAChain(_call.target)) { // We only validate calls related to being an admin of a chain return; @@ -157,7 +158,7 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste return; } - if (!validatedSelectors[selector]) { + if (!selectorsToValidate[selector]) { // The selector is not validated, any data is allowed. return; } @@ -170,7 +171,7 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @notice Validates the correctness of the new admin. /// @param _call The call data. /// @dev Ensures that the admin has a whitelisted implementation and does not remove this restriction. - function _validateNewAdmin(Call calldata _call) internal view { + function _validateNewAdmin(Call calldata _call) private view { address newChainAdmin = abi.decode(_call.data[4:], (address)); bytes32 implementationCodeHash = newChainAdmin.codehash; @@ -189,7 +190,7 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @notice Validates the removal of the restriction. /// @param _call The call data. /// @dev Ensures that this restriction is not removed. - function _validateRemoveRestriction(Call calldata _call) internal view { + function _validateRemoveRestriction(Call calldata _call) private view { if (_call.target != msg.sender) { return; } @@ -248,7 +249,7 @@ contract PermanentRestriction is Restriction, IPermanentRestriction, Ownable2Ste /// @return success Whether the `chain` is indeed an address of a ZK Chain. /// @dev Returns a tuple of the chainId and whether the call was successful. /// If the second item is `false`, the caller should ignore the first value. - function _getChainIdUnffallibleCall(address _chain) internal view returns (uint256 chainId, bool success) { + function _getChainIdUnffallibleCall(address _chain) private view returns (uint256 chainId, bool success) { bytes4 selector = IGetters.getChainId.selector; assembly { // We use scratch space here, so it is safe diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index 6644b2bef..525f89f60 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -90,12 +90,12 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { ); } - function test_allowAdminImplementation(bytes32 implementationHash) public { + function test_setAllowedAdminImplementation(bytes32 implementationHash) public { vm.expectEmit(true, false, false, true); emit IPermanentRestriction.AdminImplementationAllowed(implementationHash, true); vm.prank(owner); - permRestriction.allowAdminImplementation(implementationHash, true); + permRestriction.setAllowedAdminImplementation(implementationHash, true); } function test_setAllowedData(bytes memory data) public { @@ -106,12 +106,12 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { permRestriction.setAllowedData(data, true); } - function test_setSelectorIsValidated(bytes4 selector) public { + function test_setSelectorShouldBeValidated(bytes4 selector) public { vm.expectEmit(true, false, false, true); emit IPermanentRestriction.SelectorValidationChanged(selector, true); vm.prank(owner); - permRestriction.setSelectorIsValidated(selector, true); + permRestriction.setSelectorShouldBeValidated(selector, true); } function isAddressAdmin(address chainAddr, address _potentialAdmin) internal returns (bool) { @@ -161,7 +161,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_validateCallSetPendingAdminRemovingPermanentRestriction() public { vm.prank(owner); - permRestriction.allowAdminImplementation(address(chainAdmin).codehash, true); + permRestriction.setAllowedAdminImplementation(address(chainAdmin).codehash, true); Call memory call = Call({ target: hyperchain, @@ -178,7 +178,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_validateCallSetPendingAdmin() public { vm.prank(owner); - permRestriction.allowAdminImplementation(address(chainAdmin).codehash, true); + permRestriction.setAllowedAdminImplementation(address(chainAdmin).codehash, true); vm.prank(address(chainAdmin)); chainAdmin.addRestriction(address(permRestriction)); @@ -208,7 +208,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_validateCallCallNotAllowed() public { vm.prank(owner); - permRestriction.setSelectorIsValidated(IAdmin.acceptAdmin.selector, true); + permRestriction.setSelectorShouldBeValidated(IAdmin.acceptAdmin.selector, true); Call memory call = Call({ target: hyperchain, value: 0, @@ -224,7 +224,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_validateCall() public { vm.prank(owner); - permRestriction.setSelectorIsValidated(IAdmin.acceptAdmin.selector, true); + permRestriction.setSelectorShouldBeValidated(IAdmin.acceptAdmin.selector, true); Call memory call = Call({ target: hyperchain, value: 0,