feat(infra): strongly type keyfunder config (#4093)

resolves https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3732

- strongly type the keyfunder config
- new chains added to an environment's supported chains list must be
configured
- opens up the path to strongly type more of the env config down the
line

missing 1 chain:
<img width="336" alt="image"
src="https://github.com/hyperlane-xyz/hyperlane-monorepo/assets/10051819/698ef9db-c50a-4a49-bc11-d33a44f186bf">

missing multiple chains:
<img width="350" alt="image"
src="https://github.com/hyperlane-xyz/hyperlane-monorepo/assets/10051819/2f9e4e6c-5c36-4239-9c00-5951eca91589">
pull/4077/head
Paul Balaji 5 months ago committed by GitHub
parent 46652c62a8
commit 94e6ca21ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 17
      typescript/infra/config/environments/mainnet3/funding.ts
  2. 2
      typescript/infra/config/environments/mainnet3/igp.ts
  3. 6
      typescript/infra/config/environments/mainnet3/supportedChainNames.ts
  4. 15
      typescript/infra/config/environments/testnet4/funding.ts
  5. 1
      typescript/infra/config/environments/testnet4/index.ts
  6. 7
      typescript/infra/config/environments/testnet4/supportedChainNames.ts
  7. 6
      typescript/infra/config/registry.ts
  8. 36
      typescript/infra/scripts/funding/fund-keys-from-deployer.ts
  9. 10
      typescript/infra/scripts/print-token-prices.ts
  10. 3
      typescript/infra/src/config/environment.ts
  11. 10
      typescript/infra/src/config/funding.ts
  12. 6
      typescript/infra/src/funding/key-funder.ts

@ -3,8 +3,11 @@ import { Role } from '../../../src/roles.js';
import { Contexts } from '../../contexts.js'; import { Contexts } from '../../contexts.js';
import { environment } from './chains.js'; import { environment } from './chains.js';
import { mainnet3SupportedChainNames } from './supportedChainNames.js';
export const keyFunderConfig: KeyFunderConfig = { export const keyFunderConfig: KeyFunderConfig<
typeof mainnet3SupportedChainNames
> = {
docker: { docker: {
repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo',
tag: '1b5e4d4-20240702-152903', tag: '1b5e4d4-20240702-152903',
@ -49,6 +52,10 @@ export const keyFunderConfig: KeyFunderConfig = {
taiko: '0.2', taiko: '0.2',
viction: '3', viction: '3',
zetachain: '20', zetachain: '20',
// ignore cosmos chains
injective: '0',
neutron: '0',
osmosis: '0',
}, },
desiredKathyBalancePerChain: { desiredKathyBalancePerChain: {
arbitrum: '0.1', arbitrum: '0.1',
@ -77,6 +84,10 @@ export const keyFunderConfig: KeyFunderConfig = {
taiko: '0', taiko: '0',
viction: '0.05', viction: '0.05',
zetachain: '0', zetachain: '0',
// ignore cosmos chains
injective: '0',
neutron: '0',
osmosis: '0',
}, },
igpClaimThresholdPerChain: { igpClaimThresholdPerChain: {
arbitrum: '0.1', arbitrum: '0.1',
@ -105,5 +116,9 @@ export const keyFunderConfig: KeyFunderConfig = {
taiko: '0.1', taiko: '0.1',
viction: '2', viction: '2',
zetachain: '20', zetachain: '20',
// ignore cosmos chains
injective: '0',
neutron: '0',
osmosis: '0',
}, },
}; };

@ -28,7 +28,7 @@ const tokenPrices: ChainMap<string> = rawTokenPrices;
const FOREIGN_DEFAULT_OVERHEAD = 600_000; // cosmwasm warp route somewhat arbitrarily chosen const FOREIGN_DEFAULT_OVERHEAD = 600_000; // cosmwasm warp route somewhat arbitrarily chosen
const remoteOverhead = (remote: ChainName) => const remoteOverhead = (remote: ChainName) =>
ethereumChainNames.includes(remote) ethereumChainNames.includes(remote as any)
? multisigIsmVerificationCost( ? multisigIsmVerificationCost(
defaultMultisigConfigs[remote].threshold, defaultMultisigConfigs[remote].threshold,
defaultMultisigConfigs[remote].validators.length, defaultMultisigConfigs[remote].validators.length,

@ -1,6 +1,6 @@
// These chains may be any protocol type. // These chains may be any protocol type.
// Placing them here instead of adjacent chains file to avoid circular dep // Placing them here instead of adjacent chains file to avoid circular dep
export const supportedChainNames = [ export const mainnet3SupportedChainNames = [
'arbitrum', 'arbitrum',
'ancient8', 'ancient8',
'avalanche', 'avalanche',
@ -30,4 +30,6 @@ export const supportedChainNames = [
'taiko', 'taiko',
'viction', 'viction',
'zetachain', 'zetachain',
]; ] as const;
export const supportedChainNames = [...mainnet3SupportedChainNames];

@ -3,8 +3,11 @@ import { Role } from '../../../src/roles.js';
import { Contexts } from '../../contexts.js'; import { Contexts } from '../../contexts.js';
import { environment } from './chains.js'; import { environment } from './chains.js';
import { testnet4SupportedChainNames } from './supportedChainNames.js';
export const keyFunderConfig: KeyFunderConfig = { export const keyFunderConfig: KeyFunderConfig<
typeof testnet4SupportedChainNames
> = {
docker: { docker: {
repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo',
tag: 'efa9025-20240605-091304', tag: 'efa9025-20240605-091304',
@ -31,14 +34,21 @@ export const keyFunderConfig: KeyFunderConfig = {
// Funder boosts itself upto 5x balance on L2 before dispersing funds // Funder boosts itself upto 5x balance on L2 before dispersing funds
scrollsepolia: '1', scrollsepolia: '1',
sepolia: '5', sepolia: '5',
// no funding for solana
eclipsetestnet: '0',
solanatestnet: '0',
}, },
desiredKathyBalancePerChain: { desiredKathyBalancePerChain: {
alfajores: '1', alfajores: '1',
bsctestnet: '1', bsctestnet: '1',
fuji: '1', fuji: '1',
holesky: '0',
plumetestnet: '0.05', plumetestnet: '0.05',
scrollsepolia: '1', scrollsepolia: '1',
sepolia: '1', sepolia: '1',
// no funding for solana
eclipsetestnet: '0',
solanatestnet: '0',
}, },
igpClaimThresholdPerChain: { igpClaimThresholdPerChain: {
alfajores: '1', alfajores: '1',
@ -48,5 +58,8 @@ export const keyFunderConfig: KeyFunderConfig = {
plumetestnet: '0.1', plumetestnet: '0.1',
scrollsepolia: '0.1', scrollsepolia: '0.1',
sepolia: '1', sepolia: '1',
// no funding for solana
eclipsetestnet: '0',
solanatestnet: '0',
}, },
}; };

@ -1,5 +1,4 @@
import { IRegistry } from '@hyperlane-xyz/registry'; import { IRegistry } from '@hyperlane-xyz/registry';
import { objMerge } from '@hyperlane-xyz/utils';
import { import {
getKeysForRole, getKeysForRole,

@ -1,6 +1,5 @@
// These chains may be any protocol type.
// Placing them here instead of adjacent chains file to avoid circular dep // Placing them here instead of adjacent chains file to avoid circular dep
export const supportedChainNames = [ export const testnet4SupportedChainNames = [
'alfajores', 'alfajores',
'bsctestnet', 'bsctestnet',
'eclipsetestnet', 'eclipsetestnet',
@ -10,4 +9,6 @@ export const supportedChainNames = [
'scrollsepolia', 'scrollsepolia',
'sepolia', 'sepolia',
'solanatestnet', 'solanatestnet',
]; ] as const;
export const supportedChainNames = [...testnet4SupportedChainNames];

@ -109,10 +109,8 @@ export function getEnvAddresses(
env: DeployEnvironment, env: DeployEnvironment,
): ChainMap<ChainAddresses> { ): ChainMap<ChainAddresses> {
const envChains = getEnvChains(env); const envChains = getEnvChains(env);
return objFilter( return objFilter(getChainAddresses(), (chain, _): _ is ChainAddresses =>
getChainAddresses(), envChains.includes(chain),
(chain, addresses): addresses is ChainAddresses =>
getEnvChains(env).includes(chain),
); );
} }

@ -225,9 +225,15 @@ class ContextFunder {
public readonly context: Contexts, public readonly context: Contexts,
public readonly rolesToFund: FundableRole[], public readonly rolesToFund: FundableRole[],
public readonly skipIgpClaim: boolean, public readonly skipIgpClaim: boolean,
public readonly desiredBalancePerChain: KeyFunderConfig['desiredBalancePerChain'], public readonly desiredBalancePerChain: KeyFunderConfig<
public readonly desiredKathyBalancePerChain: KeyFunderConfig['desiredKathyBalancePerChain'], ChainName[]
public readonly igpClaimThresholdPerChain: KeyFunderConfig['igpClaimThresholdPerChain'], >['desiredBalancePerChain'],
public readonly desiredKathyBalancePerChain: KeyFunderConfig<
ChainName[]
>['desiredKathyBalancePerChain'],
public readonly igpClaimThresholdPerChain: KeyFunderConfig<
ChainName[]
>['igpClaimThresholdPerChain'],
) { ) {
// At the moment, only blessed EVM chains are supported // At the moment, only blessed EVM chains are supported
roleKeysPerChain = objFilter( roleKeysPerChain = objFilter(
@ -266,9 +272,15 @@ class ContextFunder {
multiProvider: MultiProvider, multiProvider: MultiProvider,
contextsAndRolesToFund: ContextAndRolesMap, contextsAndRolesToFund: ContextAndRolesMap,
skipIgpClaim: boolean, skipIgpClaim: boolean,
desiredBalancePerChain: KeyFunderConfig['desiredBalancePerChain'], desiredBalancePerChain: KeyFunderConfig<
desiredKathyBalancePerChain: KeyFunderConfig['desiredKathyBalancePerChain'], ChainName[]
igpClaimThresholdPerChain: KeyFunderConfig['igpClaimThresholdPerChain'], >['desiredBalancePerChain'],
desiredKathyBalancePerChain: KeyFunderConfig<
ChainName[]
>['desiredKathyBalancePerChain'],
igpClaimThresholdPerChain: KeyFunderConfig<
ChainName[]
>['igpClaimThresholdPerChain'],
filePath: string, filePath: string,
) { ) {
logger.info({ filePath }, 'Reading identifiers and addresses from file'); logger.info({ filePath }, 'Reading identifiers and addresses from file');
@ -348,9 +360,15 @@ class ContextFunder {
context: Contexts, context: Contexts,
rolesToFund: FundableRole[], rolesToFund: FundableRole[],
skipIgpClaim: boolean, skipIgpClaim: boolean,
desiredBalancePerChain: KeyFunderConfig['desiredBalancePerChain'], desiredBalancePerChain: KeyFunderConfig<
desiredKathyBalancePerChain: KeyFunderConfig['desiredKathyBalancePerChain'], ChainName[]
igpClaimThresholdPerChain: KeyFunderConfig['igpClaimThresholdPerChain'], >['desiredBalancePerChain'],
desiredKathyBalancePerChain: KeyFunderConfig<
ChainName[]
>['desiredKathyBalancePerChain'],
igpClaimThresholdPerChain: KeyFunderConfig<
ChainName[]
>['igpClaimThresholdPerChain'],
) { ) {
// only roles that are fundable keys ie. relayer and kathy // only roles that are fundable keys ie. relayer and kathy
const fundableRoleKeys: Record<FundableRole, Address> = { const fundableRoleKeys: Record<FundableRole, Address> = {

@ -1,9 +1,10 @@
import { ChainMetadata } from '@hyperlane-xyz/sdk';
import { objMap, pick } from '@hyperlane-xyz/utils'; import { objMap, pick } from '@hyperlane-xyz/utils';
// Intentionally circumvent `mainnet3/index.ts` and `getEnvironmentConfig('mainnet3')` // Intentionally circumvent `mainnet3/index.ts` and `getEnvironmentConfig('mainnet3')`
// to avoid circular dependencies. // to avoid circular dependencies.
import { getRegistry as getMainnet3Registry } from '../config/environments/mainnet3/chains.js'; import { getRegistry as getMainnet3Registry } from '../config/environments/mainnet3/chains.js';
import { supportedChainNames as mainnet3SupportedChainNames } from '../config/environments/mainnet3/supportedChainNames.js'; import { mainnet3SupportedChainNames } from '../config/environments/mainnet3/supportedChainNames.js';
const CURRENCY = 'usd'; const CURRENCY = 'usd';
@ -11,8 +12,11 @@ async function main() {
const registry = await getMainnet3Registry(); const registry = await getMainnet3Registry();
const chainMetadata = await registry.getMetadata(); const chainMetadata = await registry.getMetadata();
const metadata = pick( const metadata = pick(
await registry.getMetadata(), chainMetadata as Record<
mainnet3SupportedChainNames, (typeof mainnet3SupportedChainNames)[number],
ChainMetadata
>,
[...mainnet3SupportedChainNames],
); );
const ids = objMap( const ids = objMap(

@ -2,7 +2,6 @@ import { IRegistry } from '@hyperlane-xyz/registry';
import { import {
BridgeAdapterConfig, BridgeAdapterConfig,
ChainMap, ChainMap,
ChainMetadata,
ChainName, ChainName,
CoreConfig, CoreConfig,
IgpConfig, IgpConfig,
@ -60,7 +59,7 @@ export type EnvironmentConfig = {
role?: Role, role?: Role,
) => Promise<ChainMap<CloudAgentKey>>; ) => Promise<ChainMap<CloudAgentKey>>;
helloWorld?: Partial<Record<Contexts, HelloWorldConfig>>; helloWorld?: Partial<Record<Contexts, HelloWorldConfig>>;
keyFunderConfig?: KeyFunderConfig; keyFunderConfig?: KeyFunderConfig<string[]>;
liquidityLayerConfig?: { liquidityLayerConfig?: {
bridgeAdapters: ChainMap<BridgeAdapterConfig>; bridgeAdapters: ChainMap<BridgeAdapterConfig>;
relayer: LiquidityLayerRelayerConfig; relayer: LiquidityLayerRelayerConfig;

@ -1,4 +1,4 @@
import { ChainMap } from '@hyperlane-xyz/sdk'; import { ChainName } from '@hyperlane-xyz/sdk';
import { Contexts } from '../../config/contexts.js'; import { Contexts } from '../../config/contexts.js';
import { FundableRole, Role } from '../roles.js'; import { FundableRole, Role } from '../roles.js';
@ -12,7 +12,7 @@ export interface ContextAndRoles {
export type ContextAndRolesMap = Partial<Record<Contexts, FundableRole[]>>; export type ContextAndRolesMap = Partial<Record<Contexts, FundableRole[]>>;
export interface KeyFunderConfig { export interface KeyFunderConfig<SupportedChains extends readonly ChainName[]> {
docker: DockerConfig; docker: DockerConfig;
cronSchedule: string; cronSchedule: string;
namespace: string; namespace: string;
@ -20,7 +20,7 @@ export interface KeyFunderConfig {
contextsAndRolesToFund: ContextAndRolesMap; contextsAndRolesToFund: ContextAndRolesMap;
cyclesBetweenEthereumMessages?: number; cyclesBetweenEthereumMessages?: number;
prometheusPushGateway: string; prometheusPushGateway: string;
desiredBalancePerChain: ChainMap<string>; desiredBalancePerChain: Record<SupportedChains[number], string>;
desiredKathyBalancePerChain: ChainMap<string>; desiredKathyBalancePerChain: Record<SupportedChains[number], string>;
igpClaimThresholdPerChain: ChainMap<string>; igpClaimThresholdPerChain: Record<SupportedChains[number], string>;
} }

@ -7,7 +7,7 @@ import { execCmd } from '../utils/utils.js';
export async function runKeyFunderHelmCommand( export async function runKeyFunderHelmCommand(
helmCommand: HelmCommand, helmCommand: HelmCommand,
agentConfig: AgentContextConfig, agentConfig: AgentContextConfig,
keyFunderConfig: KeyFunderConfig, keyFunderConfig: KeyFunderConfig<string[]>,
) { ) {
const values = getKeyFunderHelmValues(agentConfig, keyFunderConfig); const values = getKeyFunderHelmValues(agentConfig, keyFunderConfig);
if (helmCommand === HelmCommand.InstallOrUpgrade) { if (helmCommand === HelmCommand.InstallOrUpgrade) {
@ -36,7 +36,7 @@ export async function runKeyFunderHelmCommand(
function getKeyFunderHelmValues( function getKeyFunderHelmValues(
agentConfig: AgentContextConfig, agentConfig: AgentContextConfig,
keyFunderConfig: KeyFunderConfig, keyFunderConfig: KeyFunderConfig<string[]>,
) { ) {
const values = { const values = {
cronjob: { cronjob: {
@ -65,7 +65,7 @@ function getKeyFunderHelmValues(
export function getKeyFunderConfig( export function getKeyFunderConfig(
coreConfig: EnvironmentConfig, coreConfig: EnvironmentConfig,
): KeyFunderConfig { ): KeyFunderConfig<string[]> {
const keyFunderConfig = coreConfig.keyFunderConfig; const keyFunderConfig = coreConfig.keyFunderConfig;
if (!keyFunderConfig) { if (!keyFunderConfig) {
throw new Error( throw new Error(

Loading…
Cancel
Save