Skip to content

Commit

Permalink
Merge pull request #390 from 0xPolygonHermez/feature/create-sovereign…
Browse files Browse the repository at this point in the history
…-genesis-tool

Feature/create sovereign genesis tool
  • Loading branch information
krlosMata authored Jan 28, 2025
2 parents f94b21a + 50fb2eb commit e8f10d5
Show file tree
Hide file tree
Showing 7 changed files with 370 additions and 10 deletions.
29 changes: 21 additions & 8 deletions deployment/v2/utils/updateVanillaGenesis.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
import {MemDB, ZkEVMDB, getPoseidon, smtUtils, processorUtils} from "@0xpolygonhermez/zkevm-commonjs";
import {expect} from "chai";
import {ethers} from "hardhat";

import {MemDB, ZkEVMDB, getPoseidon, smtUtils, processorUtils} from "@0xpolygonhermez/zkevm-commonjs";
const {getContractAddress} = require("@ethersproject/address");
const bridgeContractName = "BridgeL2SovereignChain";
import {expect} from "chai";

import {padTo32Bytes, padTo20Bytes} from "./deployment-utils";

// constants
// Those contracts names came from the genesis creation:
// - https://github.com/0xPolygonHermez/zkevm-contracts/blob/main/deployment/v2/1_createGenesis.ts#L294
// - https://github.com/0xPolygonHermez/zkevm-contracts/blob/main/deployment/v2/1_createGenesis.ts#L328
// Genesis files have been created previoudly and so they have old naming, as it shown in the links above
// Those genesis are already imported on different tooling and added as a metedata on-chain. Therefore, this util aims
// to support them too
const bridgeContractName = "BridgeL2SovereignChain";
const supportedGERManagers = ["PolygonZkEVMGlobalExitRootL2 implementation"];
const supportedBridgeContracts = ['PolygonZkEVMBridge implementation', 'PolygonZkEVMBridgeV2 implementation'];
const supportedBridgeContractsProxy = ['PolygonZkEVMBridgeV2 proxy', 'PolygonZkEVMBridge proxy'];

