Migrate SDK consts to Registry and use new registry utilities (#3615)
### Description - Remove most consts from SDK - Refactor tests that relied on chainMetadata - Remove JSON imports/exports in the SDK - Update the HelloWorld package to read metadata from the registry lib - Update the Infra package to read/write to a LocalRegistry - Update the CLI to read and write from registries ### Drive-by changes - Fix typo in `EvmHypCollateralVault` token standard - Use consistent, newest yaml lib version ### Related issues Fixes https://github.com/hyperlane-xyz/issues/issues/917 Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/2810 ### Backward compatibility No: - Several SDK exports like `chainMetadata` have been removed - Common args for the CLI like `--chain` have been replaced with `--registry` and `--overrides`pull/3685/head
parent
292879126c
commit
3528b281e3
@ -1,5 +1,5 @@ |
||||
--- |
||||
"@hyperlane-xyz/sdk": patch |
||||
'@hyperlane-xyz/sdk': patch |
||||
--- |
||||
|
||||
Allow gasLimit overrides in the SDK/CLI for deploy txs |
||||
|
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/sdk': minor |
||||
--- |
||||
|
||||
Remove consts such as chainMetadata from SDK |
@ -0,0 +1,5 @@ |
||||
--- |
||||
'@hyperlane-xyz/cli': minor |
||||
--- |
||||
|
||||
Restructure CLI params around registries |
@ -1,100 +0,0 @@ |
||||
name: cron |
||||
|
||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows |
||||
on: |
||||
schedule: |
||||
- cron: '45 14 * * *' |
||||
workflow_dispatch: |
||||
|
||||
env: |
||||
LOG_LEVEL: DEBUG |
||||
LOG_FORMAT: PRETTY |
||||
|
||||
jobs: |
||||
# copied from test.yml |
||||
yarn-install: |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
- uses: actions/setup-node@v3 |
||||
with: |
||||
node-version: 18 |
||||
|
||||
- uses: actions/checkout@v3 |
||||
with: |
||||
ref: ${{ github.sha }} |
||||
submodules: recursive |
||||
|
||||
- name: yarn-cache |
||||
uses: actions/cache@v3 |
||||
with: |
||||
path: | |
||||
**/node_modules |
||||
.yarn |
||||
key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} |
||||
|
||||
- name: yarn-install |
||||
run: yarn install |
||||
|
||||
# copied from test.yml |
||||
yarn-build: |
||||
runs-on: ubuntu-latest |
||||
needs: [yarn-install] |
||||
steps: |
||||
- uses: actions/checkout@v3 |
||||
with: |
||||
ref: ${{ github.sha }} |
||||
submodules: recursive |
||||
fetch-depth: 0 |
||||
|
||||
- name: yarn-cache |
||||
uses: actions/cache@v3 |
||||
with: |
||||
path: | |
||||
**/node_modules |
||||
.yarn |
||||
key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} |
||||
|
||||
- name: build-cache |
||||
uses: actions/cache@v3 |
||||
with: |
||||
path: | |
||||
./* |
||||
!./rust |
||||
key: ${{ github.sha }} |
||||
|
||||
- name: build |
||||
run: yarn build |
||||
|
||||
metadata-check: |
||||
runs-on: ubuntu-latest |
||||
needs: [yarn-build] |
||||
steps: |
||||
- uses: actions/checkout@v3 |
||||
with: |
||||
ref: ${{ github.sha }} |
||||
submodules: recursive |
||||
fetch-depth: 0 |
||||
|
||||
- name: yarn-cache |
||||
uses: actions/cache@v3 |
||||
with: |
||||
path: | |
||||
**/node_modules |
||||
.yarn |
||||
key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} |
||||
|
||||
- name: build-cache |
||||
uses: actions/cache@v3 |
||||
with: |
||||
path: | |
||||
./* |
||||
!./rust |
||||
key: ${{ github.sha }} |
||||
|
||||
- name: Metadata Health Check |
||||
run: yarn workspace @hyperlane-xyz/sdk run test:metadata |
||||
|
||||
- name: Post to discord webhook if metadata check fails |
||||
if: failure() |
||||
run: | |
||||
curl -X POST -H 'Content-type: application/json' --data '{"content":"SDK metadata check failed, see ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' ${{ secrets.DISCORD_WEBHOOK_URL }} |
@ -1,5 +1,13 @@ |
||||
.env* |
||||
/dist |
||||
/cache |
||||
|
||||
# Deployment artifacts and local registry configs |
||||
/configs |
||||
/artifacts |
||||
/chains |
||||
/deployments |
||||
|
||||
# Test artifacts |
||||
/test-configs/**/addresses.yaml |
||||
/test-configs/*/deployments |
@ -1,22 +0,0 @@ |
||||
# Configs for describing chain metadata for use in Hyperlane deployments or apps |
||||
# Consists of a map of chain names to metadata |
||||
# Schema here: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/sdk/src/metadata/chainMetadataTypes.ts |
||||
--- |
||||
anvil1: |
||||
chainId: 31337 |
||||
domainId: 31337 |
||||
name: anvil1 |
||||
protocol: ethereum |
||||
rpcUrls: |
||||
- http: http://127.0.0.1:8545 |
||||
nativeToken: |
||||
name: Ether |
||||
symbol: ETH |
||||
decimals: 18 |
||||
anvil2: |
||||
chainId: 31338 |
||||
domainId: 31338 |
||||
name: anvil2 |
||||
protocol: ethereum |
||||
rpcUrls: |
||||
- http: http://127.0.0.1:8555 |
@ -1,50 +1,42 @@ |
||||
# Configs for describing chain metadata for use in Hyperlane deployments or apps |
||||
# Consists of a map of chain names to metadata |
||||
# Schema here: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/sdk/src/metadata/chainMetadataTypes.ts |
||||
--- |
||||
# You can define a full config for a new chain |
||||
mychainname: |
||||
# Required fields: |
||||
chainId: 1234567890 # Number: Use EIP-155 for EVM chains |
||||
domainId: 1234567890 # Number: Recommend matching chainId when possible |
||||
name: mychainname # String: Unique identifier for the chain, must match key above |
||||
protocol: ethereum # ProtocolType: Ethereum, Sealevel, etc. |
||||
rpcUrls: # Array: List of RPC configs |
||||
# Only http field is required |
||||
- http: https://mychain.com/rpc # String: HTTP URL of the RPC endpoint (preferably HTTPS) |
||||
# Others here are optional |
||||
pagination: |
||||
maxBlockRange: 1000 # Number |
||||
maxBlockAge: 1000 # Number |
||||
minBlockNumber: 1000 # Number |
||||
retry: |
||||
maxRequests: 5 # Number |
||||
baseRetryMs: 1000 # Number |
||||
# Optional fields, not required for Hyperlane deployments but useful for apps: |
||||
isTestnet: false # Boolean: Whether the chain is considered a testnet or a mainnet |
||||
blockExplorers: # Array: List of BlockExplorer configs |
||||
# Required fields: |
||||
chainId: 1234567890 # Number: Use EIP-155 for EVM chains |
||||
domainId: 1234567890 # Number: Recommend matching chainId when possible |
||||
name: mychainname # String: Unique identifier for the chain, must match key above |
||||
protocol: ethereum # ProtocolType: Ethereum, Sealevel, etc. |
||||
rpcUrls: # Array: List of RPC configs |
||||
# Only http field is required |
||||
- http: https://mychain.com/rpc # String: HTTP URL of the RPC endpoint (preferably HTTPS) |
||||
# Others here are optional |
||||
pagination: |
||||
maxBlockRange: 1000 # Number |
||||
maxBlockAge: 1000 # Number |
||||
minBlockNumber: 1000 # Number |
||||
retry: |
||||
maxRequests: 5 # Number |
||||
baseRetryMs: 1000 # Number |
||||
# Optional fields, not required for Hyperlane deployments but useful for apps: |
||||
isTestnet: false # Boolean: Whether the chain is considered a testnet or a mainnet |
||||
blockExplorers: # Array: List of BlockExplorer configs |
||||
# Required fields: |
||||
- name: My Chain Explorer # String: Human-readable name for the explorer |
||||
url: https://mychain.com/explorer # String: Base URL for the explorer |
||||
apiUrl: https://mychain.com/api # String: Base URL for the explorer API |
||||
# Optional fields: |
||||
apiKey: myapikey # String: API key for the explorer (optional) |
||||
family: etherscan # ExplorerFamily: See ExplorerFamily for valid values |
||||
nativeToken: |
||||
name: Eth # String |
||||
symbol: ETH # String |
||||
decimals: 18 # Number |
||||
displayName: My Chain Name # String: Human-readable name of the chain |
||||
displayNameShort: My Chain # String: A shorter human-readable name |
||||
logoURI: https://mychain.com/logo.png # String: URI to a logo image for the chain |
||||
blocks: |
||||
confirmations: 12 # Number: Blocks to wait before considering a transaction confirmed |
||||
reorgPeriod: 100 # Number: Blocks before a transaction has a near-zero chance of reverting |
||||
estimateBlockTime: 15 # Number: Rough estimate of time per block in seconds |
||||
# transactionOverrides: # Object: Properties to include when forming transaction requests |
||||
# Any tx fields are allowed |
||||
|
||||
# Alternatively, you can extend a core chain config with only fields to be overridden |
||||
sepolia: |
||||
rpcUrls: |
||||
- http: https://mycustomrpc.com |
||||
- name: My Chain Explorer # String: Human-readable name for the explorer |
||||
url: https://mychain.com/explorer # String: Base URL for the explorer |
||||
apiUrl: https://mychain.com/api # String: Base URL for the explorer API |
||||
# Optional fields: |
||||
apiKey: myapikey # String: API key for the explorer (optional) |
||||
family: etherscan # ExplorerFamily: See ExplorerFamily for valid values |
||||
nativeToken: |
||||
name: Eth # String |
||||
symbol: ETH # String |
||||
decimals: 18 # Number |
||||
displayName: My Chain Name # String: Human-readable name of the chain |
||||
displayNameShort: My Chain # String: A shorter human-readable name |
||||
logoURI: https://mychain.com/logo.png # String: URI to a logo image for the chain |
||||
blocks: |
||||
confirmations: 12 # Number: Blocks to wait before considering a transaction confirmed |
||||
reorgPeriod: 100 # Number: Blocks before a transaction has a near-zero chance of reverting |
||||
estimateBlockTime: 15 # Number: Rough estimate of time per block in seconds |
||||
# transactionOverrides: # Object: Properties to include when forming transaction requests |
||||
# Any tx fields are allowed |
||||
|
@ -1,17 +0,0 @@ |
||||
anvil: |
||||
chainId: 31337 |
||||
domainId: 31337 |
||||
name: anvil |
||||
protocol: ethereum |
||||
rpcUrls: |
||||
- http: http://127.0.0.1:8545 |
||||
nativeToken: |
||||
name: Ether |
||||
symbol: ETH |
||||
decimals: 18 |
||||
alfajores: |
||||
rpcUrls: |
||||
- http: https://alfajores-forno.celo-testnet.org |
||||
blocks: |
||||
confirmations: 1 |
||||
estimateBlockTime: 1 |
@ -1,21 +0,0 @@ |
||||
# Configs for describing chain metadata for use in Hyperlane deployments or apps |
||||
# Consists of a map of chain names to metadata |
||||
# Schema here: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/sdk/src/metadata/chainMetadataTypes.ts |
||||
--- |
||||
anvil: |
||||
chainId: 31337 |
||||
domainId: 31337 |
||||
name: anvil |
||||
protocol: ethereum |
||||
rpcUrls: |
||||
- http: http://127.0.0.1:8545 |
||||
nativeToken: |
||||
name: Ether |
||||
symbol: ETH |
||||
decimals: 18 |
||||
ethereum: |
||||
rpcUrls: |
||||
- http: http://127.0.0.1:8555 |
||||
blocks: |
||||
confirmations: 1 |
||||
estimateBlockTime: 1 |
@ -0,0 +1,11 @@ |
||||
// Commands that send tx and require a key to sign.
|
||||
// It's useful to have this listed here so the context
|
||||
// middleware can request keys up front when required.
|
||||
export const SIGN_COMMANDS = ['deploy', 'send']; |
||||
|
||||
export function isSignCommand(argv: any): boolean { |
||||
return ( |
||||
SIGN_COMMANDS.includes(argv._[0]) || |
||||
(argv._.length > 1 && SIGN_COMMANDS.includes(argv._[1])) |
||||
); |
||||
} |
@ -1,86 +0,0 @@ |
||||
import { confirm } from '@inquirer/prompts'; |
||||
import { ZodTypeAny, z } from 'zod'; |
||||
|
||||
import { ChainName, HyperlaneContractsMap } from '@hyperlane-xyz/sdk'; |
||||
|
||||
import { log, logBlue } from '../logger.js'; |
||||
import { readYamlOrJson, runFileSelectionStep } from '../utils/files.js'; |
||||
|
||||
const RecursiveObjectSchema: ZodTypeAny = z.lazy(() => |
||||
z.object({}).catchall(z.union([z.string(), RecursiveObjectSchema])), |
||||
); |
||||
|
||||
const DeploymentArtifactsSchema = z.object({}).catchall(RecursiveObjectSchema); |
||||
|
||||
export function readDeploymentArtifacts(filePath: string) { |
||||
const artifacts = readYamlOrJson<HyperlaneContractsMap<any>>(filePath); |
||||
|
||||
if (!artifacts) throw new Error(`No artifacts found at ${filePath}`); |
||||
const result = DeploymentArtifactsSchema.safeParse(artifacts); |
||||
if (!result.success) { |
||||
const firstIssue = result.error.issues[0]; |
||||
logBlue( |
||||
`Read deployment artifacts from ${JSON.stringify( |
||||
result.error.issues, |
||||
null, |
||||
4, |
||||
)}`,
|
||||
); |
||||
throw new Error( |
||||
`Invalid artifacts: ${firstIssue.path} => ${firstIssue.message}`, |
||||
); |
||||
} |
||||
return artifacts; |
||||
} |
||||
|
||||
/** |
||||
* Prompts the user to specify deployment artifacts, or to generate new ones if none are present or selected. |
||||
* @returns the selected artifacts, or undefined if they are to be generated from scratch |
||||
*/ |
||||
export async function runDeploymentArtifactStep({ |
||||
artifactsPath, |
||||
message, |
||||
selectedChains, |
||||
defaultArtifactsPath = './artifacts', |
||||
defaultArtifactsNamePattern = 'core-deployment', |
||||
skipConfirmation = false, |
||||
dryRun = false, |
||||
}: { |
||||
artifactsPath?: string; |
||||
message?: string; |
||||
selectedChains?: ChainName[]; |
||||
defaultArtifactsPath?: string; |
||||
defaultArtifactsNamePattern?: string; |
||||
skipConfirmation?: boolean; |
||||
dryRun?: boolean; |
||||
}): Promise<HyperlaneContractsMap<any> | undefined> { |
||||
if (!artifactsPath) { |
||||
if (skipConfirmation) return undefined; |
||||
if (dryRun) defaultArtifactsNamePattern = 'dry-run_core-deployment'; |
||||
|
||||
const useArtifacts = await confirm({ |
||||
message: message || 'Do you want use some existing contract addresses?', |
||||
}); |
||||
if (!useArtifacts) return undefined; |
||||
|
||||
artifactsPath = await runFileSelectionStep( |
||||
defaultArtifactsPath, |
||||
'contract deployment artifacts', |
||||
defaultArtifactsNamePattern, |
||||
); |
||||
} |
||||
const artifacts = readDeploymentArtifacts(artifactsPath); |
||||
|
||||
if (selectedChains) { |
||||
const artifactChains = Object.keys(artifacts).filter((c) => |
||||
selectedChains.includes(c), |
||||
); |
||||
if (artifactChains.length === 0) { |
||||
log('No artifacts found for selected chains'); |
||||
} else { |
||||
log(`Found existing artifacts for chains: ${artifactChains.join(', ')}`); |
||||
} |
||||
} |
||||
|
||||
return artifacts; |
||||
} |
@ -1,23 +0,0 @@ |
||||
import { expect } from 'chai'; |
||||
import { ethers } from 'ethers'; |
||||
|
||||
import { getContext } from './context.js'; |
||||
|
||||
describe('context', () => { |
||||
it('Gets minimal read-only context correctly', async () => { |
||||
const context = await getContext({ chainConfigPath: './fakePath' }); |
||||
expect(!!context.multiProvider).to.be.true; |
||||
expect(context.customChains).to.eql({}); |
||||
}); |
||||
|
||||
it('Handles conditional type correctly', async () => { |
||||
const randomWallet = ethers.Wallet.createRandom(); |
||||
const context = await getContext({ |
||||
chainConfigPath: './fakePath', |
||||
keyConfig: { key: randomWallet.privateKey }, |
||||
}); |
||||
expect(!!context.multiProvider).to.be.true; |
||||
expect(context.customChains).to.eql({}); |
||||
expect(await context.signer.getAddress()).to.eql(randomWallet.address); |
||||
}); |
||||
}); |
@ -1,213 +0,0 @@ |
||||
import { input } from '@inquirer/prompts'; |
||||
import { ethers } from 'ethers'; |
||||
|
||||
import { |
||||
ChainMap, |
||||
ChainMetadata, |
||||
ChainName, |
||||
HyperlaneContractsMap, |
||||
MultiProvider, |
||||
WarpCoreConfig, |
||||
chainMetadata, |
||||
hyperlaneEnvironments, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
import { objFilter, objMap, objMerge } from '@hyperlane-xyz/utils'; |
||||
|
||||
import { runDeploymentArtifactStep } from './config/artifacts.js'; |
||||
import { readChainConfigsIfExists } from './config/chain.js'; |
||||
import { forkNetworkToMultiProvider } from './deploy/dry-run.js'; |
||||
import { runSingleChainSelectionStep } from './utils/chains.js'; |
||||
import { readYamlOrJson } from './utils/files.js'; |
||||
import { getImpersonatedSigner, getSigner } from './utils/keys.js'; |
||||
|
||||
export const sdkContractAddressesMap: HyperlaneContractsMap<any> = { |
||||
...hyperlaneEnvironments.testnet, |
||||
...hyperlaneEnvironments.mainnet, |
||||
}; |
||||
|
||||
export function getMergedContractAddresses( |
||||
artifacts?: HyperlaneContractsMap<any>, |
||||
chains?: ChainName[], |
||||
) { |
||||
// if chains include non sdkContractAddressesMap chains, don't recover interchainGasPaymaster
|
||||
let sdkContractsAddressesToRecover = sdkContractAddressesMap; |
||||
if ( |
||||
chains?.some( |
||||
(chain) => !Object.keys(sdkContractAddressesMap).includes(chain), |
||||
) |
||||
) { |
||||
sdkContractsAddressesToRecover = objMap(sdkContractAddressesMap, (_, v) => |
||||
objFilter( |
||||
v as ChainMap<any>, |
||||
(key, v): v is any => key !== 'interchainGasPaymaster', |
||||
), |
||||
); |
||||
} |
||||
return objMerge( |
||||
sdkContractsAddressesToRecover, |
||||
artifacts || {}, |
||||
) as HyperlaneContractsMap<any>; |
||||
} |
||||
|
||||
export type KeyConfig = { |
||||
key?: string; |
||||
promptMessage?: string; |
||||
}; |
||||
|
||||
export interface ContextSettings { |
||||
chainConfigPath?: string; |
||||
chains?: ChainName[]; |
||||
coreConfig?: { |
||||
coreArtifactsPath?: string; |
||||
promptMessage?: string; |
||||
}; |
||||
keyConfig?: KeyConfig; |
||||
skipConfirmation?: boolean; |
||||
warpConfig?: { |
||||
warpConfigPath?: string; |
||||
promptMessage?: string; |
||||
}; |
||||
} |
||||
|
||||
interface CommandContextBase { |
||||
chains: ChainName[]; |
||||
customChains: ChainMap<ChainMetadata>; |
||||
multiProvider: MultiProvider; |
||||
} |
||||
|
||||
// This makes return type dynamic based on the input settings
|
||||
type CommandContext<P extends ContextSettings> = CommandContextBase & |
||||
(P extends { keyConfig: object } |
||||
? { signer: ethers.Signer } |
||||
: { signer: undefined }) & |
||||
(P extends { coreConfig: object } |
||||
? { coreArtifacts: HyperlaneContractsMap<any> } |
||||
: { coreArtifacts: undefined }) & |
||||
(P extends { warpConfig: object } |
||||
? { warpCoreConfig: WarpCoreConfig } |
||||
: { warpCoreConfig: undefined }); |
||||
|
||||
/** |
||||
* Retrieves context for the user-selected command |
||||
* @returns context for the current command |
||||
*/ |
||||
export async function getContext<P extends ContextSettings>({ |
||||
chainConfigPath, |
||||
coreConfig, |
||||
keyConfig, |
||||
skipConfirmation, |
||||
warpConfig, |
||||
}: P): Promise<CommandContext<P>> { |
||||
const customChains = readChainConfigsIfExists(chainConfigPath); |
||||
|
||||
const signer = await getSigner({ |
||||
keyConfig, |
||||
skipConfirmation, |
||||
}); |
||||
|
||||
let coreArtifacts = undefined; |
||||
if (coreConfig) { |
||||
coreArtifacts = |
||||
(await runDeploymentArtifactStep({ |
||||
artifactsPath: coreConfig.coreArtifactsPath, |
||||
message: |
||||
coreConfig.promptMessage || |
||||
'Do you want to use some core deployment address artifacts? This is required for PI chains (non-core chains).', |
||||
skipConfirmation, |
||||
})) || {}; |
||||
} |
||||
|
||||
let warpCoreConfig = undefined; |
||||
if (warpConfig) { |
||||
let warpConfigPath = warpConfig.warpConfigPath; |
||||
if (!warpConfigPath) { |
||||
// prompt for path to token config
|
||||
warpConfigPath = await input({ |
||||
message: |
||||
warpConfig.promptMessage || |
||||
'Please provide a path to the Warp config', |
||||
}); |
||||
} |
||||
|
||||
warpCoreConfig = readYamlOrJson<WarpCoreConfig>(warpConfigPath); |
||||
} |
||||
|
||||
const multiProvider = getMultiProvider(customChains, signer); |
||||
|
||||
return { |
||||
customChains, |
||||
signer, |
||||
multiProvider, |
||||
coreArtifacts, |
||||
warpCoreConfig, |
||||
} as CommandContext<P>; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves dry-run context for the user-selected command |
||||
* @returns dry-run context for the current command |
||||
*/ |
||||
export async function getDryRunContext<P extends ContextSettings>({ |
||||
chainConfigPath, |
||||
chains, |
||||
coreConfig, |
||||
keyConfig, |
||||
skipConfirmation, |
||||
}: P): Promise<CommandContext<P>> { |
||||
const customChains = readChainConfigsIfExists(chainConfigPath); |
||||
|
||||
let coreArtifacts = undefined; |
||||
if (coreConfig) { |
||||
coreArtifacts = |
||||
(await runDeploymentArtifactStep({ |
||||
artifactsPath: coreConfig.coreArtifactsPath, |
||||
message: |
||||
coreConfig.promptMessage || |
||||
'Do you want to use some core deployment address artifacts? This is required for PI chains (non-core chains).', |
||||
skipConfirmation, |
||||
})) || {}; |
||||
} |
||||
|
||||
const multiProvider = getMultiProvider(customChains); |
||||
|
||||
if (!chains?.length) { |
||||
if (skipConfirmation) throw new Error('No chains provided'); |
||||
chains = [ |
||||
await runSingleChainSelectionStep( |
||||
customChains, |
||||
'Select chain to dry-run against:', |
||||
), |
||||
]; |
||||
} |
||||
|
||||
await forkNetworkToMultiProvider(multiProvider, chains[0]); |
||||
|
||||
const impersonatedSigner = await getImpersonatedSigner({ |
||||
keyConfig, |
||||
skipConfirmation, |
||||
}); |
||||
|
||||
if (impersonatedSigner) multiProvider.setSharedSigner(impersonatedSigner); |
||||
|
||||
return { |
||||
chains, |
||||
signer: impersonatedSigner, |
||||
multiProvider, |
||||
coreArtifacts, |
||||
} as CommandContext<P>; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves a new MultiProvider based on all known chain metadata & custom user chains |
||||
* @param customChains Custom chains specified by the user |
||||
* @returns a new MultiProvider |
||||
*/ |
||||
export function getMultiProvider( |
||||
customChains: ChainMap<ChainMetadata>, |
||||
signer?: ethers.Signer, |
||||
) { |
||||
const chainConfigs = { ...chainMetadata, ...customChains }; |
||||
const multiProvider = new MultiProvider(chainConfigs); |
||||
if (signer) multiProvider.setSharedSigner(signer); |
||||
return multiProvider; |
||||
} |
@ -0,0 +1,134 @@ |
||||
import { ethers } from 'ethers'; |
||||
|
||||
import { IRegistry } from '@hyperlane-xyz/registry'; |
||||
import { ChainName, MultiProvider } from '@hyperlane-xyz/sdk'; |
||||
|
||||
import { isSignCommand } from '../commands/signCommands.js'; |
||||
import { forkNetworkToMultiProvider } from '../deploy/dry-run.js'; |
||||
import { MergedRegistry } from '../registry/MergedRegistry.js'; |
||||
import { runSingleChainSelectionStep } from '../utils/chains.js'; |
||||
import { getImpersonatedSigner, getSigner } from '../utils/keys.js'; |
||||
|
||||
import { |
||||
CommandContext, |
||||
ContextSettings, |
||||
WriteCommandContext, |
||||
} from './types.js'; |
||||
|
||||
export async function contextMiddleware(argv: Record<string, any>) { |
||||
const isDryRun = !!argv.dryRun; |
||||
const requiresKey = isSignCommand(argv); |
||||
const settings: ContextSettings = { |
||||
registryUri: argv.registry, |
||||
registryOverrideUri: argv.overrides, |
||||
key: argv.key, |
||||
requiresKey, |
||||
skipConfirmation: argv.yes, |
||||
}; |
||||
const context = isDryRun |
||||
? await getDryRunContext(settings, argv.dryRun) |
||||
: await getContext(settings); |
||||
argv.context = context; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves context for the user-selected command |
||||
* @returns context for the current command |
||||
*/ |
||||
export async function getContext({ |
||||
registryUri, |
||||
registryOverrideUri, |
||||
key, |
||||
requiresKey, |
||||
skipConfirmation, |
||||
}: ContextSettings): Promise<CommandContext> { |
||||
const registry = getRegistry(registryUri, registryOverrideUri); |
||||
|
||||
let signer: ethers.Wallet | undefined = undefined; |
||||
if (requiresKey) { |
||||
({ key, signer } = await getSigner({ key, skipConfirmation })); |
||||
} |
||||
const multiProvider = await getMultiProvider(registry, signer); |
||||
|
||||
return { |
||||
registry, |
||||
chainMetadata: multiProvider.metadata, |
||||
multiProvider, |
||||
key, |
||||
signer, |
||||
skipConfirmation: !!skipConfirmation, |
||||
} as CommandContext; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves dry-run context for the user-selected command |
||||
* @returns dry-run context for the current command |
||||
*/ |
||||
export async function getDryRunContext( |
||||
{ registryUri, registryOverrideUri, key, skipConfirmation }: ContextSettings, |
||||
chain?: ChainName, |
||||
): Promise<CommandContext> { |
||||
const registry = getRegistry(registryUri, registryOverrideUri, true); |
||||
const chainMetadata = await registry.getMetadata(); |
||||
|
||||
if (!chain) { |
||||
if (skipConfirmation) throw new Error('No chains provided'); |
||||
chain = await runSingleChainSelectionStep( |
||||
chainMetadata, |
||||
'Select chain to dry-run against:', |
||||
); |
||||
} |
||||
|
||||
const multiProvider = await getMultiProvider(registry); |
||||
await forkNetworkToMultiProvider(multiProvider, chain); |
||||
const { key: impersonatedKey, signer: impersonatedSigner } = |
||||
await getImpersonatedSigner({ |
||||
key, |
||||
skipConfirmation, |
||||
}); |
||||
multiProvider.setSharedSigner(impersonatedSigner); |
||||
|
||||
return { |
||||
registry, |
||||
chainMetadata: multiProvider.metadata, |
||||
key: impersonatedKey, |
||||
signer: impersonatedSigner, |
||||
multiProvider: multiProvider, |
||||
skipConfirmation: !!skipConfirmation, |
||||
isDryRun: true, |
||||
dryRunChain: chain, |
||||
} as WriteCommandContext; |
||||
} |
||||
|
||||
/** |
||||
* Creates a new MergedRegistry using the provided URIs |
||||
* The intention of the MergedRegistry is to join the common data |
||||
* from a primary URI (such as the Hyperlane default Github repo) |
||||
* and an override one (such as a local directory) |
||||
* @returns a new MergedRegistry |
||||
*/ |
||||
function getRegistry( |
||||
primaryRegistryUri: string, |
||||
overrideRegistryUri: string, |
||||
isDryRun?: boolean, |
||||
): IRegistry { |
||||
const registryUris = [primaryRegistryUri, overrideRegistryUri] |
||||
.map((r) => r.trim()) |
||||
.filter((r) => !!r); |
||||
return new MergedRegistry({ |
||||
registryUris, |
||||
isDryRun, |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* Retrieves a new MultiProvider based on all known chain metadata & custom user chains |
||||
* @param customChains Custom chains specified by the user |
||||
* @returns a new MultiProvider |
||||
*/ |
||||
async function getMultiProvider(registry: IRegistry, signer?: ethers.Signer) { |
||||
const chainMetadata = await registry.getMetadata(); |
||||
const multiProvider = new MultiProvider(chainMetadata); |
||||
if (signer) multiProvider.setSharedSigner(signer); |
||||
return multiProvider; |
||||
} |
@ -0,0 +1,43 @@ |
||||
import type { ethers } from 'ethers'; |
||||
import type { CommandModule } from 'yargs'; |
||||
|
||||
import type { IRegistry } from '@hyperlane-xyz/registry'; |
||||
import type { |
||||
ChainMap, |
||||
ChainMetadata, |
||||
MultiProvider, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
|
||||
export interface ContextSettings { |
||||
registryUri: string; |
||||
registryOverrideUri: string; |
||||
key?: string; |
||||
requiresKey?: boolean; |
||||
skipConfirmation?: boolean; |
||||
} |
||||
|
||||
export interface CommandContext { |
||||
registry: IRegistry; |
||||
chainMetadata: ChainMap<ChainMetadata>; |
||||
multiProvider: MultiProvider; |
||||
skipConfirmation: boolean; |
||||
key?: string; |
||||
signer?: ethers.Signer; |
||||
} |
||||
|
||||
export interface WriteCommandContext extends CommandContext { |
||||
key: string; |
||||
signer: ethers.Signer; |
||||
isDryRun?: boolean; |
||||
dryRunChain?: string; |
||||
} |
||||
|
||||
export type CommandModuleWithContext<Args> = CommandModule< |
||||
{}, |
||||
Args & { context: CommandContext } |
||||
>; |
||||
|
||||
export type CommandModuleWithWriteContext<Args> = CommandModule< |
||||
{}, |
||||
Args & { context: WriteCommandContext } |
||||
>; |
@ -0,0 +1,156 @@ |
||||
import { Logger } from 'pino'; |
||||
|
||||
import { |
||||
BaseRegistry, |
||||
ChainAddresses, |
||||
GithubRegistry, |
||||
IRegistry, |
||||
RegistryContent, |
||||
RegistryType, |
||||
} from '@hyperlane-xyz/registry'; |
||||
import { LocalRegistry } from '@hyperlane-xyz/registry/local'; |
||||
import { |
||||
ChainMap, |
||||
ChainMetadata, |
||||
ChainName, |
||||
WarpCoreConfig, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
import { |
||||
isHttpsUrl, |
||||
objKeys, |
||||
objMerge, |
||||
rootLogger, |
||||
} from '@hyperlane-xyz/utils'; |
||||
|
||||
export interface MergedRegistryOptions { |
||||
registryUris: Array<string>; |
||||
isDryRun?: boolean; |
||||
logger?: Logger; |
||||
} |
||||
|
||||
export class MergedRegistry extends BaseRegistry implements IRegistry { |
||||
public readonly type = RegistryType.Local; |
||||
public readonly registries: Array<IRegistry>; |
||||
public readonly isDryRun: boolean; |
||||
|
||||
constructor({ registryUris, logger, isDryRun }: MergedRegistryOptions) { |
||||
logger ||= rootLogger.child({ module: 'MergedRegistry' }); |
||||
super({ uri: '__merged_registry__', logger }); |
||||
|
||||
if (!registryUris.length) |
||||
throw new Error('At least one registry URI is required'); |
||||
|
||||
this.registries = registryUris.map((uri, index) => { |
||||
if (isHttpsUrl(uri)) { |
||||
return new GithubRegistry({ uri, logger: logger!.child({ index }) }); |
||||
} else { |
||||
return new LocalRegistry({ uri, logger: logger!.child({ index }) }); |
||||
} |
||||
}); |
||||
|
||||
this.isDryRun = !!isDryRun; |
||||
} |
||||
|
||||
async listRegistryContent(): Promise<RegistryContent> { |
||||
const results = await this.multiRegistryRead((r) => |
||||
r.listRegistryContent(), |
||||
); |
||||
return results.reduce((acc, content) => objMerge(acc, content), { |
||||
chains: {}, |
||||
deployments: {}, |
||||
}); |
||||
} |
||||
|
||||
async getChains(): Promise<Array<ChainName>> { |
||||
return objKeys(await this.getMetadata); |
||||
} |
||||
|
||||
async getMetadata(): Promise<ChainMap<ChainMetadata>> { |
||||
const results = await this.multiRegistryRead((r) => r.getMetadata()); |
||||
return results.reduce((acc, content) => objMerge(acc, content), {}); |
||||
} |
||||
|
||||
async getChainMetadata(chainName: ChainName): Promise<ChainMetadata | null> { |
||||
return (await this.getMetadata())[chainName] || null; |
||||
} |
||||
|
||||
async getAddresses(): Promise<ChainMap<ChainAddresses>> { |
||||
const results = await this.multiRegistryRead((r) => r.getAddresses()); |
||||
return results.reduce((acc, content) => objMerge(acc, content), {}); |
||||
} |
||||
|
||||
async getChainAddresses( |
||||
chainName: ChainName, |
||||
): Promise<ChainAddresses | null> { |
||||
return (await this.getAddresses())[chainName] || null; |
||||
} |
||||
|
||||
async addChain(chain: { |
||||
chainName: ChainName; |
||||
metadata?: ChainMetadata; |
||||
addresses?: ChainAddresses; |
||||
}): Promise<void> { |
||||
return this.multiRegistryWrite( |
||||
async (registry) => await registry.addChain(chain), |
||||
`adding chain ${chain.chainName}`, |
||||
); |
||||
} |
||||
|
||||
async updateChain(chain: { |
||||
chainName: ChainName; |
||||
metadata?: ChainMetadata; |
||||
addresses?: ChainAddresses; |
||||
}): Promise<void> { |
||||
return this.multiRegistryWrite( |
||||
async (registry) => await registry.updateChain(chain), |
||||
`updating chain ${chain.chainName}`, |
||||
); |
||||
} |
||||
|
||||
async removeChain(chain: ChainName): Promise<void> { |
||||
return this.multiRegistryWrite( |
||||
async (registry) => await registry.removeChain(chain), |
||||
`removing chain ${chain}`, |
||||
); |
||||
} |
||||
|
||||
async addWarpRoute(config: WarpCoreConfig): Promise<void> { |
||||
return this.multiRegistryWrite( |
||||
async (registry) => await registry.addWarpRoute(config), |
||||
'adding warp route', |
||||
); |
||||
} |
||||
|
||||
protected multiRegistryRead<R>( |
||||
readFn: (registry: IRegistry) => Promise<R> | R, |
||||
) { |
||||
return Promise.all(this.registries.map(readFn)); |
||||
} |
||||
|
||||
protected async multiRegistryWrite( |
||||
writeFn: (registry: IRegistry) => Promise<void>, |
||||
logMsg: string, |
||||
): Promise<void> { |
||||
if (this.isDryRun) return; |
||||
for (const registry of this.registries) { |
||||
// TODO remove this when GithubRegistry supports write methods
|
||||
if (registry.type === RegistryType.Github) { |
||||
this.logger.warn(`skipping ${logMsg} at ${registry.type} registry`); |
||||
continue; |
||||
} |
||||
try { |
||||
this.logger.info( |
||||
`${logMsg} at ${registry.type} registry at ${registry.uri}`, |
||||
); |
||||
await writeFn(registry); |
||||
this.logger.info(`done ${logMsg} at ${registry.type} registry`); |
||||
} catch (error) { |
||||
// To prevent loss of artifacts, MergedRegistry write methods are failure tolerant
|
||||
this.logger.error( |
||||
`failure ${logMsg} at ${registry.type} registry`, |
||||
error, |
||||
); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,19 @@ |
||||
import select from '@inquirer/select'; |
||||
|
||||
import { Token } from '@hyperlane-xyz/sdk'; |
||||
|
||||
export async function runTokenSelectionStep( |
||||
tokens: Token[], |
||||
message = 'Select token', |
||||
) { |
||||
const choices = tokens.map((t) => ({ |
||||
name: `${t.symbol} - ${t.addressOrDenom}`, |
||||
value: t.addressOrDenom, |
||||
})); |
||||
const routerAddress = (await select({ |
||||
message, |
||||
choices, |
||||
pageSize: 20, |
||||
})) as string; |
||||
return routerAddress; |
||||
} |
@ -0,0 +1,14 @@ |
||||
# Configs for describing chain metadata for use in Hyperlane deployments or apps |
||||
# Consists of a map of chain names to metadata |
||||
# Schema here: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/sdk/src/metadata/chainMetadataTypes.ts |
||||
--- |
||||
chainId: 31337 |
||||
domainId: 31337 |
||||
name: anvil1 |
||||
protocol: ethereum |
||||
rpcUrls: |
||||
- http: http://127.0.0.1:8545 |
||||
nativeToken: |
||||
name: Ether |
||||
symbol: ETH |
||||
decimals: 18 |
@ -0,0 +1,7 @@ |
||||
--- |
||||
chainId: 31338 |
||||
domainId: 31338 |
||||
name: anvil2 |
||||
protocol: ethereum |
||||
rpcUrls: |
||||
- http: http://127.0.0.1:8555 |
@ -0,0 +1,13 @@ |
||||
chainId: 44787 |
||||
domainId: 44787 |
||||
name: alfajores |
||||
nativeToken: |
||||
decimals: 18 |
||||
name: CELO |
||||
symbol: CELO |
||||
protocol: ethereum |
||||
rpcUrls: |
||||
- http: https://alfajores-forno.celo-testnet.org |
||||
blocks: |
||||
confirmations: 1 |
||||
estimateBlockTime: 1 |
@ -0,0 +1,10 @@ |
||||
chainId: 31337 |
||||
domainId: 31337 |
||||
name: anvil |
||||
protocol: ethereum |
||||
rpcUrls: |
||||
- http: http://127.0.0.1:8545 |
||||
nativeToken: |
||||
name: Ether |
||||
symbol: ETH |
||||
decimals: 18 |
@ -0,0 +1,6 @@ |
||||
chainId: 43113 |
||||
domainId: 43113 |
||||
name: fuji |
||||
protocol: ethereum |
||||
rpcUrls: |
||||
- http: https://api.avax-test.network/ext/bc/C/rpc |
@ -0,0 +1,11 @@ |
||||
--- |
||||
chainId: 31337 |
||||
domainId: 31337 |
||||
name: anvil1 |
||||
protocol: ethereum |
||||
rpcUrls: |
||||
- http: http://127.0.0.1:8545 |
||||
nativeToken: |
||||
name: Ether |
||||
symbol: ETH |
||||
decimals: 18 |
@ -0,0 +1,9 @@ |
||||
chainId: 1 |
||||
domainId: 1 |
||||
name: ethereum |
||||
protocol: ethereum |
||||
rpcUrls: |
||||
- http: http://127.0.0.1:8555 |
||||
blocks: |
||||
confirmations: 1 |
||||
estimateBlockTime: 1 |
@ -1,9 +1,8 @@ |
||||
# A config for a multisig Interchain Security Module (ISM) |
||||
# Schema: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/sdk/src/ism/types.ts |
||||
# |
||||
|
||||
--- |
||||
anvil: |
||||
anvil1: |
||||
threshold: 1 # Number: Signatures required to approve a message |
||||
validators: # Array: List of validator addresses |
||||
- '0xa0ee7a142d267c1f36714e4a8f75612f20a79720' |
@ -1,8 +1,9 @@ |
||||
import { RouterConfig, chainMetadata } from '@hyperlane-xyz/sdk'; |
||||
import { chainMetadata } from '@hyperlane-xyz/registry'; |
||||
import { RouterConfig } from '@hyperlane-xyz/sdk'; |
||||
|
||||
export type HelloWorldConfig = RouterConfig; |
||||
|
||||
// SET DESIRED NETWORKS HERE
|
||||
// SET DESIRED NETWORKS HERE OR USE THE DEFAULT SET FROM THE REGISTRY
|
||||
export const prodConfigs = { |
||||
alfajores: chainMetadata.alfajores, |
||||
...chainMetadata, |
||||
}; |
||||
|
@ -0,0 +1,8 @@ |
||||
# @hyperlane-xyz/infra |
||||
|
||||
Various scripts and utilities for managing and deploying Hyperlane infrastructure. |
||||
|
||||
## Tips |
||||
|
||||
- To enable more verbose logging, see the log env vars mentioned in the [root readme](../../README.md) |
||||
- To configure the local registry path, set the `REGISTRY_URI` env var |
@ -0,0 +1,24 @@ |
||||
// These chains may be any protocol type.
|
||||
// Placing them here instead of adjacent chains file to avoid circular dep
|
||||
export const supportedChainNames = [ |
||||
'arbitrum', |
||||
'ancient8', |
||||
'avalanche', |
||||
'blast', |
||||
'bsc', |
||||
'celo', |
||||
'ethereum', |
||||
'neutron', |
||||
'mantapacific', |
||||
'mode', |
||||
'moonbeam', |
||||
'optimism', |
||||
'polygon', |
||||
'gnosis', |
||||
'base', |
||||
'scroll', |
||||
'polygonzkevm', |
||||
'injective', |
||||
'inevm', |
||||
'viction', |
||||
]; |
@ -1,18 +1,15 @@ |
||||
import { ChainMap, ChainMetadata, chainMetadata } from '@hyperlane-xyz/sdk'; |
||||
import { |
||||
testChainMetadata as defaultTestChainMetadata, |
||||
testChains as defaultTestChains, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
|
||||
import { AgentChainNames, Role } from '../../../src/roles.js'; |
||||
|
||||
export const testConfigs: ChainMap<ChainMetadata> = { |
||||
test1: chainMetadata.test1, |
||||
test2: chainMetadata.test2, |
||||
test3: chainMetadata.test3, |
||||
}; |
||||
|
||||
export type TestChains = keyof typeof testConfigs; |
||||
export const chainNames = Object.keys(testConfigs) as TestChains[]; |
||||
export const testChainNames = defaultTestChains; |
||||
export const testChainMetadata = { ...defaultTestChainMetadata }; |
||||
|
||||
export const agentChainNames: AgentChainNames = { |
||||
[Role.Validator]: chainNames, |
||||
[Role.Relayer]: chainNames, |
||||
[Role.Scraper]: chainNames, |
||||
[Role.Validator]: testChainNames, |
||||
[Role.Relayer]: testChainNames, |
||||
[Role.Scraper]: testChainNames, |
||||
}; |
||||
|
@ -1,9 +1,9 @@ |
||||
import { ChainMap, OwnableConfig } from '@hyperlane-xyz/sdk'; |
||||
|
||||
import { chainNames } from './chains.js'; |
||||
import { testChainNames } from './chains.js'; |
||||
|
||||
// Owner is hardhat account 0
|
||||
const OWNER_ADDRESS = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'; |
||||
export const owners: ChainMap<OwnableConfig> = Object.fromEntries( |
||||
chainNames.map((chain) => [chain, { owner: OWNER_ADDRESS }]), |
||||
testChainNames.map((chain) => [chain, { owner: OWNER_ADDRESS }]), |
||||
); |
||||
|
@ -1,33 +1,24 @@ |
||||
import { |
||||
ChainMap, |
||||
ChainMetadata, |
||||
Chains, |
||||
chainMetadata, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
import { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk'; |
||||
import { objKeys } from '@hyperlane-xyz/utils'; |
||||
|
||||
// All supported chains for the testnet4 environment.
|
||||
// These chains may be any protocol type.
|
||||
export const supportedChainNames = [ |
||||
Chains.alfajores, |
||||
Chains.bsctestnet, |
||||
Chains.eclipsetestnet, |
||||
Chains.fuji, |
||||
Chains.plumetestnet, |
||||
Chains.scrollsepolia, |
||||
Chains.sepolia, |
||||
Chains.solanatestnet, |
||||
]; |
||||
import { getChainMetadatas } from '../../../src/config/chain.js'; |
||||
import { getChain } from '../../registry.js'; |
||||
|
||||
import { supportedChainNames } from './supportedChainNames.js'; |
||||
|
||||
export const environment = 'testnet4'; |
||||
|
||||
const { ethereumMetadatas: defaultEthereumMainnetConfigs } = |
||||
getChainMetadatas(supportedChainNames); |
||||
|
||||
export const testnetConfigs: ChainMap<ChainMetadata> = { |
||||
...Object.fromEntries( |
||||
supportedChainNames.map((chain) => [chain, chainMetadata[chain]]), |
||||
), |
||||
...defaultEthereumMainnetConfigs, |
||||
bsctestnet: { |
||||
...chainMetadata.bsctestnet, |
||||
...getChain('bsctestnet'), |
||||
transactionOverrides: { |
||||
gasPrice: 8 * 10 ** 9, // 8 gwei
|
||||
}, |
||||
}, |
||||
}; |
||||
|
||||
export const ethereumChainNames = objKeys(defaultEthereumMainnetConfigs); |
||||
|
@ -0,0 +1,12 @@ |
||||
// These chains may be any protocol type.
|
||||
// Placing them here instead of adjacent chains file to avoid circular dep
|
||||
export const supportedChainNames = [ |
||||
'alfajores', |
||||
'bsctestnet', |
||||
'eclipsetestnet', |
||||
'fuji', |
||||
'plumetestnet', |
||||
'scrollsepolia', |
||||
'sepolia', |
||||
'solanatestnet', |
||||
]; |
@ -0,0 +1,109 @@ |
||||
import { dirname, join } from 'path'; |
||||
import { fileURLToPath } from 'url'; |
||||
|
||||
import { ChainAddresses } from '@hyperlane-xyz/registry'; |
||||
import { LocalRegistry } from '@hyperlane-xyz/registry/local'; |
||||
import { |
||||
ChainMap, |
||||
ChainMetadata, |
||||
ChainName, |
||||
getDomainId as resolveDomainId, |
||||
getReorgPeriod as resolveReorgPeriod, |
||||
} from '@hyperlane-xyz/sdk'; |
||||
import { objFilter, rootLogger } from '@hyperlane-xyz/utils'; |
||||
|
||||
import type { DeployEnvironment } from '../src/config/environment.js'; |
||||
|
||||
import { supportedChainNames as mainnet3Chains } from './environments/mainnet3/supportedChainNames.js'; |
||||
import { |
||||
testChainMetadata, |
||||
testChainNames as testChains, |
||||
} from './environments/test/chains.js'; |
||||
import { supportedChainNames as testnet4Chains } from './environments/testnet4/supportedChainNames.js'; |
||||
|
||||
const DEFAULT_REGISTRY_URI = join( |
||||
dirname(fileURLToPath(import.meta.url)), |
||||
'../../../../', |
||||
'hyperlane-registry', |
||||
); |
||||
|
||||
// A global Registry singleton
|
||||
// All uses of chain metadata or chain address artifacts should go through this registry.
|
||||
let registry: LocalRegistry; |
||||
|
||||
export function setRegistry(reg: LocalRegistry) { |
||||
registry = reg; |
||||
} |
||||
|
||||
export function getRegistry(): LocalRegistry { |
||||
if (!registry) { |
||||
const registryUri = process.env.REGISTRY_URI || DEFAULT_REGISTRY_URI; |
||||
rootLogger.info('Using registry URI:', registryUri); |
||||
registry = new LocalRegistry({ |
||||
uri: registryUri, |
||||
logger: rootLogger.child({ module: 'infra-registry' }), |
||||
}); |
||||
} |
||||
return registry; |
||||
} |
||||
|
||||
export function getChains(): ChainName[] { |
||||
return getRegistry().getChains(); |
||||
} |
||||
|
||||
export function getChain(chainName: ChainName): ChainMetadata { |
||||
if (testChains.includes(chainName)) { |
||||
return testChainMetadata[chainName]; |
||||
} |
||||
return getRegistry().getChainMetadata(chainName); |
||||
} |
||||
|
||||
export function getDomainId(chainName: ChainName): number { |
||||
return resolveDomainId(getChain(chainName)); |
||||
} |
||||
|
||||
export function getReorgPeriod(chainName: ChainName): number { |
||||
return resolveReorgPeriod(getChain(chainName)); |
||||
} |
||||
|
||||
export function getChainMetadata(): ChainMap<ChainMetadata> { |
||||
return getRegistry().getMetadata(); |
||||
} |
||||
|
||||
export function getChainAddresses(): ChainMap<ChainAddresses> { |
||||
return getRegistry().getAddresses(); |
||||
} |
||||
|
||||
export function getEnvChains(env: DeployEnvironment): ChainName[] { |
||||
if (env === 'mainnet3') return mainnet3Chains; |
||||
if (env === 'testnet4') return testnet4Chains; |
||||
if (env === 'test') return testChains; |
||||
throw Error(`Unsupported deploy environment: ${env}`); |
||||
} |
||||
|
||||
export function getMainnets(): ChainName[] { |
||||
return getEnvChains('mainnet3'); |
||||
} |
||||
|
||||
export function getTestnets(): ChainName[] { |
||||
return getEnvChains('testnet4'); |
||||
} |
||||
|
||||
export function getEnvAddresses( |
||||
env: DeployEnvironment, |
||||
): ChainMap<ChainAddresses> { |
||||
const envChains = getEnvChains(env); |
||||
return objFilter( |
||||
getChainAddresses(), |
||||
(chain, addresses): addresses is ChainAddresses => |
||||
getEnvChains(env).includes(chain), |
||||
); |
||||
} |
||||
|
||||
export function getMainnetAddresses(): ChainMap<ChainAddresses> { |
||||
return getEnvAddresses('mainnet3'); |
||||
} |
||||
|
||||
export function getTestnetAddresses(): ChainMap<ChainAddresses> { |
||||
return getEnvAddresses('testnet4'); |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue