@hyperlane-xyz/core
Version:
Core solidity contracts for Hyperlane
232 lines (212 loc) • 7.37 kB
text/typescript
import fs from 'fs'
import path from 'path'
import dirtree from 'directory-tree'
import { predeploys } from '../src'
interface DeploymentInfo {
folder: string
name: string
chainid: number
rpc?: string
l1Explorer?: string
l2Explorer?: string
notice?: string
}
const PUBLIC_DEPLOYMENTS: DeploymentInfo[] = [
{
folder: 'mainnet',
name: 'Optimism (mainnet)',
chainid: 10,
rpc: 'https://mainnet.optimism.io',
l1Explorer: 'https://etherscan.io',
l2Explorer: 'https://optimistic.etherscan.io',
},
{
folder: 'goerli',
name: 'Optimism Goerli (public testnet)',
chainid: 420,
rpc: 'https://goerli.optimism.io',
l1Explorer: 'https://goerli.etherscan.io',
l2Explorer: 'https://goerli-optimism.etherscan.io/',
},
]
// List of contracts that are part of a deployment but aren't meant to be used by the general
// public. E.g., implementation addresses for proxy contracts or helpers used during the
// deployment process. Although these addresses are public and users can technically try to use
// them, there's no point in doing so. As a result, we hide these addresses to avoid confusion.
const HIDDEN_CONTRACTS = [
// Used for being able to verify the ChugSplashProxy contract.
'L1StandardBridge_for_verification_only',
// Implementation address for the Proxy__OVM_L1CrossDomainMessenger.
'OVM_L1CrossDomainMessenger',
// Utility for modifying many records in the AddressManager at the same time.
'AddressDictator',
// Utility for modifying a ChugSplashProxy during an upgrade.
'ChugSplashDictator',
]
interface ContractInfo {
name: string
address: string
}
/**
* Gets the full deployment folder path for a given deployment.
*
* @param name Deployment folder name.
* @returns Full path to the deployment folder.
*/
const getDeploymentFolderPath = (name: string): string => {
return path.resolve(__dirname, `../deployments/${name}`)
}
/**
* Helper function for adding a line to a string. Avoids having to add the ugly \n to each new line
* that you want to add a string.
*
* @param str String to add a line to.
* @param line Line to add to the string.
* @returns String with the added line and a newline at the end.
*/
const addline = (str: string, line: string): string => {
return str + line + '\n'
}
/**
* Generates a nicely formatted table presenting a list of contracts.
*
* @param contracts List of contracts to display.
* @param explorer URL for etherscan for the network that the contracts are deployed to.
* @returns Nicely formatted markdown-compatible table as a string.
*/
const buildContractsTable = (
contracts: ContractInfo[],
explorer?: string
): string => {
// Being very verbose within this function to make it clear what's going on.
// We use HTML instead of markdown so we can get a table that displays well on GitHub.
// GitHub READMEs are 1012px wide. Adding a 506px image to each table header is a hack that
// allows us to create a table where each column is 1/2 the full README width.
let table = ``
table = addline(table, '<table>')
table = addline(table, '<tr>')
table = addline(table, '<th>')
table = addline(table, '<img width="506px" height="0px" />')
table = addline(table, '<p><small>Contract</small></p>')
table = addline(table, '</th>')
table = addline(table, '<th>')
table = addline(table, '<img width="506px" height="0px" />')
table = addline(table, '<p><small>Address</small></p>')
table = addline(table, '</th>')
table = addline(table, '</tr>')
for (const contract of contracts) {
// Don't add records for contract addresses that aren't meant to be public-facing.
if (HIDDEN_CONTRACTS.includes(contract.name)) {
continue
}
table = addline(table, '<tr>')
table = addline(table, '<td>')
table = addline(table, contract.name)
table = addline(table, '</td>')
table = addline(table, '<td align="center">')
if (explorer) {
table = addline(
table,
`<a href="${explorer}/address/${contract.address}">`
)
table = addline(table, `<code>${contract.address}</code>`)
table = addline(table, '</a>')
} else {
table = addline(table, `<code>${contract.address}</code>`)
}
table = addline(table, '</td>')
table = addline(table, '</tr>')
}
table = addline(table, '</table>')
return table
}
/**
* Gets the list of L1 contracts for a given deployment.
*
* @param deployment Folder where the deployment is located.
* @returns List of L1 contracts for thegiven deployment.
*/
const getL1Contracts = (deployment: string): ContractInfo[] => {
const l1ContractsFolder = getDeploymentFolderPath(deployment)
return dirtree(l1ContractsFolder)
.children.filter((child) => {
return child.extension === '.json'
})
.map((child) => {
return {
name: child.name.replace('.json', ''),
// eslint-disable-next-line @typescript-eslint/no-var-requires
address: require(path.join(l1ContractsFolder, child.name)).address,
}
})
}
/* eslint-disable @typescript-eslint/no-unused-vars */
/**
* Gets the list of L2 contracts for a given deployment.
*
* @param deployment Folder where the deployment is located.
* @returns List of L2 system contracts for the given deployment.
*/
const getL2Contracts = (deployment: string): ContractInfo[] => {
// Deployment parameter is currently unused because all networks have the same predeploy
// addresses. However, we've had situations in the past where we've had to deploy one-off
// system contracts to a network. If we want to do that again in the future then we'll want some
// kind of custom logic based on the network in question. Hence, the deployment parameter.
return Object.entries(predeploys).map(([name, address]) => {
return {
name,
address,
}
})
}
/* eslint-enable @typescript-eslint/no-unused-vars */
const main = async () => {
for (const deployment of PUBLIC_DEPLOYMENTS) {
let md = ``
md = addline(md, `# ${deployment.name}`)
if (deployment.notice) {
md = addline(md, `## Notice`)
md = addline(md, deployment.notice)
}
md = addline(md, `## Network Info`)
md = addline(md, `- **Chain ID**: ${deployment.chainid}`)
if (deployment.rpc) {
md = addline(md, `- **Public RPC**: ${deployment.rpc}`)
}
if (deployment.l2Explorer) {
md = addline(md, `- **Block Explorer**: ${deployment.l2Explorer}`)
}
md = addline(md, `## Layer 1 Contracts`)
md = addline(
md,
buildContractsTable(
getL1Contracts(deployment.folder),
deployment.l1Explorer
)
)
md = addline(md, `## Layer 2 Contracts`)
md = addline(
md,
buildContractsTable(
getL2Contracts(deployment.folder),
deployment.l2Explorer
)
)
// Write the README file for the deployment
fs.writeFileSync(
path.join(getDeploymentFolderPath(deployment.folder), 'README.md'),
md
)
}
let primary = ``
primary = addline(primary, `# Optimism Deployments`)
for (const deployment of PUBLIC_DEPLOYMENTS) {
primary = addline(
primary,
`- [${deployment.name}](./${deployment.folder}#readme)`
)
}
// Write the primary README file
fs.writeFileSync(path.resolve(__dirname, '../deployments/README.md'), primary)
}
main()