async function updateVanillaGenesis(genesis, chainID, initializeParams) {
// Load genesis on a zkEVMDB
const poseidon = await getPoseidon();
Expand Down Expand Up @@ -71,11 +84,11 @@ async function updateVanillaGenesis(genesis, chainID, initializeParams) {
const gerContractName = "GlobalExitRootManagerL2SovereignChain";
const gerFactory = await ethers.getContractFactory(gerContractName);
const oldBridge = genesis.genesis.find(function (obj) {
return obj.contractName == "PolygonZkEVMBridgeV2";
return supportedBridgeContracts.includes(obj.contractName);
});
// Get bridge proxy address
const bridgeProxy = genesis.genesis.find(function (obj) {
return obj.contractName == "PolygonZkEVMBridgeV2 proxy";
return supportedBridgeContractsProxy.includes(obj.contractName);
});
const deployGERData = await gerFactory.getDeployTransaction(bridgeProxy.address);
injectedTx.data = deployGERData.data;
Expand All @@ -90,13 +103,13 @@ async function updateVanillaGenesis(genesis, chainID, initializeParams) {
await zkEVMDB.consolidate(batch);

// replace old bridge and ger manager by sovereign contracts bytecode
oldBridge.contractName = bridgeContractName;
oldBridge.contractName = bridgeContractName + " implementation";
oldBridge.bytecode = `0x${await zkEVMDB.getBytecode(sovereignBridgeAddress)}`;

const oldGer = genesis.genesis.find(function (obj) {
return obj.contractName == "PolygonZkEVMGlobalExitRootL2";
return supportedGERManagers.includes(obj.contractName);
});
oldGer.contractName = gerContractName;
oldGer.contractName = gerContractName + " implementation";
oldGer.bytecode = `0x${await zkEVMDB.getBytecode(GERAddress)}`;

// Setup a second zkEVM to initialize both contracts
Expand Down
4 changes: 2 additions & 2 deletions tools/createNewRollup/genesis.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
}
},
{
"contractName": "PolygonZkEVMBridgeV2",
"contractName": "PolygonZkEVMBridgeV2 implementation",
"balance": "0",
"nonce": "1",
"address": "0xBC9585CB224A8F42EF9C9F0b99c531B450b11886",
Expand All @@ -40,7 +40,7 @@
}
},
{
"contractName": "PolygonZkEVMGlobalExitRootL2",
"contractName": "PolygonZkEVMGlobalExitRootL2 implementation",
"balance": "0",
"nonce": "1",
"address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9",
Expand Down
4 changes: 4 additions & 0 deletions tools/createSovereignGenesis/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
genesis-rollupID-*.json
output-rollupID-*.json
genesis-base.json
create-genesis-sovereign-params.json
53 changes: 53 additions & 0 deletions tools/createSovereignGenesis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Create sovereign genesis
Script to generate the genesis file for a rollup with `SovereignContracts`. This genesis si aim to be used for chains that are run with vanilla clients.
This script should be run after the rollup is created, so its `rollupID` and the bridge initialization parameters are known.
The script does the following:
- read base genesis file
- deploy sovereign cobtracts
- initialize them

## Setup
- install packages
```
npm i
```

- Set env variables
````
cp .env.example .env
````

Fill `.env` with your `INFURA_PROJECT_ID` and `ETHERSCAN_API_KEY`

- Copy configuration files:
```
cp ./tools/createSovereignGenesis/create-genesis-sovereign-params.json.example ./tools/createSovereignGenesis/create-genesis-sovereign-params.json
```

- Copy genesis base file:
```
cp ./tools/createSovereignGenesis/genesis-base.json.example ./tools/createSovereignGenesis/genesis-base.json
```

- Set your parameters
- `rollupManagerAddress`: `polygonRollupManager` smart contract address
- `rollupID`: Rollup identifier. Assigned to a rollup when it is created in the contracts
- `chainID`: ChainID of the rollup
- `gasTokenAddress`: Address of the native gas token of the rollup, zero if ether
- `bridgeManager`: bridge manager address
- `sovereignWETHAddress`: sovereign WETH address
- `sovereignWETHAddressIsNotMintable`: Flag to indicate if the wrapped ETH is not mintable
- `globalExitRootUpdater`: Address of globalExitRootUpdater for sovereign chains
- `globalExitRootRemover`: Address of globalExitRootRemover for sovereign chains

- Run tool:
```
npx hardhat run ./tools/createSovereignGenesis/create-sovereign-genesis.ts --network sepolia
```

### More Info
- All commands are done from root repository
- The output files are:
- `genesis-rollupID-${rollupID}__${timestamp}`: genesis file
- `output-rollupID-${rollupID}__${timestamp}`: input parameters, gastokenAddress information and network used
- outputs are saved in the tool folder: `./tools/createSovereignGenesis`
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"rollupManagerAddress": "0xe983fD1798689eee00c0Fb77e79B8f372DF41060",
"rollupID": 3,
"chainID": 1001,
"gasTokenAddress": "0x0000000000000000000000000000000000000000",
"bridgeManager": "0x8576158a89648aA88B6036f47B8b74Fc0C2b5c66",
"sovereignWETHAddress": "0x0000000000000000000000000000000000000000",
"sovereignWETHAddressIsNotMintable": false,
"globalExitRootUpdater": "0x8576158a89648aA88B6036f47B8b74Fc0C2b5c66",
"globalExitRootRemover": "0x8576158a89648aA88B6036f47B8b74Fc0C2b5c66"
}
187 changes: 187 additions & 0 deletions tools/createSovereignGenesis/create-sovereign-genesis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/* eslint-disable no-await-in-loop, no-use-before-define, no-lonely-if */
/* eslint-disable no-console, no-inner-declarations, no-undef, import/no-unresolved */
// external dependencies
import path = require("path");
import fs = require("fs");

import * as dotenv from "dotenv";
dotenv.config({path: path.resolve(__dirname, "../../.env")});
import {ethers, hardhatArguments} from "hardhat";

