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 |
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* |
.env* |
||||||
/dist |
/dist |
||||||
/cache |
/cache |
||||||
|
|
||||||
|
# Deployment artifacts and local registry configs |
||||||
/configs |
/configs |
||||||
/artifacts |
/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 |
# 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 |
# 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 |
# Required fields: |
||||||
mychainname: |
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: |
# Required fields: |
||||||
chainId: 1234567890 # Number: Use EIP-155 for EVM chains |
- name: My Chain Explorer # String: Human-readable name for the explorer |
||||||
domainId: 1234567890 # Number: Recommend matching chainId when possible |
url: https://mychain.com/explorer # String: Base URL for the explorer |
||||||
name: mychainname # String: Unique identifier for the chain, must match key above |
apiUrl: https://mychain.com/api # String: Base URL for the explorer API |
||||||
protocol: ethereum # ProtocolType: Ethereum, Sealevel, etc. |
# Optional fields: |
||||||
rpcUrls: # Array: List of RPC configs |
apiKey: myapikey # String: API key for the explorer (optional) |
||||||
# Only http field is required |
family: etherscan # ExplorerFamily: See ExplorerFamily for valid values |
||||||
- http: https://mychain.com/rpc # String: HTTP URL of the RPC endpoint (preferably HTTPS) |
nativeToken: |
||||||
# Others here are optional |
name: Eth # String |
||||||
pagination: |
symbol: ETH # String |
||||||
maxBlockRange: 1000 # Number |
decimals: 18 # Number |
||||||
maxBlockAge: 1000 # Number |
displayName: My Chain Name # String: Human-readable name of the chain |
||||||
minBlockNumber: 1000 # Number |
displayNameShort: My Chain # String: A shorter human-readable name |
||||||
retry: |
logoURI: https://mychain.com/logo.png # String: URI to a logo image for the chain |
||||||
maxRequests: 5 # Number |
blocks: |
||||||
baseRetryMs: 1000 # Number |
confirmations: 12 # Number: Blocks to wait before considering a transaction confirmed |
||||||
# Optional fields, not required for Hyperlane deployments but useful for apps: |
reorgPeriod: 100 # Number: Blocks before a transaction has a near-zero chance of reverting |
||||||
isTestnet: false # Boolean: Whether the chain is considered a testnet or a mainnet |
estimateBlockTime: 15 # Number: Rough estimate of time per block in seconds |
||||||
blockExplorers: # Array: List of BlockExplorer configs |
# transactionOverrides: # Object: Properties to include when forming transaction requests |
||||||
# Required fields: |
# Any tx fields are allowed |
||||||
- 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 |
|
||||||
|
@ -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) |
# 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 |
# 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 |
threshold: 1 # Number: Signatures required to approve a message |
||||||
validators: # Array: List of validator addresses |
validators: # Array: List of validator addresses |
||||||
- '0xa0ee7a142d267c1f36714e4a8f75612f20a79720' |
- '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; |
export type HelloWorldConfig = RouterConfig; |
||||||
|
|
||||||
// SET DESIRED NETWORKS HERE
|
// SET DESIRED NETWORKS HERE OR USE THE DEFAULT SET FROM THE REGISTRY
|
||||||
export const prodConfigs = { |
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'; |
import { AgentChainNames, Role } from '../../../src/roles.js'; |
||||||
|
|
||||||
export const testConfigs: ChainMap<ChainMetadata> = { |
export const testChainNames = defaultTestChains; |
||||||
test1: chainMetadata.test1, |
export const testChainMetadata = { ...defaultTestChainMetadata }; |
||||||
test2: chainMetadata.test2, |
|
||||||
test3: chainMetadata.test3, |
|
||||||
}; |
|
||||||
|
|
||||||
export type TestChains = keyof typeof testConfigs; |
|
||||||
export const chainNames = Object.keys(testConfigs) as TestChains[]; |
|
||||||
|
|
||||||
export const agentChainNames: AgentChainNames = { |
export const agentChainNames: AgentChainNames = { |
||||||
[Role.Validator]: chainNames, |
[Role.Validator]: testChainNames, |
||||||
[Role.Relayer]: chainNames, |
[Role.Relayer]: testChainNames, |
||||||
[Role.Scraper]: chainNames, |
[Role.Scraper]: testChainNames, |
||||||
}; |
}; |
||||||
|
@ -1,9 +1,9 @@ |
|||||||
import { ChainMap, OwnableConfig } from '@hyperlane-xyz/sdk'; |
import { ChainMap, OwnableConfig } from '@hyperlane-xyz/sdk'; |
||||||
|
|
||||||
import { chainNames } from './chains.js'; |
import { testChainNames } from './chains.js'; |
||||||
|
|
||||||
// Owner is hardhat account 0
|
// Owner is hardhat account 0
|
||||||
const OWNER_ADDRESS = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'; |
const OWNER_ADDRESS = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'; |
||||||
export const owners: ChainMap<OwnableConfig> = Object.fromEntries( |
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 { |
import { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk'; |
||||||
ChainMap, |
import { objKeys } from '@hyperlane-xyz/utils'; |
||||||
ChainMetadata, |
|
||||||
Chains, |
|
||||||
chainMetadata, |
|
||||||
} from '@hyperlane-xyz/sdk'; |
|
||||||
|
|
||||||
// All supported chains for the testnet4 environment.
|
import { getChainMetadatas } from '../../../src/config/chain.js'; |
||||||
// These chains may be any protocol type.
|
import { getChain } from '../../registry.js'; |
||||||
export const supportedChainNames = [ |
|
||||||
Chains.alfajores, |
import { supportedChainNames } from './supportedChainNames.js'; |
||||||
Chains.bsctestnet, |
|
||||||
Chains.eclipsetestnet, |
|
||||||
Chains.fuji, |
|
||||||
Chains.plumetestnet, |
|
||||||
Chains.scrollsepolia, |
|
||||||
Chains.sepolia, |
|
||||||
Chains.solanatestnet, |
|
||||||
]; |
|
||||||
|
|
||||||
export const environment = 'testnet4'; |
export const environment = 'testnet4'; |
||||||
|
|
||||||
|
const { ethereumMetadatas: defaultEthereumMainnetConfigs } = |
||||||
|
getChainMetadatas(supportedChainNames); |
||||||
|
|
||||||
export const testnetConfigs: ChainMap<ChainMetadata> = { |
export const testnetConfigs: ChainMap<ChainMetadata> = { |
||||||
...Object.fromEntries( |
...defaultEthereumMainnetConfigs, |
||||||
supportedChainNames.map((chain) => [chain, chainMetadata[chain]]), |
|
||||||
), |
|
||||||
bsctestnet: { |
bsctestnet: { |
||||||
...chainMetadata.bsctestnet, |
...getChain('bsctestnet'), |
||||||
transactionOverrides: { |
transactionOverrides: { |
||||||
gasPrice: 8 * 10 ** 9, // 8 gwei
|
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