-
Notifications
You must be signed in to change notification settings - Fork 335
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #390 from 0xPolygonHermez/feature/create-sovereign…
…-genesis-tool Feature/create sovereign genesis tool
- Loading branch information
Showing
7 changed files
with
370 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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` |
11 changes: 11 additions & 0 deletions
11
tools/createSovereignGenesis/create-genesis-sovereign-params.json.example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
187
tools/createSovereignGenesis/create-sovereign-genesis.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); |
Oops, something went wrong.