// internal dependencies
import updateVanillaGenesis from "../../deployment/v2/utils/updateVanillaGenesis";
import { PolygonRollupManager, PolygonZkEVMBridgeV2} from "../../typechain-types";
import "../../deployment/helpers/utils";

// script utils
const dateStr = new Date().toISOString();

// read files
const genesisBase = require("./genesis-base.json");
const createGenesisSovereignParams = require("./create-genesis-sovereign-params.json");

async function main() {
// check tool parameters
const mandatoryParameters = [
"rollupManagerAddress",
"rollupID",
"chainID",
"bridgeManager",
"gasTokenAddress",
"sovereignWETHAddress",
"sovereignWETHAddressIsNotMintable",
"globalExitRootUpdater",
"globalExitRootRemover"
];

for (const parameterName of mandatoryParameters) {
if (createGenesisSovereignParams[parameterName] === undefined || createGenesisSovereignParams[parameterName] === "") {
throw new Error(`Missing parameter: ${parameterName}`);
}
}

// Load provider
const currentProvider = ethers.provider;

// Load Rollup manager
const PolygonRollupManagerFactory = await ethers.getContractFactory("PolygonRollupManager");
const rollupManagerContract = PolygonRollupManagerFactory.attach(
createGenesisSovereignParams.rollupManagerAddress
) as PolygonRollupManager;

// Checks like in bridge contract
if (
ethers.isAddress(createGenesisSovereignParams.gasTokenAddress) &&
createGenesisSovereignParams.gasTokenAddress !== ethers.ZeroAddress &&
createGenesisSovereignParams.sovereignWETHAddress === ethers.ZeroAddress &&
createGenesisSovereignParams.sovereignWETHAddressIsNotMintable === true
) {
throw new Error(
"InvalidSovereignWETHAddressParams: if gasTokenAddress is not 0x0, and sovereignWETHAddress is 0x0, sovereignWETHAddressIsNotMintable must be false"
);
}

if (
createGenesisSovereignParams.gasTokenAddress === ethers.ZeroAddress &&
(createGenesisSovereignParams.sovereignWETHAddress !== ethers.ZeroAddress ||
createGenesisSovereignParams.sovereignWETHAddressIsNotMintable === true)
) {
throw new Error(
"InvalidSovereignWETHAddressParams: If gasTokenAddress is 0x0, sovereignWETHAddress must be 0x0 and sovereignWETHAddressIsNotMintable must be false"
);
}

// Create output
const outputJson = {} as any;

// get token information
let gasTokenAddress, gasTokenNetwork, gasTokenMetadata;

// Get bridge instance
const bridgeFactory = await ethers.getContractFactory("PolygonZkEVMBridgeV2");
const bridgeContractAddress = await rollupManagerContract.bridgeAddress();
const rollupBridgeContract = bridgeFactory.attach(bridgeContractAddress) as PolygonZkEVMBridgeV2;
if (
ethers.isAddress(createGenesisSovereignParams.gasTokenAddress) &&
createGenesisSovereignParams.gasTokenAddress !== ethers.ZeroAddress
) {
// Get token metadata
gasTokenMetadata = await rollupBridgeContract.getTokenMetadata(createGenesisSovereignParams.gasTokenAddress);
outputJson.gasTokenMetadata = gasTokenMetadata;
// If gas token metadata includes `0x124e4f545f56414c49445f454e434f44494e47 (NOT_VALID_ENCODING)` means there is no erc20 token deployed at the selected gas token network
if (gasTokenMetadata.includes("124e4f545f56414c49445f454e434f44494e47")) {
throw new Error(
`Invalid gas token address, no ERC20 token deployed at the selected gas token network ${createGenesisSovereignParams.gasTokenAddress}`
);
}
const wrappedData = await rollupBridgeContract.wrappedTokenToTokenInfo(createGenesisSovereignParams.gasTokenAddress);
if (wrappedData.originNetwork != 0n) {
// Wrapped token
gasTokenAddress = wrappedData.originTokenAddress;
gasTokenNetwork = wrappedData.originNetwork;
} else {
// Mainnet token
gasTokenAddress = createGenesisSovereignParams.gasTokenAddress;
gasTokenNetwork = 0n;
}
} else {
gasTokenAddress = ethers.ZeroAddress;
gasTokenNetwork = 0;
gasTokenMetadata = "0x";
}


// start final genesis creation
let finalGenesis = genesisBase;

// initialize sovereign bridge parameters
const initializeParams = {
rollupID: createGenesisSovereignParams.rollupID,
gasTokenAddress,
gasTokenNetwork,
polygonRollupManager: ethers.ZeroAddress,
gasTokenMetadata,
bridgeManager: createGenesisSovereignParams.bridgeManager,
sovereignWETHAddress: createGenesisSovereignParams.sovereignWETHAddress,
sovereignWETHAddressIsNotMintable: createGenesisSovereignParams.sovereignWETHAddressIsNotMintable,
globalExitRootUpdater: createGenesisSovereignParams.globalExitRootUpdater,
globalExitRootRemover: createGenesisSovereignParams.globalExitRootRemover,
};

finalGenesis = await updateVanillaGenesis(finalGenesis, createGenesisSovereignParams.chainID, initializeParams);

// Add weth address to deployment output if gas token address is provided and sovereignWETHAddress is not provided
let outWETHAddress;
if (
gasTokenAddress !== ethers.ZeroAddress &&
ethers.isAddress(gasTokenAddress) &&
(createGenesisSovereignParams.sovereignWETHAddress === ethers.ZeroAddress ||
!ethers.isAddress(createGenesisSovereignParams.sovereignWETHAddress))
) {
console.log("Rollup with custom gas token, adding WETH address to deployment output...");
const wethObject = genesisBase.genesis.find(function (obj: {contractName: string}) {
return obj.contractName == "WETH";
});
outWETHAddress = wethObject.address;
}

// Populate final output
outputJson.network = hardhatArguments.network;
outputJson.rollupID = createGenesisSovereignParams.rollupID;
outputJson.gasTokenAddress = gasTokenAddress;
outputJson.gasTokenNetwork = gasTokenNetwork;
outputJson.gasTokenMetadata = gasTokenMetadata;
outputJson.rollupManagerAddress = createGenesisSovereignParams.rollupManagerAddress;
outputJson.chainID = createGenesisSovereignParams.chainID;
outputJson.bridgeManager = createGenesisSovereignParams.bridgeManager;
outputJson.sovereignWETHAddress = createGenesisSovereignParams.sovereignWETHAddress;
outputJson.sovereignWETHAddressIsNotMintable = createGenesisSovereignParams.sovereignWETHAddressIsNotMintable;
outputJson.globalExitRootUpdater = createGenesisSovereignParams.globalExitRootUpdater;
outputJson.globalExitRootRemover = createGenesisSovereignParams.globalExitRootRemover;

if (typeof outWETHAddress !== 'undefined') {
outputJson.WETHAddress = outWETHAddress;
}

// path output genesis
const pathOutputGenesisJson = createGenesisSovereignParams.outputGenesisPath
? path.join(__dirname, createGenesisSovereignParams.outputGenesisPath)
: path.join(__dirname, `./genesis-rollupID-${createGenesisSovereignParams.rollupID}__${dateStr}.json`);

const pathOutputJson = createGenesisSovereignParams.outputPath
? path.join(__dirname, createGenesisSovereignParams.outputPath)
: path.join(__dirname, `./output-rollupID-${createGenesisSovereignParams.rollupID}__${dateStr}.json`);

// write files
fs.writeFileSync(pathOutputGenesisJson, JSON.stringify(finalGenesis, null, 1));
fs.writeFileSync(pathOutputJson, JSON.stringify(outputJson, null, 1));

console.log("Output saved at:");
console.log(` output genesis: ${pathOutputGenesisJson}`);
console.log(` output info : ${pathOutputJson}`);
}

main().catch((e) => {
console.error(e);
process.exit(1);
});
Loading

0 comments on commit e8f10d5

Please sign in to comment.