Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(registration): Add Chain registrar contract #1064

Open
wants to merge 57 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
ec8e9da
Deploy legacy bridge inside foundry (#787)
Deniallugo Sep 10, 2024
73b20c4
deploy legacy bridge (#795)
Deniallugo Sep 12, 2024
3a1b5d4
use chain admin for bridgehub manipulations (#799)
Deniallugo Sep 12, 2024
9ebf9be
fix(deploy): Initilize chain using ChainAdmin (#807)
Deniallugo Sep 13, 2024
bce4b2d
feat: multicall3 on L2 (#805)
pompon0 Sep 14, 2024
1fba03d
merging main (#824)
koloz193 Sep 27, 2024
939151e
Add utils for DecentralizeGovernance transaction (#829)
vladbochok Oct 2, 2024
aafee03
feat(zk_toolbox): dedicated command for deploying multicall3 on L2 (#…
pompon0 Oct 3, 2024
a7e8923
Merge main back into protocol defense (#839)
koloz193 Oct 4, 2024
8499c67
Merge branch 'main' into release-v25-protocol-defense
koloz193 Oct 4, 2024
efa5d3b
remove whitespace from config
koloz193 Oct 4, 2024
84d5e37
Update foundry bytecode (#764)
Deniallugo Oct 4, 2024
dcb0eca
feat(zkstack): Show chain info (#955)
Deniallugo Oct 22, 2024
72a3e8e
Add time window asserter
ischasny Oct 1, 2024
4c69685
Changed assertion message
ischasny Oct 30, 2024
9ed4ad0
Review feedback
ischasny Oct 31, 2024
26bcdbe
Merge branch 'release-v25-protocol-defense' into ivan/add-time-window…
ischasny Oct 31, 2024
9f3f710
Fix lint
ischasny Oct 31, 2024
698f6ae
Update l2-contracts/contracts/dev-contracts/TimestampAsserter.sol
ischasny Oct 31, 2024
50a0db7
add new lines
ischasny Oct 31, 2024
9fb1264
Merge pull request #843 from matter-labs/ivan/add-time-window-asserter
ischasny Oct 31, 2024
b7b9f1b
fix: add deploy timestap asserter command
ischasny Nov 11, 2024
46d7508
Typo
ischasny Nov 11, 2024
658713f
Merge pull request #1058 from matter-labs/ivan/add-deploy-timestamp-a…
ischasny Nov 11, 2024
9c3118c
add new chain registrator contract
Deniallugo Nov 11, 2024
9fd56a3
Add test
Deniallugo Nov 11, 2024
0589a9b
Make test passes
Deniallugo Nov 12, 2024
ca8edff
Add deployer
Deniallugo Nov 12, 2024
0e9e30d
Fix registrator
Deniallugo Nov 12, 2024
e4b0c85
Simplify propose chain signature
Deniallugo Nov 12, 2024
1804692
Simplify propose chain signature
Deniallugo Nov 12, 2024
9294bac
Finalize registration
Deniallugo Nov 13, 2024
5579d5a
Update initialization
Deniallugo Nov 14, 2024
43b788e
Fix reentranc
Deniallugo Nov 14, 2024
9c148cc
Change to call of get admin
Deniallugo Nov 14, 2024
7610082
Fix lints
Deniallugo Nov 14, 2024
567abc5
Return correct admin
Deniallugo Nov 14, 2024
6ea44a2
Small refactoring
Deniallugo Nov 14, 2024
7a642e5
Fix base token transfering
Deniallugo Nov 16, 2024
d0e3772
Propose chain registration script
Deniallugo Nov 18, 2024
e99c0ff
Propose chain registration script
Deniallugo Nov 18, 2024
9d007bb
Fix script
Deniallugo Nov 18, 2024
9a72fc2
Deploy registrar with transperent proxy
Deniallugo Nov 21, 2024
49befd8
Implemet get deployed config
Deniallugo Nov 21, 2024
b077dd4
Registered chain config as an additional function
Deniallugo Nov 21, 2024
9c138fd
Apply suggestions from code review
Deniallugo Nov 21, 2024
7cc89ac
Try to fix the test
Deniallugo Nov 21, 2024
139474f
Properly fix test
Deniallugo Nov 21, 2024
5828b73
Apply review suggestions
Deniallugo Nov 21, 2024
6ed3ebc
Remove unnnecessary functions
Deniallugo Nov 22, 2024
9ef8a21
Add more comments
Deniallugo Nov 22, 2024
8ceaa5b
Add more comments
Deniallugo Nov 22, 2024
2953478
Merge branch 'dev' into deniallugo-chain-registrator
Deniallugo Nov 25, 2024
2b54930
Fix system contracts
Deniallugo Nov 25, 2024
3388f52
Add more tests
Deniallugo Nov 25, 2024
8101384
Apply suggestions from code review
Deniallugo Nov 25, 2024
f0b38c0
Fix review
Deniallugo Nov 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions l1-contracts/contracts/chain-registrar/ChainRegistrar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import {IBridgehub} from "../bridgehub/IBridgehub.sol";
import {PubdataPricingMode} from "../state-transition/chain-deps/ZkSyncHyperchainStorage.sol";
import {IStateTransitionManager} from "../state-transition/IStateTransitionManager.sol";
import {ETH_TOKEN_ADDRESS} from "../common/Config.sol";
import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol";
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol";
import {IGetters} from "../state-transition/chain-interfaces/IGetters.sol";
import {SafeERC20} from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol";

/// @title ChainRegistrar Contract
/// @author Matter Labs
/// @custom:security-contact [email protected]
/// @notice This contract is used for proposing and registering new chains in the zkSync ecosystem.
Deniallugo marked this conversation as resolved.
Show resolved Hide resolved
/// @notice It helps chain administrators retrieve all necessary L1 information about their chain.
Deniallugo marked this conversation as resolved.
Show resolved Hide resolved
/// @notice Additionally, it assists zkSync administrators in verifying the correctness of registration transactions.
Deniallugo marked this conversation as resolved.
Show resolved Hide resolved
/// @dev ChainRegistrar is designed for use with a proxy for upgradability.
/// @dev It interacts with the Bridgehub for getting chain registration results.
/// @dev This contract does not make write calls to the Bridgehub itself for security reasons.
contract ChainRegistrar is Ownable2StepUpgradeable {
using SafeERC20 for IERC20;

/// @notice Address that will be used for deploying L2 contracts.
/// @dev During the chain proposal, some base tokens must be transferred to this address.
address public l2Deployer;

/// @notice Address of the ZKsync Bridgehub.
Deniallugo marked this conversation as resolved.
Show resolved Hide resolved
IBridgehub public bridgehub;

/// @notice Mapping of proposed chains by author and chain ID.
/// @notice Stores chain proposals made by users, where each address can propose a chain with a unique chain ID.
mapping(address => mapping(uint256 => ChainConfig)) public proposedChains;

/// @dev Thrown when trying to register a chain that is already deployed.
error ChainIsAlreadyDeployed();

/// @dev Thrown when querying information about a chain that is not yet deployed.
error ChainIsNotYetDeployed();

/// @dev Thrown when the bridge for a chain is not registered.
error BridgeIsNotRegistered();

/// @notice Emitted when a new chain registration proposal is made.
/// @param chainId Unique ID of the proposed chain.
/// @param author Address of the proposer.
event NewChainRegistrationProposal(uint256 indexed chainId, address author);

/// @notice Emitted when the L2 deployer address is changed.
/// @param newDeployer Address of the new L2 deployer.
event L2DeployerChanged(address newDeployer);

/// @dev Struct for holding the base token configuration of a chain.
struct BaseToken {
/// @notice Gas price multiplier numerator, used to compare the base token price to ether for L1->L2 transactions.
uint128 gasPriceMultiplierNominator;
/// @notice Gas price multiplier denominator, used to compare the base token price to ether for L1->L2 transactions.
uint128 gasPriceMultiplierDenominator;
/// @notice Address of the base token used for gas fees.
address tokenAddress;
/// @notice Address responsible for setting the token multiplier.
address tokenMultiplierSetter;
}
Deniallugo marked this conversation as resolved.
Show resolved Hide resolved

/// @dev Struct for holding the configuration of a proposed chain.
// solhint-disable-next-line gas-struct-packing
struct ChainConfig {
Deniallugo marked this conversation as resolved.
Show resolved Hide resolved
/// @notice Unique chain ID.
uint256 chainId;
/// @notice Base token configuration for the chain.
BaseToken baseToken;
/// @notice Operator responsible for making commit transactions.
address blobOperator;
/// @notice Operator responsible for making prove and execute transactions.
address operator;
/// @notice Governor of the chain; will receive ownership of the ChainAdmin contract.
address governor;
/// @notice Mode for charging users for pubdata.
PubdataPricingMode pubdataPricingMode;
}

/// @dev Struct for holding the configuration of a fully deployed chain.
// solhint-disable-next-line gas-struct-packing
struct RegisteredChainConfig {
Deniallugo marked this conversation as resolved.
Show resolved Hide resolved
/// @notice Address of the pending admin for the chain.
address pendingChainAdmin;
/// @notice Address of the current admin for the chain.
address chainAdmin;
/// @notice Address of the main contract (diamond proxy) for the deployed chain.
address diamondProxy;
/// @notice Address of the L2 bridge inside the deployed chain.
address l2BridgeAddress;
}

/// @notice Initializes the contract with the given parameters.
/// @dev Can only be called once, during contract deployment.
/// @param _bridgehub Address of the ZKsync Bridgehub.
/// @param _l2Deployer Address of the L2 deployer.
/// @param _owner Address of the contract owner.
function initialize(address _bridgehub, address _l2Deployer, address _owner) public {
Deniallugo marked this conversation as resolved.
Show resolved Hide resolved
bridgehub = IBridgehub(_bridgehub);
l2Deployer = _l2Deployer;
_transferOwnership(_owner);
}

/// @notice Proposes a new chain to be registered in the zkSync ecosystem.
Deniallugo marked this conversation as resolved.
Show resolved Hide resolved
/// @dev The proposal will fail if the chain has already been registered.
/// @dev For non-ETH-based chains, either an equivalent of 1 ETH of the base token must be approved or transferred to the L2 deployer.
/// @param _chainId Unique ID of the proposed chain.
/// @param _pubdataPricingMode Mode for charging users for pubdata.
/// @param _blobOperator Address responsible for commit transactions.
/// @param _operator Address responsible for prove and execute transactions.
/// @param _governor Address to receive ownership of the ChainAdmin contract.
/// @param _baseTokenAddress Address of the base token used for gas fees.
/// @param _tokenMultiplierSetter Address responsible for setting the base token multiplier.
/// @param _gasPriceMultiplierNominator Gas price multiplier numerator for L1->L2 transactions.
/// @param _gasPriceMultiplierDenominator Gas price multiplier denominator for L1->L2 transactions.
function proposeChainRegistration(
uint256 _chainId,
PubdataPricingMode _pubdataPricingMode,
address _blobOperator,
address _operator,
address _governor,
Deniallugo marked this conversation as resolved.
Show resolved Hide resolved
address _baseTokenAddress,
address _tokenMultiplierSetter,
uint128 _gasPriceMultiplierNominator,
uint128 _gasPriceMultiplierDenominator
) external {
ChainConfig memory config = ChainConfig({
sanekmelnikov marked this conversation as resolved.
Show resolved Hide resolved
chainId: _chainId,
pubdataPricingMode: _pubdataPricingMode,
blobOperator: _blobOperator,
operator: _operator,
governor: _governor,
baseToken: BaseToken({
tokenAddress: _baseTokenAddress,
tokenMultiplierSetter: _tokenMultiplierSetter,
gasPriceMultiplierNominator: _gasPriceMultiplierNominator,
gasPriceMultiplierDenominator: _gasPriceMultiplierDenominator
})
});

if (bridgehub.stateTransitionManager(config.chainId) != address(0)) {
revert ChainIsAlreadyDeployed();
}

proposedChains[msg.sender][_chainId] = config;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it makes sense to allow user to re-propose config? I would rather restrict it to make avoid any confusion on the final config (as soon as config is onchain it immutable).

Copy link
Contributor Author

@Deniallugo Deniallugo Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, let's restrict it, i initially thought about it, as a way to give an ability to change it and then make a deployment period when they can't change it. but probably it'd be easier to simply restring it


// Handle base token transfer for non-ETH-based networks.
if (config.baseToken.tokenAddress != ETH_TOKEN_ADDRESS) {
uint256 amount = (1 ether * config.baseToken.gasPriceMultiplierNominator) /
Deniallugo marked this conversation as resolved.
Show resolved Hide resolved
config.baseToken.gasPriceMultiplierDenominator;
if (IERC20(config.baseToken.tokenAddress).balanceOf(l2Deployer) < amount) {
IERC20(config.baseToken.tokenAddress).safeTransferFrom(msg.sender, l2Deployer, amount);
}
}

emit NewChainRegistrationProposal(config.chainId, msg.sender);
}

/// @notice Changes the address of the L2 deployer.
/// @param _newDeployer New address of the L2 deployer.
function changeDeployer(address _newDeployer) public onlyOwner {
l2Deployer = _newDeployer;
emit L2DeployerChanged(l2Deployer);
}

/// @notice Retrieves the configuration of a registered chain by its ID.
/// @param _chainId ID of the chain.
/// @return The configuration of the registered chain.
function getRegisteredChainConfig(uint256 _chainId) public view returns (RegisteredChainConfig memory) {
address stm = bridgehub.stateTransitionManager(_chainId);
if (stm == address(0)) {
revert ChainIsNotYetDeployed();
}

address diamondProxy = IStateTransitionManager(stm).getHyperchain(_chainId);
address pendingChainAdmin = IGetters(diamondProxy).getPendingAdmin();
address chainAdmin = IGetters(diamondProxy).getAdmin();
sanekmelnikov marked this conversation as resolved.
Show resolved Hide resolved
address l2BridgeAddress = bridgehub.sharedBridge().l2BridgeAddress(_chainId);
if (l2BridgeAddress == address(0)) {
revert BridgeIsNotRegistered();
}

RegisteredChainConfig memory config = RegisteredChainConfig({
pendingChainAdmin: pendingChainAdmin,
chainAdmin: chainAdmin,
diamondProxy: diamondProxy,
l2BridgeAddress: l2BridgeAddress
});

return config;
}
}
13 changes: 13 additions & 0 deletions l1-contracts/contracts/dev-contracts/test/DummyHyperchain.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {MailboxFacet} from "../../state-transition/chain-deps/facets/Mailbox.sol
import {FeeParams, PubdataPricingMode} from "../../state-transition/chain-deps/ZkSyncHyperchainStorage.sol";

contract DummyHyperchain is MailboxFacet {
address public admin;
constructor(address bridgeHubAddress, uint256 _eraChainId) MailboxFacet(_eraChainId) {
s.bridgehub = bridgeHubAddress;
}
Expand Down Expand Up @@ -32,6 +33,18 @@ contract DummyHyperchain is MailboxFacet {
s.priorityTxMaxGasLimit = type(uint256).max;
}

function initialize(address _admin) external {
admin = _admin;
}

function getAdmin() external view returns (address) {
return admin;
}

function getPendingAdmin() external view returns (address) {
return admin;
}

function _randomFeeParams() internal pure returns (FeeParams memory) {
return
FeeParams({
Expand Down
27 changes: 27 additions & 0 deletions l1-contracts/deploy-scripts/DeployL1.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {L1SharedBridge} from "contracts/bridge/L1SharedBridge.sol";
import {L1ERC20Bridge} from "contracts/bridge/L1ERC20Bridge.sol";
import {DiamondProxy} from "contracts/state-transition/chain-deps/DiamondProxy.sol";
import {AddressHasNoCode} from "./ZkSyncScriptErrors.sol";
import {ChainRegistrar} from "../contracts/chain-registrar/ChainRegistrar.sol";

contract DeployL1Script is Script {
using stdToml for string;
Expand All @@ -52,6 +53,7 @@ contract DeployL1Script is Script {
address blobVersionedHashRetriever;
address validatorTimelock;
address create2Factory;
address chainRegistrar;
}

// solhint-disable-next-line gas-struct-packing
Expand Down Expand Up @@ -88,6 +90,7 @@ contract DeployL1Script is Script {
uint256 l1ChainId;
uint256 eraChainId;
address deployerAddress;
address l2Deployer;
address ownerAddress;
bool testnetVerifier;
ContractsConfig contracts;
Expand Down Expand Up @@ -157,6 +160,7 @@ contract DeployL1Script is Script {
deployErc20BridgeImplementation();
deployErc20BridgeProxy();
updateSharedBridge();
deployChainRegistrar();

updateOwners();

Expand All @@ -176,6 +180,7 @@ contract DeployL1Script is Script {
// https://book.getfoundry.sh/cheatcodes/parse-toml
config.eraChainId = toml.readUint("$.era_chain_id");
config.ownerAddress = toml.readAddress("$.owner_address");
config.l2Deployer = toml.readAddress("$.l2_deployer");
config.testnetVerifier = toml.readBool("$.testnet_verifier");

config.contracts.governanceSecurityCouncilAddress = toml.readAddress(
Expand Down Expand Up @@ -301,6 +306,27 @@ contract DeployL1Script is Script {
addresses.governance = contractAddress;
}

function deployChainRegistrar() internal {
bytes memory bytecodeImplementation = abi.encodePacked(type(ChainRegistrar).creationCode);
address chainRegistrarImplementation = deployViaCreate2(bytecodeImplementation);
console.log("Chain Registrar implementation deployed at:", chainRegistrarImplementation);

bytes memory bytecode = abi.encodePacked(
type(TransparentUpgradeableProxy).creationCode,
abi.encode(
chainRegistrarImplementation,
addresses.transparentProxyAdmin,
abi.encodeCall(
ChainRegistrar.initialize,
(addresses.bridgehub.bridgehubProxy, config.l2Deployer, config.ownerAddress)
)
)
);
address chainRegistrar = deployViaCreate2(bytecode);
console.log("Chain Registrar deployed at:", chainRegistrar);
addresses.chainRegistrar = chainRegistrar;
}

function deployChainAdmin() internal {
bytes memory accessControlRestrictionBytecode = abi.encodePacked(
type(ChainAdmin).creationCode,
Expand Down Expand Up @@ -722,6 +748,7 @@ contract DeployL1Script is Script {
vm.serializeAddress("deployed_addresses", "chain_admin", addresses.chainAdmin);
vm.serializeString("deployed_addresses", "bridgehub", bridgehub);
vm.serializeString("deployed_addresses", "state_transition", stateTransition);
vm.serializeAddress("deployed_addresses", "chain_registrar", addresses.chainRegistrar);
string memory deployedAddresses = vm.serializeString("deployed_addresses", "bridges", bridges);

vm.serializeAddress("root", "create2_factory_addr", addresses.create2Factory);
Expand Down
8 changes: 7 additions & 1 deletion l1-contracts/deploy-scripts/DeployL2Contracts.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

pragma solidity ^0.8.21;

import {Script} from "forge-std/Script.sol";
import {Script, console2 as console} from "forge-std/Script.sol";
import {stdToml} from "forge-std/StdToml.sol";

import {Utils} from "./Utils.sol";
import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol";
import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol";
import {L1SharedBridge} from "contracts/bridge/L1SharedBridge.sol";
import {ChainRegistrar} from "contracts/chain-registrar/ChainRegistrar.sol";

contract DeployL2Script is Script {
using stdToml for string;
Expand All @@ -22,6 +23,8 @@ contract DeployL2Script is Script {
address l1SharedBridgeProxy;
address governance;
address erc20BridgeProxy;
address chainRegistrar;
address proposalAuthor;
// The owner of the contract sets the validator/attester weights.
// Can be the developer multisig wallet on mainnet.
address consensusRegistryOwner;
Expand Down Expand Up @@ -90,6 +93,7 @@ contract DeployL2Script is Script {
deploySharedBridge();
deploySharedBridgeProxy(legacyBridge);
initializeChain();
finalizeRegistration();

saveOutput();
}
Expand Down Expand Up @@ -184,6 +188,8 @@ contract DeployL2Script is Script {
config.l1SharedBridgeProxy = toml.readAddress("$.l1_shared_bridge");
config.erc20BridgeProxy = toml.readAddress("$.erc20_bridge");
config.consensusRegistryOwner = toml.readAddress("$.consensus_registry_owner");
config.chainRegistrar = toml.readAddress("$.chain_registrar");
config.proposalAuthor = toml.readAddress("$.proposal_author");
config.chainId = toml.readUint("$.chain_id");
config.eraChainId = toml.readUint("$.era_chain_id");
}
Expand Down
Loading
Loading