Logging overhaul (#3481)

### Description

- Replace use of debug lib with Pino
- Replace `loggerName` params with Logger types
- Update SDK Readme

### Related issues

Fixes https://github.com/hyperlane-xyz/issues/issues/918

### Backward compatibility

No, breaks anyone passing in custom logger to SDK classes, which is likely no one

### Testing

- Tested in CLI
- Tested in Warp UI
snapshot-igp-config
J M Rossy 8 months ago committed by GitHub
parent 4f0ea4361e
commit 4e7a43be6a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 9
      .changeset/shy-eyes-swim.md
  2. 2
      typescript/cli/ci-test.sh
  3. 3
      typescript/cli/cli.ts
  4. 18
      typescript/cli/env.ts
  5. 58
      typescript/cli/logger.ts
  6. 2
      typescript/cli/src/commands/chains.ts
  7. 2
      typescript/cli/src/commands/config.ts
  8. 2
      typescript/cli/src/commands/deploy.ts
  9. 2
      typescript/cli/src/commands/send.ts
  10. 2
      typescript/cli/src/config/artifacts.ts
  11. 2
      typescript/cli/src/config/chain.ts
  12. 2
      typescript/cli/src/config/hooks.ts
  13. 2
      typescript/cli/src/config/ism.ts
  14. 2
      typescript/cli/src/config/multisig.ts
  15. 2
      typescript/cli/src/config/warp.ts
  16. 2
      typescript/cli/src/deploy/agent.ts
  17. 16
      typescript/cli/src/deploy/core.ts
  18. 2
      typescript/cli/src/deploy/utils.ts
  19. 2
      typescript/cli/src/deploy/warp.ts
  20. 36
      typescript/cli/src/logger.ts
  21. 2
      typescript/cli/src/send/message.ts
  22. 2
      typescript/cli/src/send/transfer.ts
  23. 2
      typescript/cli/src/status/message.ts
  24. 2
      typescript/cli/src/utils/chains.ts
  25. 2
      typescript/cli/src/utils/files.ts
  26. 2
      typescript/cli/src/utils/version-check.ts
  27. 2
      typescript/cli/tsconfig.json
  28. 11
      typescript/helloworld/src/app/app.ts
  29. 2
      typescript/helloworld/src/multiProtocolApp/sealevelAdapter.ts
  30. 12
      typescript/infra/scripts/check-rpc-urls.ts
  31. 8
      typescript/infra/scripts/funding/fund-deterministic-key-from-deployer.ts
  32. 55
      typescript/infra/scripts/funding/fund-keys-from-deployer.ts
  33. 55
      typescript/infra/scripts/helloworld/kathy.ts
  34. 12
      typescript/infra/scripts/middleware/portal-relayer.ts
  35. 14
      typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts
  36. 12
      typescript/infra/src/utils/metrics.ts
  37. 12
      typescript/sdk/README.md
  38. 3
      typescript/sdk/package.json
  39. 6
      typescript/sdk/src/app/HyperlaneApp.ts
  40. 6
      typescript/sdk/src/app/MultiProtocolApp.ts
  41. 8
      typescript/sdk/src/core/HyperlaneCore.ts
  42. 16
      typescript/sdk/src/core/HyperlaneCoreDeployer.ts
  43. 6
      typescript/sdk/src/core/MultiProtocolCore.ts
  44. 14
      typescript/sdk/src/core/TestRecipientDeployer.ts
  45. 51
      typescript/sdk/src/deploy/HyperlaneDeployer.ts
  46. 4
      typescript/sdk/src/deploy/HyperlaneProxyFactoryDeployer.ts
  47. 50
      typescript/sdk/src/deploy/verify/ContractVerifier.ts
  48. 10
      typescript/sdk/src/deploy/verify/PostDeploymentContractVerifier.ts
  49. 2
      typescript/sdk/src/gas/HyperlaneIgpChecker.ts
  50. 16
      typescript/sdk/src/gas/HyperlaneIgpDeployer.ts
  51. 4
      typescript/sdk/src/gas/token-prices.ts
  52. 28
      typescript/sdk/src/hook/HyperlaneHookDeployer.ts
  53. 11
      typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts
  54. 36
      typescript/sdk/src/ism/HyperlaneIsmFactory.ts
  55. 12
      typescript/sdk/src/ism/utils.ts
  56. 5
      typescript/sdk/src/logger.ts
  57. 14
      typescript/sdk/src/metadata/ChainMetadataManager.ts
  58. 46
      typescript/sdk/src/metadata/health.ts
  59. 16
      typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerRouterDeployer.ts
  60. 15
      typescript/sdk/src/providers/MultiProtocolProvider.ts
  61. 18
      typescript/sdk/src/providers/MultiProvider.ts
  62. 11
      typescript/sdk/src/providers/SmartProvider/HyperlaneEtherscanProvider.ts
  63. 24
      typescript/sdk/src/providers/SmartProvider/HyperlaneJsonRpcProvider.ts
  64. 21
      typescript/sdk/src/providers/SmartProvider/SmartProvider.ts
  65. 4
      typescript/sdk/src/router/GasRouterDeployer.ts
  66. 8
      typescript/sdk/src/router/HyperlaneRouterDeployer.ts
  67. 6
      typescript/sdk/src/router/ProxiedRouterDeployer.ts
  68. 4
      typescript/sdk/src/router/RouterApps.ts
  69. 1
      typescript/sdk/src/test/metadata-check.ts
  70. 9
      typescript/sdk/src/token/deploy.ts
  71. 39
      typescript/sdk/src/warp/WarpCore.ts
  72. 4
      typescript/utils/index.ts
  73. 3
      typescript/utils/package.json
  74. 6
      typescript/utils/src/base64.ts
  75. 7
      typescript/utils/src/env.ts
  76. 75
      typescript/utils/src/logging.ts
  77. 121
      yarn.lock

@ -0,0 +1,9 @@
---
'@hyperlane-xyz/helloworld': minor
'@hyperlane-xyz/infra': minor
'@hyperlane-xyz/utils': minor
'@hyperlane-xyz/cli': minor
'@hyperlane-xyz/sdk': minor
---
Replace Debug logger with Pino

@ -87,7 +87,7 @@ set -e
echo "{}" > /tmp/empty-artifacts.json
export DEBUG=hyperlane:*
export LOG_LEVEL=DEBUG
DEPLOYER=$(cast rpc eth_accounts | jq -r '.[0]')
BEFORE=$(cast balance $DEPLOYER --rpc-url http://127.0.0.1:${CHAIN1_PORT})

@ -2,12 +2,13 @@
import chalk from 'chalk';
import yargs from 'yargs';
import { errorRed } from './logger.js';
import './env.js';
import { chainsCommand } from './src/commands/chains.js';
import { configCommand } from './src/commands/config.js';
import { deployCommand } from './src/commands/deploy.js';
import { sendCommand } from './src/commands/send.js';
import { statusCommand } from './src/commands/status.js';
import { errorRed } from './src/logger.js';
import { checkVersion } from './src/utils/version-check.js';
import { VERSION } from './src/version.js';

@ -0,0 +1,18 @@
// This file isn't in the src dir so it it's imported before others
// See https://github.com/trivago/prettier-plugin-sort-imports/issues/112
// Default rootLogger into pretty mode
process.env.LOG_PRETTY ??= 'true';
// Workaround for bug in bigint-buffer which solana-web3.js depends on
// https://github.com/no2chem/bigint-buffer/issues/31#issuecomment-1752134062
const defaultWarn = console.warn;
console.warn = (...args) => {
if (
args &&
typeof args[0] === 'string' &&
args[0]?.includes('bigint: Failed to load bindings')
)
return;
defaultWarn(...args);
};

@ -1,58 +0,0 @@
// This file isn't in the src dir so it it's imported before others after import sort
// See bigint hack below and https://github.com/trivago/prettier-plugin-sort-imports/issues/112
import chalk from 'chalk';
import debug from 'debug';
// Workaround for bug in bigint-buffer which solana-web3.js depends on
// https://github.com/no2chem/bigint-buffer/issues/31#issuecomment-1752134062
const defaultWarn = console.warn;
console.warn = (...args: any) => {
if (
args &&
typeof args[0] === 'string' &&
args[0]?.includes('bigint: Failed to load bindings')
)
return;
defaultWarn(...args);
};
const HYPERLANE_NS = 'hyperlane';
// Default root logger for use in utils/scripts
export const logger = debug(HYPERLANE_NS);
export const error = debug(`${HYPERLANE_NS}:ERROR`);
export function createLogger(namespace: string, isError = false) {
return isError ? error.extend(namespace) : logger.extend(namespace);
}
// Ensure hyperlane logging is enabled by forcing inclusion of hyperlane namespace
const activeNamespaces = debug.disable();
const otherNamespaces = activeNamespaces
.split(',')
.filter((ns) => ns.includes(HYPERLANE_NS));
const hypNamespaces = `${HYPERLANE_NS},${HYPERLANE_NS}:*`;
debug.enable(
otherNamespaces ? `${otherNamespaces},${hypNamespaces}` : `${hypNamespaces}`,
);
// Change Debug's output format to remove prefixes + postfixes
function formatArgs(this: debug.Debugger, args: any[]) {
args.push(debug.humanize(this.diff));
args.pop();
}
debug.formatArgs = formatArgs;
// Colored logs directly to console
export const logBlue = (...args: any) => console.log(chalk.blue(...args));
export const logPink = (...args: any) =>
console.log(chalk.magentaBright(...args));
export const logGray = (...args: any) => console.log(chalk.gray(...args));
export const logGreen = (...args: any) => console.log(chalk.green(...args));
export const logRed = (...args: any) => console.log(chalk.red(...args));
export const logBoldUnderlinedRed = (...args: any) =>
console.log(chalk.red.bold.underline(...args));
export const logTip = (...args: any) => console.log(chalk.bgYellow(...args));
export const errorRed = (...args: any) => console.error(chalk.red(...args));
export const log = (...args: any) => console.log(...args);
export const logTable = (...args: any) => console.table(...args);

@ -9,7 +9,7 @@ import {
hyperlaneContractAddresses,
} from '@hyperlane-xyz/sdk';
import { log, logBlue, logGray, logTable } from '../../logger.js';
import { log, logBlue, logGray, logTable } from '../logger.js';
/**
* Parent command

@ -1,6 +1,5 @@
import { CommandModule } from 'yargs';
import { log, logGreen } from '../../logger.js';
import { createChainConfig, readChainConfigs } from '../config/chain.js';
import { createHooksConfigMap } from '../config/hooks.js';
import { createIsmConfigMap, readIsmConfig } from '../config/ism.js';
@ -12,6 +11,7 @@ import {
createWarpRouteDeployConfig,
readWarpRouteDeployConfig,
} from '../config/warp.js';
import { log, logGreen } from '../logger.js';
import { FileFormat } from '../utils/files.js';
import {

@ -1,9 +1,9 @@
import { CommandModule } from 'yargs';
import { log, logGray } from '../../logger.js';
import { runKurtosisAgentDeploy } from '../deploy/agent.js';
import { runCoreDeploy } from '../deploy/core.js';
import { runWarpRouteDeploy } from '../deploy/warp.js';
import { log, logGray } from '../logger.js';
import { ENV } from '../utils/env.js';
import {

@ -1,7 +1,7 @@
import { ethers } from 'ethers';
import { CommandModule, Options } from 'yargs';
import { log } from '../../logger.js';
import { log } from '../logger.js';
import { sendTestMessage } from '../send/message.js';
import { sendTestTransfer } from '../send/transfer.js';
import { ENV } from '../utils/env.js';

@ -3,7 +3,7 @@ import { ZodTypeAny, z } from 'zod';
import { ChainName, HyperlaneContractsMap } from '@hyperlane-xyz/sdk';
import { log, logBlue } from '../../logger.js';
import { log, logBlue } from '../logger.js';
import { readYamlOrJson, runFileSelectionStep } from '../utils/files.js';
const RecursiveObjectSchema: ZodTypeAny = z.lazy(() =>

@ -8,8 +8,8 @@ import {
} from '@hyperlane-xyz/sdk';
import { ProtocolType, objMerge } from '@hyperlane-xyz/utils';
import { errorRed, log, logBlue, logGreen } from '../../logger.js';
import { getMultiProvider } from '../context.js';
import { errorRed, log, logBlue, logGreen } from '../logger.js';
import {
FileFormat,
isFile,

@ -18,7 +18,7 @@ import {
toWei,
} from '@hyperlane-xyz/utils';
import { errorRed, log, logBlue, logGreen, logRed } from '../../logger.js';
import { errorRed, log, logBlue, logGreen, logRed } from '../logger.js';
import { runMultiChainSelectionStep } from '../utils/chains.js';
import { FileFormat, mergeYamlOrJson, readYamlOrJson } from '../utils/files.js';

@ -10,7 +10,7 @@ import {
logBoldUnderlinedRed,
logGreen,
logRed,
} from '../../logger.js';
} from '../logger.js';
import { runMultiChainSelectionStep } from '../utils/chains.js';
import { FileFormat, mergeYamlOrJson, readYamlOrJson } from '../utils/files.js';

@ -9,8 +9,8 @@ import {
objMap,
} from '@hyperlane-xyz/utils';
import { errorRed, log, logBlue, logGreen } from '../../logger.js';
import { sdkContractAddressesMap } from '../context.js';
import { errorRed, log, logBlue, logGreen } from '../logger.js';
import { runMultiChainSelectionStep } from '../utils/chains.js';
import { FileFormat, mergeYamlOrJson, readYamlOrJson } from '../utils/files.js';

@ -4,7 +4,7 @@ import { z } from 'zod';
import { TokenType, ZHash } from '@hyperlane-xyz/sdk';
import { errorRed, logBlue, logGreen } from '../../logger.js';
import { errorRed, logBlue, logGreen } from '../logger.js';
import {
runMultiChainSelectionStep,
runSingleChainSelectionStep,

@ -2,8 +2,8 @@ import terminalLink from 'terminal-link';
import { toBase64 } from '@hyperlane-xyz/utils';
import { logBlue, logGreen } from '../../logger.js';
import { getContext } from '../context.js';
import { logBlue, logGreen } from '../logger.js';
import {
runMultiChainSelectionStep,
runSingleChainSelectionStep,

@ -29,14 +29,6 @@ import {
} from '@hyperlane-xyz/sdk';
import { Address, objFilter, objMerge } from '@hyperlane-xyz/utils';
import {
log,
logBlue,
logBoldUnderlinedRed,
logGray,
logGreen,
logRed,
} from '../../logger.js';
import { runDeploymentArtifactStep } from '../config/artifacts.js';
import { presetHookConfigs, readHooksConfigMap } from '../config/hooks.js';
import { readIsmConfig } from '../config/ism.js';
@ -47,6 +39,14 @@ import {
getMergedContractAddresses,
sdkContractAddressesMap,
} from '../context.js';
import {
log,
logBlue,
logBoldUnderlinedRed,
logGray,
logGreen,
logRed,
} from '../logger.js';
import { runMultiChainSelectionStep } from '../utils/chains.js';
import {
prepNewArtifactsFiles,

@ -9,8 +9,8 @@ import {
} from '@hyperlane-xyz/sdk';
import { ProtocolType } from '@hyperlane-xyz/utils';
import { log, logGreen } from '../../logger.js';
import { parseIsmConfig } from '../config/ism.js';
import { log, logGreen } from '../logger.js';
import { assertGasBalances } from '../utils/balances.js';
import { assertSigner } from '../utils/keys.js';

@ -22,13 +22,13 @@ import {
} from '@hyperlane-xyz/sdk';
import { Address, ProtocolType, objMap } from '@hyperlane-xyz/utils';
import { log, logBlue, logGray, logGreen } from '../../logger.js';
import {
WarpRouteDeployConfig,
readWarpRouteDeployConfig,
} from '../config/warp.js';
import { MINIMUM_WARP_DEPLOY_GAS } from '../consts.js';
import { getContext, getMergedContractAddresses } from '../context.js';
import { log, logBlue, logGray, logGreen } from '../logger.js';
import {
isFile,
prepNewArtifactsFiles,

@ -0,0 +1,36 @@
import chalk, { ChalkInstance } from 'chalk';
import { pino } from 'pino';
import { isLogPretty, rootLogger } from '@hyperlane-xyz/utils';
export const logger = rootLogger.child({ module: 'cli' });
export const log = (msg: string, ...args: any) => logger.info(msg, ...args);
export function logColor(
level: pino.Level,
chalkInstance: ChalkInstance,
...args: any
) {
// Only use color when pretty is enabled
if (isLogPretty) {
logger[level](chalkInstance(...args));
} else {
// @ts-ignore pino type more restrictive than pino's actual arg handling
logger[level](...args);
}
}
export const logBlue = (...args: any) => logColor('info', chalk.blue, ...args);
export const logPink = (...args: any) =>
logColor('info', chalk.magentaBright, ...args);
export const logGray = (...args: any) => logColor('info', chalk.gray, ...args);
export const logGreen = (...args: any) =>
logColor('info', chalk.green, ...args);
export const logRed = (...args: any) => logColor('info', chalk.red, ...args);
export const logBoldUnderlinedRed = (...args: any) =>
logColor('info', chalk.red.bold.underline, ...args);
export const logTip = (...args: any) =>
logColor('info', chalk.bgYellow, ...args);
export const errorRed = (...args: any) => logColor('error', chalk.red, ...args);
// No support for table in pino so print directly to console
export const logTable = (...args: any) => console.table(...args);

@ -8,10 +8,10 @@ import {
} from '@hyperlane-xyz/sdk';
import { addressToBytes32, timeout } from '@hyperlane-xyz/utils';
import { errorRed, log, logBlue, logGreen } from '../../logger.js';
import { MINIMUM_TEST_SEND_GAS } from '../consts.js';
import { getContext, getMergedContractAddresses } from '../context.js';
import { runPreflightChecks } from '../deploy/utils.js';
import { errorRed, log, logBlue, logGreen } from '../logger.js';
import { runSingleChainSelectionStep } from '../utils/chains.js';
export async function sendTestMessage({

@ -14,10 +14,10 @@ import {
} from '@hyperlane-xyz/sdk';
import { Address, timeout } from '@hyperlane-xyz/utils';
import { logBlue, logGreen, logRed } from '../../logger.js';
import { MINIMUM_TEST_SEND_GAS } from '../consts.js';
import { getContext, getMergedContractAddresses } from '../context.js';
import { runPreflightChecks } from '../deploy/utils.js';
import { logBlue, logGreen, logRed } from '../logger.js';
import { runSingleChainSelectionStep } from '../utils/chains.js';
export async function sendTestTransfer({

@ -2,8 +2,8 @@ import { input } from '@inquirer/prompts';
import { ChainName, HyperlaneCore } from '@hyperlane-xyz/sdk';
import { log, logBlue, logGreen } from '../../logger.js';
import { getContext, getMergedContractAddresses } from '../context.js';
import { log, logBlue, logGreen } from '../logger.js';
import { runSingleChainSelectionStep } from '../utils/chains.js';
export async function checkMessageStatus({

@ -9,7 +9,7 @@ import {
testnetChainsMetadata,
} from '@hyperlane-xyz/sdk';
import { log, logBlue, logRed, logTip } from '../../logger.js';
import { log, logBlue, logRed, logTip } from '../logger.js';
// A special value marker to indicate user selected
// a new chain in the list

@ -6,7 +6,7 @@ import { parse as yamlParse, stringify as yamlStringify } from 'yaml';
import { objMerge } from '@hyperlane-xyz/utils';
import { log, logBlue } from '../../logger.js';
import { log, logBlue } from '../logger.js';
import { getTimestampForFilename } from './time.js';

@ -1,6 +1,6 @@
import latestVersion from 'latest-version';
import { log } from '../../logger.js';
import { log } from '../logger.js';
import { VERSION } from '../version.js';
export async function checkVersion() {

@ -6,5 +6,5 @@
"outDir": "./dist/",
"rootDir": "."
},
"include": ["./cli.ts", "./logger.ts", "./src/**/*.ts", "./src/*.d.ts", "./examples/**/*.ts"],
"include": ["./cli.ts", "src/logger.ts", "env.ts", "./src/**/*.ts", "./src/*.d.ts", "./examples/**/*.ts",],
}

@ -9,7 +9,7 @@ import {
MultiProvider,
RouterApp,
} from '@hyperlane-xyz/sdk';
import { Address, debug } from '@hyperlane-xyz/utils';
import { Address, rootLogger } from '@hyperlane-xyz/utils';
import { HelloWorld } from '../types';
@ -23,7 +23,12 @@ export class HelloWorldApp extends RouterApp<HelloWorldFactories> {
multiProvider: MultiProvider,
foreignDeployments: ChainMap<Address> = {},
) {
super(contractsMap, multiProvider, undefined, foreignDeployments);
super(
contractsMap,
multiProvider,
rootLogger.child({ module: 'HelloWorldApp' }),
foreignDeployments,
);
}
router(contracts: HyperlaneContracts<HelloWorldFactories>): HelloWorld {
@ -55,7 +60,7 @@ export class HelloWorldApp extends RouterApp<HelloWorldFactories> {
gasLimit,
value: value.add(quote),
});
debug('Sending hello message', {
this.logger.info('Sending hello message', {
from,
to,
message,

@ -44,7 +44,7 @@ export class SealevelHelloWorldAdapter
value: string,
sender: Address,
): Promise<SolanaWeb3Transaction> {
this.logger(
this.logger.info(
'Creating sendHelloWorld tx for sealevel',
this.chainName,
destination,

@ -1,6 +1,6 @@
import { ethers } from 'ethers';
import { debug, error } from '@hyperlane-xyz/utils';
import { rootLogger } from '@hyperlane-xyz/utils';
import { getSecretRpcEndpoint } from '../src/agents';
@ -16,7 +16,7 @@ async function main() {
const chains = multiProvider.getKnownChainNames();
const providers: [string, ethers.providers.JsonRpcProvider][] = [];
for (const chain of chains) {
debug(`Building providers for ${chain}`);
rootLogger.debug(`Building providers for ${chain}`);
const rpcData = [
...(await getSecretRpcEndpoint(environment, chain, false)),
...(await getSecretRpcEndpoint(environment, chain, true)),
@ -25,11 +25,15 @@ async function main() {
providers.push([chain, new ethers.providers.StaticJsonRpcProvider(url)]);
}
for (const [chain, provider] of providers) {
debug(`Testing provider for ${chain}: ${provider.connection.url}`);
rootLogger.debug(
`Testing provider for ${chain}: ${provider.connection.url}`,
);
try {
await provider.getBlockNumber();
} catch (e) {
error(`Provider failed for ${chain}: ${provider.connection.url}`);
rootLogger.error(
`Provider failed for ${chain}: ${provider.connection.url}`,
);
}
}
}

@ -1,7 +1,7 @@
import { BigNumber } from 'ethers';
import { format } from 'util';
import { error, promiseObjAll } from '@hyperlane-xyz/utils';
import { promiseObjAll, rootLogger } from '@hyperlane-xyz/utils';
import { Contexts } from '../../config/contexts';
import {
@ -13,6 +13,8 @@ import { assertChain } from '../../src/utils/utils';
import { getArgs } from '../agent-utils';
import { getEnvironmentConfig } from '../core-utils';
const logger = rootLogger.child({ module: 'fund-deterministic-key' });
async function main() {
const argv = await getArgs()
.string('role')
@ -74,7 +76,7 @@ async function main() {
const desired = gasPrice.mul(argv.gasAmount!);
const value = desired.sub(actual);
if (value.gt(0)) {
console.log(
logger.info(
`Funding ${address} on chain '${chain}' with ${value} native tokens`,
);
await multiProvider.sendTransaction(chain, {
@ -87,7 +89,7 @@ async function main() {
}
main().catch((err) => {
error('Error occurred in main', {
logger.error('Error occurred in main', {
// JSON.stringifying an Error returns '{}'.
// This is a workaround from https://stackoverflow.com/a/60370781
error: format(err),

@ -11,14 +11,7 @@ import {
MultiProvider,
RpcConsensusType,
} from '@hyperlane-xyz/sdk';
import {
Address,
error,
log,
objFilter,
objMap,
warn,
} from '@hyperlane-xyz/utils';
import { Address, objFilter, objMap, rootLogger } from '@hyperlane-xyz/utils';
import { Contexts } from '../../config/contexts';
import {
@ -53,7 +46,8 @@ import { getEnvironmentConfig } from '../core-utils';
import * as L1ETHGateway from './utils/L1ETHGateway.json';
import * as L1MessageQueue from './utils/L1MessageQueue.json';
import * as L1ScrollMessenger from './utils/L1ScrollMessenger.json';
import * as PolygonZkEVMBridge from './utils/PolygonZkEVMBridge.json';
const logger = rootLogger.child({ module: 'fund-keys' });
const nativeBridges = {
scrollsepolia: {
@ -247,7 +241,7 @@ async function main() {
await submitMetrics(metricsRegister, 'key-funder');
if (failureOccurred) {
error('At least one failure occurred when funding');
logger.error('At least one failure occurred when funding');
process.exit(1);
}
}
@ -276,9 +270,12 @@ class ContextFunder {
isEthereumProtocolChain(chain) &&
multiProvider.tryGetChainName(chain) !== null;
if (!valid) {
warn('Skipping funding for non-blessed or non-Ethereum chain', {
chain,
});
logger.warn(
'Skipping funding for non-blessed or non-Ethereum chain',
{
chain,
},
);
}
return valid;
},
@ -308,7 +305,7 @@ class ContextFunder {
desiredKathyBalancePerChain: KeyFunderConfig['desiredKathyBalancePerChain'],
filePath: string,
) {
log('Reading identifiers and addresses from file', {
logger.info('Reading identifiers and addresses from file', {
filePath,
});
// A big array of KeyAsAddress, including keys that we may not care about.
@ -358,7 +355,7 @@ class ContextFunder {
},
);
log('Successfully read keys for context from file', {
logger.info('Successfully read keys for context from file', {
filePath,
readOnlyKeysPerChain,
context,
@ -467,7 +464,7 @@ class ContextFunder {
const failureOccurred = (await Promise.allSettled(promises)).reduce(
(failureAgg, result, i) => {
if (result.status === 'rejected') {
error('Funding promise for chain rejected', {
logger.error('Funding promise for chain rejected', {
chain: chainKeyEntries[i][0],
error: format(result.reason),
});
@ -487,7 +484,7 @@ class ContextFunder {
): Promise<boolean> {
const provider = this.multiProvider.tryGetProvider(chain);
if (!provider) {
error('Cannot get chain connection', {
logger.error('Cannot get chain connection', {
chain,
});
// Consider this an error, but don't throw and prevent all future funding attempts
@ -500,7 +497,7 @@ class ContextFunder {
try {
await this.fundKeyIfRequired(chain, key, desiredBalance);
} catch (err) {
error('Error funding key', {
logger.error('Error funding key', {
key: await getKeyInfo(
key,
chain,
@ -540,7 +537,7 @@ class ContextFunder {
private async attemptToClaimFromIgp(chain: ChainName) {
const igpClaimThresholdEther = igpClaimThresholdPerChain[chain];
if (!igpClaimThresholdEther) {
warn(`No IGP claim threshold for chain ${chain}`);
logger.warn(`No IGP claim threshold for chain ${chain}`);
return;
}
const igpClaimThreshold = ethers.utils.parseEther(igpClaimThresholdEther);
@ -549,14 +546,14 @@ class ContextFunder {
const igp = this.igp.getContracts(chain).interchainGasPaymaster;
const igpBalance = await provider.getBalance(igp.address);
log('Checking IGP balance', {
logger.info('Checking IGP balance', {
chain,
igpBalance: ethers.utils.formatEther(igpBalance),
igpClaimThreshold: ethers.utils.formatEther(igpClaimThreshold),
});
if (igpBalance.gt(igpClaimThreshold)) {
log('IGP balance exceeds claim threshold, claiming', {
logger.info('IGP balance exceeds claim threshold, claiming', {
chain,
});
await this.multiProvider.sendTransaction(
@ -564,7 +561,7 @@ class ContextFunder {
await igp.populateTransaction.claim(),
);
} else {
log('IGP balance does not exceed claim threshold, skipping', {
logger.info('IGP balance does not exceed claim threshold, skipping', {
chain,
});
}
@ -619,14 +616,14 @@ class ContextFunder {
const funderAddress = await this.multiProvider.getSignerAddress(chain);
if (fundingAmount.eq(0)) {
log('Skipping funding for key', {
logger.info('Skipping funding for key', {
key: keyInfo,
context: this.context,
chain,
});
return;
} else {
log('Funding key', {
logger.info('Funding key', {
chain,
amount: ethers.utils.formatEther(fundingAmount),
key: keyInfo,
@ -644,7 +641,7 @@ class ContextFunder {
to: key.address,
value: fundingAmount,
});
log('Sent transaction', {
logger.info('Sent transaction', {
key: keyInfo,
txUrl: this.multiProvider.tryGetExplorerTxUrl(chain, {
hash: tx.transactionHash,
@ -652,7 +649,7 @@ class ContextFunder {
context: this.context,
chain,
});
log('Got transaction receipt', {
logger.info('Got transaction receipt', {
key: keyInfo,
tx,
context: this.context,
@ -662,7 +659,7 @@ class ContextFunder {
private async bridgeToL2(l2Chain: L2Chain, to: string, amount: BigNumber) {
const l1Chain = L2ToL1[l2Chain];
log('Bridging ETH to L2', {
logger.info('Bridging ETH to L2', {
amount: ethers.utils.formatEther(amount),
l1Funder: await getAddressInfo(
await this.multiProvider.getSignerAddress(l1Chain),
@ -867,7 +864,7 @@ async function gracefullyHandleError(
await fn();
return false;
} catch (err) {
error(errorMessage, {
logger.error(errorMessage, {
chain,
error: format(err),
});
@ -876,7 +873,7 @@ async function gracefullyHandleError(
}
main().catch((err) => {
error('Error occurred in main', {
logger.error('Error occurred in main', {
// JSON.stringifying an Error returns '{}'.
// This is a workaround from https://stackoverflow.com/a/60370781
error: format(err),

@ -17,15 +17,12 @@ import {
import {
Address,
ProtocolType,
debug,
ensure0x,
error,
log,
objMap,
retryAsync,
rootLogger,
strip0x,
timeout,
warn,
} from '@hyperlane-xyz/utils';
import { Contexts } from '../../config/contexts';
@ -45,6 +42,8 @@ import { getEnvironmentConfig } from '../core-utils';
import { getHelloWorldMultiProtocolApp } from './utils';
const logger = rootLogger.child({ module: 'kathy' });
const metricsRegister = new Registry();
// TODO rename counter names
const messagesSendCount = new Counter({
@ -162,7 +161,7 @@ async function main(): Promise<boolean> {
let errorOccurred = false;
startMetricsServer(metricsRegister);
debug('Starting up', { environment });
logger.debug('Starting up', { environment });
const coreConfig = getEnvironmentConfig(environment);
// const coreConfig = getCoreConfigStub(environment);
@ -202,7 +201,7 @@ async function main(): Promise<boolean> {
.filter((v) => v !== null)
.map((v) => v!);
debug('Pairings calculated', { chains, pairings });
logger.debug('Pairings calculated', { chains, pairings });
let allowedToSend: number;
let currentPairingIndex: number;
@ -214,7 +213,7 @@ async function main(): Promise<boolean> {
// Start with pairing 0
currentPairingIndex = 0;
debug('Cycling once through all pairs');
logger.debug('Cycling once through all pairs');
} else {
// If we are not cycling just once and are running this as a service, do so at an interval.
// Track how many we are still allowed to send in case some messages send slower than expected.
@ -223,14 +222,14 @@ async function main(): Promise<boolean> {
// in case we are restarting kathy, keep it from always running the exact same messages first
currentPairingIndex = Date.now() % pairings.length;
debug('Running as a service', {
logger.debug('Running as a service', {
sendFrequency,
});
setInterval(() => {
// bucket cap since if we are getting really behind it probably does not make sense to let it run away.
allowedToSend = Math.min(allowedToSend + 1, MAX_MESSAGES_ALLOWED_TO_SEND);
debug('Tick; allowed to send another message', {
logger.debug('Tick; allowed to send another message', {
allowedToSend,
sendFrequency,
});
@ -265,7 +264,7 @@ async function main(): Promise<boolean> {
await app.stats(),
)) {
for (const [destination, counts] of Object.entries(destinationStats)) {
debug('Message stats', {
logger.debug('Message stats', {
origin,
destination,
currentCycle,
@ -278,7 +277,7 @@ async function main(): Promise<boolean> {
cycleMessageCount = 0;
if (cycleOnce) {
log('Finished cycling through all pairs once');
logger.info('Finished cycling through all pairs once');
// Return true to signify messages should stop being sent.
return true;
}
@ -310,7 +309,7 @@ async function main(): Promise<boolean> {
(origin === 'ethereum' || destination === 'ethereum') &&
currentCycle % (cyclesBetweenEthereumMessages + 1) !== 0
) {
debug('Skipping message to/from Ethereum', {
logger.debug('Skipping message to/from Ethereum', {
currentCycle,
origin,
destination,
@ -334,7 +333,7 @@ async function main(): Promise<boolean> {
// In the cycle-once case, the loop is expected to exit before ever hitting
// this condition.
if (allowedToSend <= 0) {
debug('Waiting before sending next message', {
logger.debug('Waiting before sending next message', {
...logCtx,
sendFrequency,
});
@ -342,7 +341,7 @@ async function main(): Promise<boolean> {
}
allowedToSend--;
debug('Initiating sending of new message', logCtx);
logger.debug('Initiating sending of new message', logCtx);
try {
await sendMessage(
@ -356,10 +355,10 @@ async function main(): Promise<boolean> {
messageSendTimeout,
messageReceiptTimeout,
);
log('Message sent successfully', { origin, destination });
logger.info('Message sent successfully', { origin, destination });
messagesSendCount.labels({ ...labels, status: 'success' }).inc();
} catch (e) {
error(`Error sending message, continuing...`, {
logger.error(`Error sending message, continuing...`, {
error: format(e),
...logCtx,
});
@ -371,7 +370,7 @@ async function main(): Promise<boolean> {
origin,
coreConfig.owners[origin].owner,
).catch((e) => {
warn('Failed to update wallet balance for chain', {
logger.warn('Failed to update wallet balance for chain', {
chain: origin,
err: format(e),
});
@ -419,7 +418,7 @@ async function sendMessage(
const metricLabels = { origin, remote: destination };
log('Sending message', {
logger.info('Sending message', {
origin,
destination,
interchainGasPayment: value,
@ -493,7 +492,7 @@ async function sendMessage(
);
messageSendSeconds.labels(metricLabels).inc((Date.now() - startTime) / 1000);
log('Message sent, waiting for it to be processed', {
logger.info('Message sent, waiting for it to be processed', {
origin,
destination,
receipt,
@ -509,7 +508,7 @@ async function sendMessage(
messageReceiptSeconds
.labels(metricLabels)
.inc((Date.now() - startTime) / 1000);
log('Message received', {
logger.info('Message received', {
origin,
destination,
});
@ -535,7 +534,11 @@ async function updateWalletBalanceMetricFor(
token_symbol: 'Native',
})
.set(balance);
debug('Wallet balance updated for chain', { chain, signerAddress, balance });
logger.debug('Wallet balance updated for chain', {
chain,
signerAddress,
balance,
});
}
// Get a core config intended for testing Kathy without secret access
@ -549,7 +552,7 @@ export function getCoreConfigStub(environment: DeployEnvironment) {
const privateKeyEvm = process.env.KATHY_PRIVATE_KEY_EVM;
if (!privateKeyEvm) throw new Error('KATHY_PRIVATE_KEY_EVM env var not set');
const evmSigner = new Wallet(privateKeyEvm);
console.log('evmSigner address', evmSigner.address);
logger.info('evmSigner address', evmSigner.address);
multiProvider.setSharedSigner(evmSigner);
// const privateKeySealevel = process.env.KATHY_PRIVATE_KEY_SEALEVEL;
@ -559,7 +562,7 @@ export function getCoreConfigStub(environment: DeployEnvironment) {
// const sealevelSigner = Keypair.fromSeed(
// Buffer.from(privateKeySealevel, 'hex'),
// );
// console.log('sealevelSigner address', sealevelSigner.publicKey.toBase58());
// console.logger.info('sealevelSigner address', sealevelSigner.publicKey.toBase58());
const testnetKeys = objMap(testnetConfigs, (_, __) => ({
address: evmSigner.address,
@ -580,15 +583,15 @@ export function getCoreConfigStub(environment: DeployEnvironment) {
main()
.then((errorOccurred: boolean) => {
log('Main exited');
logger.info('Main exited');
if (errorOccurred) {
error('An error occurred at some point');
logger.error('An error occurred at some point');
process.exit(1);
} else {
process.exit(0);
}
})
.catch((e) => {
error('Error in main', { error: format(e) });
logger.error('Error in main', { error: format(e) });
process.exit(1);
});

@ -5,13 +5,15 @@ import {
attachContractsMap,
liquidityLayerFactories,
} from '@hyperlane-xyz/sdk';
import { error, log } from '@hyperlane-xyz/utils';
import { rootLogger } from '@hyperlane-xyz/utils';
import { bridgeAdapterConfigs } from '../../config/environments/testnet4/token-bridge';
import { readJSON, sleep } from '../../src/utils/utils';
import { getArgs, getEnvironmentDirectory } from '../agent-utils';
import { getEnvironmentConfig } from '../core-utils';
const logger = rootLogger.child({ module: 'portal-relayer' });
async function relayPortalTransfers() {
const { environment } = await getArgs().argv;
const config = getEnvironmentConfig(environment);
@ -32,7 +34,7 @@ async function relayPortalTransfers() {
const tick = async () => {
for (const chain of Object.keys(bridgeAdapterConfigs)) {
log('Processing chain', {
logger.info('Processing chain', {
chain,
});
@ -43,7 +45,7 @@ async function relayPortalTransfers() {
)
).flat();
log('Portal messages', {
logger.info('Portal messages', {
portalMessages,
});
@ -52,7 +54,7 @@ async function relayPortalTransfers() {
try {
await app.attemptPortalTransferCompletion(message);
} catch (err) {
error('Error attempting portal transfer', {
logger.error('Error attempting portal transfer', {
message,
err,
});
@ -66,7 +68,7 @@ async function relayPortalTransfers() {
try {
await tick();
} catch (err) {
error('Error processing chains in tick', {
logger.error('Error processing chains in tick', {
err,
});
}

@ -17,14 +17,16 @@ import {
} from '@hyperlane-xyz/sdk';
import {
ProtocolType,
debug,
objMap,
promiseObjAll,
rootLogger,
} from '@hyperlane-xyz/utils';
import { startMetricsServer } from '../../src/utils/metrics';
import { readYaml } from '../../src/utils/utils';
const logger = rootLogger.child({ module: 'warp-balance-monitor' });
const metricsRegister = new Registry();
const warpRouteTokenBalance = new Gauge({
name: 'hyperlane_warp_route_token_balance',
@ -72,16 +74,16 @@ async function main(): Promise<boolean> {
startMetricsServer(metricsRegister);
console.log('Starting Warp Route balance monitor');
logger.info('Starting Warp Route balance monitor');
const multiProtocolProvider = new MultiProtocolProvider();
setInterval(async () => {
try {
debug('Checking balances');
logger.debug('Checking balances');
const balances = await checkBalance(tokenConfig, multiProtocolProvider);
updateTokenBalanceMetrics(tokenConfig, balances);
} catch (e) {
console.error('Error checking balances', e);
logger.error('Error checking balances', e);
}
}, checkFrequency);
return true;
@ -229,7 +231,7 @@ export function updateTokenBalanceMetrics(
token_type: token.type,
})
.set(balances[chain]);
debug('Wallet balance updated for chain', {
logger.debug('Wallet balance updated for chain', {
chain,
token: token.name,
balance: balances[chain],
@ -237,4 +239,4 @@ export function updateTokenBalanceMetrics(
});
}
main().then(console.log).catch(console.error);
main().then(logger.info).catch(logger.error);

@ -2,14 +2,16 @@ import http from 'http';
import { Pushgateway, Registry } from 'prom-client';
import { format } from 'util';
import { error, log } from '@hyperlane-xyz/utils';
import { rootLogger } from '@hyperlane-xyz/utils';
const logger = rootLogger.child({ module: 'metrics' });
function getPushGateway(register: Registry): Pushgateway | null {
const gatewayAddr = process.env['PROMETHEUS_PUSH_GATEWAY'];
if (gatewayAddr) {
return new Pushgateway(gatewayAddr, [], register);
} else {
console.warn(
logger.warn(
'Prometheus push gateway address was not defined; not publishing metrics.',
);
return null;
@ -32,7 +34,7 @@ export async function submitMetrics(
resp = (await gateway.pushAdd({ jobName })).resp;
}
} catch (e) {
error('Error when pushing metrics', { error: format(e) });
logger.error('Error when pushing metrics', { error: format(e) });
return;
}
@ -40,7 +42,7 @@ export async function submitMetrics(
typeof resp == 'object' && resp != null && 'statusCode' in resp
? (resp as any).statusCode
: 'unknown';
log('Prometheus metrics pushed to PushGateway', { statusCode });
logger.info('Prometheus metrics pushed to PushGateway', { statusCode });
}
/**
@ -60,7 +62,7 @@ export function startMetricsServer(register: Registry): http.Server {
.then((metricsStr) => {
res.writeHead(200, { ContentType: 'text/plain' }).end(metricsStr);
})
.catch((err) => console.error(err));
.catch((err) => logger.error(err));
})
.listen(parseInt(process.env['PROMETHEUS_PORT'] || '9090'));
}

@ -2,7 +2,7 @@
The Hyperlane SDK helps developers create and manage interchain applications.
For details on how to use the various abstractions and utilities, [see the documentation](https://docs.hyperlane.xyz/docs/sdks/building-applications)
For more details on Hyperlane concepts, [see the documentation](https://docs.hyperlane.xyz)
## Install
@ -20,9 +20,15 @@ yarn add @hyperlane-xyz/sdk
The names and relevant metadata for all Hyperlane-supported chains are included in this SDK, including public RPC and Explorer urls. It also includes the addresses for all Hyperlane core contracts and middleware.
### Deployment, testing, and development classes
### Classes for development, deployment, and testing
Classes for deploying, testing, and building applications using Hyperlane are included in the SDK. See [the docs](https://docs.hyperlane.xyz/docs/sdks/building-applications/nodejs-sdk) for details.
The SDK includes various classes for building, deploying, and testing multi-chain applications. Different abstractions serve different use cases. A few common utilities include:
- `MultiProvider` / `MultiProtocolProvider`: A utility for managing chain metadata, and RPC providers.
- `HyperlaneApp` / `MultiProtocolApp`: A base to extend for a multi-chain app.
- `HyperlaneCore` / `MultiProtocolCore`: A class for common interactions with Hyperlane core deployments.
- `HyperlaneDeployer`: The base class for executing multi-chain contract deployments.
- `Token` & `WarpCore`: Utilities for interacting with Warp Route deployments.
### Chain Logos

@ -10,14 +10,13 @@
"@solana/spl-token": "^0.3.8",
"@solana/web3.js": "^1.78.0",
"@types/coingecko-api": "^1.0.10",
"@types/debug": "^4.1.7",
"@wagmi/chains": "^1.8.0",
"bignumber.js": "^9.1.1",
"coingecko-api": "^1.0.10",
"cosmjs-types": "^0.9.0",
"cross-fetch": "^3.1.5",
"debug": "^4.3.4",
"ethers": "^5.7.2",
"pino": "^8.19.0",
"viem": "^1.20.0",
"zod": "^3.21.2"
},

@ -1,6 +1,4 @@
import debug from 'debug';
import { objMap } from '@hyperlane-xyz/utils';
import { objMap, rootLogger } from '@hyperlane-xyz/utils';
import { connectContracts, serializeContracts } from '../contracts/contracts';
import {
@ -21,7 +19,7 @@ export class HyperlaneApp<
constructor(
contractsMap: HyperlaneContractsMap<Factories>,
public readonly multiProvider: MultiProvider,
public readonly logger = debug('hyperlane:App'),
public readonly logger = rootLogger.child({ module: 'App' }),
) {
const connectedContractsMap = objMap(contractsMap, (chain, contracts) =>
connectContracts(contracts, multiProvider.getSignerOrProvider(chain)),

@ -1,11 +1,11 @@
import { PublicKey } from '@solana/web3.js';
import debug from 'debug';
import {
Address,
ProtocolType,
objMap,
promiseObjAll,
rootLogger,
symmetricDifference,
} from '@hyperlane-xyz/utils';
@ -33,7 +33,7 @@ export abstract class BaseAppAdapter {
public readonly chainName: ChainName,
public readonly multiProvider: MultiProtocolProvider<any>,
public readonly addresses: Record<string, Address>,
public readonly logger = debug(`hyperlane:AppAdapter`),
public readonly logger = rootLogger.child({ module: `AppAdapter` }),
) {}
}
@ -119,7 +119,7 @@ export abstract class MultiProtocolApp<
constructor(
public readonly multiProvider: MultiProtocolProvider,
public readonly addresses: ChainMap<ContractAddrs>,
public readonly logger = debug('hyperlane:MultiProtocolApp'),
public readonly logger = rootLogger.child({ module: 'MultiProtocolApp' }),
) {
const multiProviderChains = multiProvider.getKnownChainNames();
const addressesChains = Object.keys(addresses);

@ -113,11 +113,11 @@ export class HyperlaneCore extends HyperlaneApp<CoreFactories> {
): Promise<true> {
await pollAsync(
async () => {
this.logger(`Checking if message ${messageId} was processed`);
this.logger.debug(`Checking if message ${messageId} was processed`);
const mailbox = this.contractsMap[destination].mailbox;
const delivered = await mailbox.delivered(messageId);
if (delivered) {
this.logger(`Message ${messageId} was processed`);
this.logger.info(`Message ${messageId} was processed`);
return true;
} else {
throw new Error(`Message ${messageId} not yet processed`);
@ -153,7 +153,9 @@ export class HyperlaneCore extends HyperlaneApp<CoreFactories> {
),
),
);
this.logger(`All messages processed for tx ${sourceTx.transactionHash}`);
this.logger.info(
`All messages processed for tx ${sourceTx.transactionHash}`,
);
}
// Redundant with static method but keeping for backwards compatibility

@ -1,12 +1,10 @@
import debug from 'debug';
import {
IPostDispatchHook,
Mailbox,
TestRecipient,
ValidatorAnnounce,
} from '@hyperlane-xyz/core';
import { Address } from '@hyperlane-xyz/utils';
import { Address, rootLogger } from '@hyperlane-xyz/utils';
import { HyperlaneContracts } from '../contracts/types';
import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer';
@ -36,7 +34,7 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer<
contractVerifier?: ContractVerifier,
) {
super(multiProvider, coreFactories, {
logger: debug('hyperlane:CoreDeployer'),
logger: rootLogger.child({ module: 'CoreDeployer' }),
chainTimeoutMs: 1000 * 60 * 10, // 10 minutes
ismFactory,
contractVerifier,
@ -80,7 +78,7 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer<
this.ismFactory.getContracts(chain),
);
if (!matches) {
this.logger('Deploying default ISM');
this.logger.debug('Deploying default ISM');
defaultIsm = await this.deployIsm(
chain,
config.defaultIsm,
@ -91,14 +89,14 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer<
const hookAddresses = { mailbox: mailbox.address, proxyAdmin };
this.logger('Deploying default hook');
this.logger.debug('Deploying default hook');
const defaultHook = await this.deployHook(
chain,
config.defaultHook,
hookAddresses,
);
this.logger('Deploying required hook');
this.logger.debug('Deploying required hook');
const requiredHook = await this.deployHook(
chain,
config.requiredHook,
@ -107,7 +105,7 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer<
// configure mailbox
try {
this.logger('Initializing mailbox');
this.logger.debug('Initializing mailbox');
await this.multiProvider.handleTx(
chain,
mailbox.initialize(
@ -130,7 +128,7 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer<
throw e;
}
this.logger('Mailbox already initialized');
this.logger.debug('Mailbox already initialized');
const overrides = this.multiProvider.getTransactionOverrides(chain);
await this.configureHook(

@ -1,6 +1,4 @@
import debug from 'debug';
import { HexString, ProtocolType } from '@hyperlane-xyz/utils';
import { HexString, ProtocolType, rootLogger } from '@hyperlane-xyz/utils';
import { AdapterClassType, MultiProtocolApp } from '../app/MultiProtocolApp';
import {
@ -24,7 +22,7 @@ export class MultiProtocolCore extends MultiProtocolApp<
constructor(
public readonly multiProvider: MultiProtocolProvider,
public readonly addresses: ChainMap<CoreAddresses>,
public readonly logger = debug('hyperlane:MultiProtocolCore'),
public readonly logger = rootLogger.child({ module: 'MultiProtocolCore' }),
) {
super(multiProvider, addresses, logger);
}

@ -1,7 +1,5 @@
import debug from 'debug';
import { TestRecipient, TestRecipient__factory } from '@hyperlane-xyz/core';
import { Address } from '@hyperlane-xyz/utils';
import { Address, rootLogger } from '@hyperlane-xyz/utils';
import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer';
import { ContractVerifier } from '../deploy/verify/ContractVerifier';
@ -35,7 +33,7 @@ export class TestRecipientDeployer extends HyperlaneDeployer<
contractVerifier?: ContractVerifier,
) {
super(multiProvider, testRecipientFactories, {
logger: debug('hyperlane:TestRecipientDeployer'),
logger: rootLogger.child({ module: 'TestRecipientDeployer' }),
contractVerifier,
});
}
@ -44,10 +42,10 @@ export class TestRecipientDeployer extends HyperlaneDeployer<
chain: ChainName,
config: TestRecipientConfig,
): Promise<TestRecipientContracts> {
this.logger(`Deploying TestRecipient on ${chain}`, config);
this.logger.debug(`Deploying TestRecipient on ${chain}`, config);
const testRecipient = await this.deployContract(chain, 'testRecipient', []);
if (config.interchainSecurityModule) {
this.logger(`Checking TestRecipient ISM on ${chain}`);
this.logger.debug(`Checking TestRecipient ISM on ${chain}`);
await this.configureIsm(
chain,
testRecipient,
@ -56,9 +54,7 @@ export class TestRecipientDeployer extends HyperlaneDeployer<
(tr, ism) => tr.populateTransaction.setInterchainSecurityModule(ism),
);
} else {
this.logger(
`WARNING: No ISM config provided for TestRecipient on ${chain}`,
);
this.logger.warn(`No ISM config provided for TestRecipient on ${chain}`);
}
return {
testRecipient,

@ -1,5 +1,5 @@
import { Debugger, debug } from 'debug';
import { Contract, PopulatedTransaction, ethers } from 'ethers';
import { Logger } from 'pino';
import {
IPostDispatchHook,
@ -18,6 +18,7 @@ import {
Address,
ProtocolType,
eqAddress,
rootLogger,
runWithTimeout,
} from '@hyperlane-xyz/utils';
@ -50,7 +51,7 @@ import {
} from './verify/utils';
export interface DeployerOptions {
logger?: Debugger;
logger?: Logger;
chainTimeoutMs?: number;
ismFactory?: HyperlaneIsmFactory;
contractVerifier?: ContractVerifier;
@ -65,7 +66,7 @@ export abstract class HyperlaneDeployer<
public deployedContracts: HyperlaneContractsMap<Factories> = {};
public startingBlockNumbers: ChainMap<number | undefined> = {};
protected logger: Debugger;
protected logger: Logger;
protected chainTimeoutMs: number;
constructor(
@ -74,7 +75,7 @@ export abstract class HyperlaneDeployer<
protected readonly options: DeployerOptions = {},
protected readonly recoverVerificationInputs = false,
) {
this.logger = options?.logger ?? debug('hyperlane:deployer');
this.logger = options?.logger ?? rootLogger.child({ module: 'deployer' });
this.chainTimeoutMs = options?.chainTimeoutMs ?? 5 * 60 * 1000; // 5 minute timeout per chain
this.options.ismFactory?.setDeployer(this);
@ -111,14 +112,14 @@ export abstract class HyperlaneDeployer<
true,
).intersection;
this.logger(`Start deploy to ${targetChains}`);
this.logger.debug(`Start deploy to ${targetChains}`);
for (const chain of targetChains) {
const signerUrl = await this.multiProvider.tryGetExplorerAddressUrl(
chain,
);
const signerAddress = await this.multiProvider.getSignerAddress(chain);
const fromString = signerUrl || signerAddress;
this.logger(`Deploying to ${chain} from ${fromString}`);
this.logger.debug(`Deploying to ${chain} from ${fromString}`);
this.startingBlockNumbers[chain] = await this.multiProvider
.getProvider(chain)
.getBlockNumber();
@ -165,7 +166,9 @@ export abstract class HyperlaneDeployer<
if (eqAddress(address, signer)) {
return fn();
} else {
this.logger(`Signer (${signer}) does not match ${label} (${address})`);
this.logger.debug(
`Signer (${signer}) does not match ${label} (${address})`,
);
}
return undefined;
}
@ -191,13 +194,13 @@ export abstract class HyperlaneDeployer<
const code = await this.multiProvider.getProvider(chain).getCode(admin);
// if admin is a ProxyAdmin, run the proxyAdminOwnerFn (if deployer is owner)
if (code !== '0x') {
this.logger(`Admin is a ProxyAdmin (${admin})`);
this.logger.debug(`Admin is a ProxyAdmin (${admin})`);
const proxyAdmin = ProxyAdmin__factory.connect(admin, proxy.signer);
return this.runIfOwner(chain, proxyAdmin, () =>
proxyAdminOwnerFn(proxyAdmin),
);
} else {
this.logger(`Admin is an EOA (${admin})`);
this.logger.debug(`Admin is an EOA (${admin})`);
// if admin is an EOA, run the signerAdminFn (if deployer is admin)
return this.runIf(chain, admin, () => signerAdminFn(), 'admin');
}
@ -238,7 +241,7 @@ export abstract class HyperlaneDeployer<
}
if (!matches) {
await this.runIfOwner(chain, contract, async () => {
this.logger(`Set ISM on ${chain} with address ${targetIsm}`);
this.logger.debug(`Set ISM on ${chain} with address ${targetIsm}`);
await this.multiProvider.sendTransaction(
chain,
setIsm(contract, targetIsm),
@ -260,7 +263,7 @@ export abstract class HyperlaneDeployer<
const configuredHook = await getHook(contract);
if (!eqAddress(targetHook.address, configuredHook)) {
const result = await this.runIfOwner(chain, contract, async () => {
this.logger(
this.logger.debug(
`Set hook on ${chain} to ${targetHook.address}, currently is ${configuredHook}`,
);
await this.multiProvider.sendTransaction(
@ -287,7 +290,9 @@ export abstract class HyperlaneDeployer<
client: MailboxClient,
config: MailboxClientConfig,
): Promise<void> {
this.logger(`Initializing mailbox client (if not already) on ${local}...`);
this.logger.debug(
`Initializing mailbox client (if not already) on ${local}...`,
);
if (config.hook) {
await this.configureHook(
local,
@ -312,7 +317,7 @@ export abstract class HyperlaneDeployer<
);
}
this.logger(`Mailbox client on ${local} initialized...`);
this.logger.debug(`Mailbox client on ${local} initialized...`);
}
public async deployContractFromFactory<F extends ethers.ContractFactory>(
@ -340,7 +345,7 @@ export abstract class HyperlaneDeployer<
}
}
this.logger(
this.logger.debug(
`Deploy ${contractName} on ${chain} with constructor args (${constructorArgs.join(
', ',
)})`,
@ -352,7 +357,7 @@ export abstract class HyperlaneDeployer<
);
if (initializeArgs) {
this.logger(`Initialize ${contractName} on ${chain}`);
this.logger.debug(`Initialize ${contractName} on ${chain}`);
const overrides = this.multiProvider.getTransactionOverrides(chain);
const initTx = await contract.initialize(...initializeArgs, overrides);
await this.multiProvider.handleTx(chain, initTx);
@ -373,7 +378,7 @@ export abstract class HyperlaneDeployer<
);
} catch (error) {
// log error but keep deploying, can also verify post-deployment if needed
this.logger(`Error verifying contract: ${error}`);
this.logger.debug(`Error verifying contract: ${error}`);
}
return contract;
@ -410,12 +415,12 @@ export abstract class HyperlaneDeployer<
proxy.address,
);
if (eqAddress(admin, actualAdmin)) {
this.logger(`Admin set correctly, skipping admin change`);
this.logger.debug(`Admin set correctly, skipping admin change`);
return;
}
const txOverrides = this.multiProvider.getTransactionOverrides(chain);
this.logger(`Changing proxy admin`);
this.logger.debug(`Changing proxy admin`);
await this.runIfAdmin(
chain,
proxy,
@ -440,11 +445,11 @@ export abstract class HyperlaneDeployer<
): Promise<void> {
const current = await proxy.callStatic.implementation();
if (eqAddress(implementation.address, current)) {
this.logger(`Implementation set correctly, skipping upgrade`);
this.logger.debug(`Implementation set correctly, skipping upgrade`);
return;
}
this.logger(`Upgrading and initializing implementation`);
this.logger.debug(`Upgrading and initializing implementation`);
const initData = implementation.interface.encodeFunctionData(
'initialize',
initializeArgs,
@ -544,7 +549,7 @@ export abstract class HyperlaneDeployer<
ReturnType<F['deploy']>
>;
if (hit) {
this.logger(
this.logger.debug(
`Recovered ${contractName.toString()} on ${chain} ${cachedAddress}`,
);
return contract;
@ -659,9 +664,9 @@ export abstract class HyperlaneDeployer<
const current = await ownable.owner();
const owner = config.ownerOverrides?.[contractName as K] ?? config.owner;
if (!eqAddress(current, owner)) {
this.logger('Current owner and config owner to not match');
this.logger.debug('Current owner and config owner to not match');
const receipt = await this.runIfOwner(chain, ownable, () => {
this.logger(
this.logger.debug(
`Transferring ownership of ${contractName} to ${owner} on ${chain}`,
);
return this.multiProvider.handleTx(

@ -1,4 +1,4 @@
import debug from 'debug';
import { rootLogger } from '@hyperlane-xyz/utils';
import { HyperlaneContracts } from '../contracts/types';
import { MultiProvider } from '../providers/MultiProvider';
@ -21,7 +21,7 @@ export class HyperlaneProxyFactoryDeployer extends HyperlaneDeployer<
contractVerifier?: ContractVerifier,
) {
super(multiProvider, proxyFactoryFactories, {
logger: debug('hyperlane:IsmFactoryDeployer'),
logger: rootLogger.child({ module: 'IsmFactoryDeployer' }),
contractVerifier,
});
}

@ -1,8 +1,8 @@
import fetch from 'cross-fetch';
import { Debugger, debug } from 'debug';
import { ethers } from 'ethers';
import { Logger } from 'pino';
import { sleep, strip0x } from '@hyperlane-xyz/utils';
import { rootLogger, sleep, strip0x } from '@hyperlane-xyz/utils';
import { ExplorerFamily } from '../../metadata/chainMetadataTypes';
import { MultiProvider } from '../../providers/MultiProvider';
@ -19,9 +19,9 @@ import {
} from './types';
export class ContractVerifier {
private logger = debug(`hyperlane:ContractVerifier`);
protected logger = rootLogger.child({ module: 'ContractVerifier' });
private contractSourceMap: { [contractName: string]: string } = {};
protected contractSourceMap: { [contractName: string]: string } = {};
protected readonly standardInputJson: string;
protected readonly compilerOptions: CompilerOptions;
@ -70,7 +70,7 @@ export class ContractVerifier {
private async submitForm(
chain: ChainName,
action: ExplorerApiActions,
verificationLogger: Debugger,
verificationLogger: Logger,
options?: FormOptions<typeof action>,
): Promise<any> {
const { apiUrl, family } = this.multiProvider.getExplorerApi(chain);
@ -139,7 +139,7 @@ export class ContractVerifier {
}
if (errorMessage) {
verificationLogger(errorMessage);
verificationLogger.debug(errorMessage);
throw new Error(`[${chain}] ${errorMessage}`);
}
}
@ -153,7 +153,7 @@ export class ContractVerifier {
const errorMessage = `Verification failed. ${
result.result ?? response.statusText
}`;
verificationLogger(errorMessage);
verificationLogger.debug(errorMessage);
throw new Error(`[${chain}] ${errorMessage}`);
}
@ -163,7 +163,7 @@ export class ContractVerifier {
private async isAlreadyVerified(
chain: ChainName,
input: ContractVerificationInput,
verificationLogger: Debugger,
verificationLogger: Logger,
): Promise<boolean> {
try {
const result = await this.submitForm(
@ -176,7 +176,7 @@ export class ContractVerifier {
);
return !!result[0]?.SourceCode;
} catch (error) {
verificationLogger(
verificationLogger.debug(
`Error checking if contract is already verified: ${error}`,
);
return false;
@ -186,7 +186,7 @@ export class ContractVerifier {
private async verifyProxy(
chain: ChainName,
input: ContractVerificationInput,
verificationLogger: Debugger,
verificationLogger: Logger,
): Promise<void> {
if (!input.isProxy) return;
@ -211,11 +211,11 @@ export class ContractVerifier {
chain,
input.address,
);
verificationLogger(
verificationLogger.debug(
`Successfully verified proxy ${addressUrl}#readProxyContract`,
);
} catch (error) {
verificationLogger(
verificationLogger.debug(
`Verification of proxy at ${input.address} failed: ${error}`,
);
throw error;
@ -225,14 +225,14 @@ export class ContractVerifier {
private async verifyImplementation(
chain: ChainName,
input: ContractVerificationInput,
verificationLogger: Debugger,
verificationLogger: Logger,
): Promise<void> {
verificationLogger(`Verifying implementation at ${input.address}`);
verificationLogger.debug(`Verifying implementation at ${input.address}`);
const sourceName = this.contractSourceMap[input.name];
if (!sourceName) {
const errorMessage = `Contract '${input.name}' not found in provided build artifact`;
verificationLogger(errorMessage);
verificationLogger.error(errorMessage);
throw new Error(`[${chain}] ${errorMessage}`);
}
@ -263,7 +263,7 @@ export class ContractVerifier {
chain,
input.address,
);
verificationLogger(`Successfully verified ${addressUrl}#code`);
verificationLogger.debug(`Successfully verified ${addressUrl}#code`);
}
async verifyContract(
@ -271,34 +271,36 @@ export class ContractVerifier {
input: ContractVerificationInput,
logger = this.logger,
): Promise<void> {
const verificationLogger = logger.extend(`${chain}:${input.name}`);
const verificationLogger = logger.child({ chain, name: input.name });
const metadata = this.multiProvider.tryGetChainMetadata(chain);
const rpcUrl = metadata?.rpcUrls[0].http ?? '';
if (rpcUrl.includes('localhost') || rpcUrl.includes('127.0.0.1')) {
verificationLogger('Skipping verification for local endpoints');
verificationLogger.debug('Skipping verification for local endpoints');
return;
}
const explorerApi = this.multiProvider.tryGetExplorerApi(chain);
if (!explorerApi) {
verificationLogger('No explorer API set, skipping');
verificationLogger.debug('No explorer API set, skipping');
return;
}
if (!explorerApi.family) {
verificationLogger(`No explorer family set, skipping`);
verificationLogger.debug(`No explorer family set, skipping`);
return;
}
if (explorerApi.family === ExplorerFamily.Other) {
verificationLogger(`Unsupported explorer family, skipping`);
verificationLogger.debug(`Unsupported explorer family, skipping`);
return;
}
if (input.address === ethers.constants.AddressZero) return;
if (Array.isArray(input.constructorArguments)) {
verificationLogger('Constructor arguments in legacy format, skipping');
verificationLogger.debug(
'Constructor arguments in legacy format, skipping',
);
return;
}
@ -307,7 +309,9 @@ export class ContractVerifier {
chain,
input.address,
);
verificationLogger(`Contract already verified at ${addressUrl}#code`);
verificationLogger.debug(
`Contract already verified at ${addressUrl}#code`,
);
await sleep(200); // There is a rate limit of 5 requests per second
return;
}

@ -1,4 +1,4 @@
import { debug } from 'debug';
import { rootLogger } from '@hyperlane-xyz/utils';
import { ExplorerFamily } from '../../metadata/chainMetadataTypes';
import { MultiProvider } from '../../providers/MultiProvider';
@ -9,7 +9,9 @@ import { ContractVerifier } from './ContractVerifier';
import { BuildArtifact, CompilerOptions, VerificationInput } from './types';
export class PostDeploymentContractVerifier extends MultiGeneric<VerificationInput> {
protected logger = debug('hyperlane:PostDeploymentContractVerifier');
protected logger = rootLogger.child({
module: 'PostDeploymentContractVerifier',
});
protected readonly contractVerifier: ContractVerifier;
constructor(
@ -34,13 +36,13 @@ export class PostDeploymentContractVerifier extends MultiGeneric<VerificationInp
// can check explorer family here to avoid doing these checks for each input in verifier
const { family } = this.multiProvider.getExplorerApi(chain);
if (family === ExplorerFamily.Other) {
this.logger(
this.logger.warn(
`Skipping verification for ${chain} due to unsupported explorer family.`,
);
return;
}
this.logger(`Verifying ${chain}...`);
this.logger.debug(`Verifying ${chain}...`);
for (const input of this.get(chain)) {
await this.contractVerifier.verifyContract(chain, input, this.logger);
}

@ -84,7 +84,7 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker<
for (const remote of remotes) {
let expectedOverhead = this.configMap[local].overhead[remote];
if (!expectedOverhead) {
this.app.logger(
this.app.logger.debug(
`No overhead configured for ${local} -> ${remote}, defaulting to 0`,
);
expectedOverhead = 0;

@ -1,11 +1,9 @@
import debug from 'debug';
import {
InterchainGasPaymaster,
ProxyAdmin,
StorageGasOracle,
} from '@hyperlane-xyz/core';
import { eqAddress } from '@hyperlane-xyz/utils';
import { eqAddress, rootLogger } from '@hyperlane-xyz/utils';
import { chainMetadata } from '../consts/chainMetadata';
import { HyperlaneContracts } from '../contracts/types';
@ -27,7 +25,7 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer<
contractVerifier?: ContractVerifier,
) {
super(multiProvider, igpFactories, {
logger: debug('hyperlane:IgpDeployer'),
logger: rootLogger.child({ module: 'IgpDeployer' }),
contractVerifier,
});
}
@ -58,7 +56,7 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer<
!eqAddress(currentGasConfig.gasOracle, storageGasOracle.address) ||
!currentGasConfig.gasOverhead.eq(newGasOverhead)
) {
this.logger(
this.logger.debug(
`Setting gas params for ${chain} -> ${remote}: gasOverhead = ${newGasOverhead} gasOracle = ${storageGasOracle.address}`,
);
gasParamsToSet.push({
@ -93,11 +91,11 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer<
const gasOracle = await this.deployContract(chain, 'storageGasOracle', []);
if (!config.oracleConfig) {
this.logger('No oracle config provided, skipping...');
this.logger.debug('No oracle config provided, skipping...');
return gasOracle;
}
this.logger(`Configuring gas oracle from ${chain}...`);
this.logger.debug(`Configuring gas oracle from ${chain}...`);
const configsToSet: Array<StorageGasOracle.RemoteGasDataConfigStruct> = [];
// For each remote, check if the gas oracle has the correct data
@ -113,7 +111,9 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer<
!actual.gasPrice.eq(desired.gasPrice) ||
!actual.tokenExchangeRate.eq(desired.tokenExchangeRate)
) {
this.logger(`-> ${remote} ${serializeDifference(actual, desired)}`);
this.logger.debug(
`-> ${remote} ${serializeDifference(actual, desired)}`,
);
configsToSet.push({
remoteDomain,
...desired,

@ -1,6 +1,6 @@
import CoinGecko from 'coingecko-api';
import { sleep, warn } from '@hyperlane-xyz/utils';
import { rootLogger, sleep } from '@hyperlane-xyz/utils';
import { chainMetadata as defaultChainMetadata } from '../consts/chainMetadata';
import { CoreChainName, Mainnets } from '../consts/chains';
@ -131,7 +131,7 @@ export class CoinGeckoTokenPriceGetter implements TokenPriceGetter {
try {
await this.queryTokenPrices(toQuery);
} catch (e) {
warn('Failed to query token prices', e);
rootLogger.warn('Failed to query token prices', e);
}
}
return chains.map((chain) => this.cache.fetch(chain));

@ -1,4 +1,3 @@
import debug from 'debug';
import { ethers } from 'ethers';
import {
@ -10,7 +9,7 @@ import {
ProtocolFee,
StaticAggregationHook__factory,
} from '@hyperlane-xyz/core';
import { Address, addressToBytes32 } from '@hyperlane-xyz/utils';
import { Address, addressToBytes32, rootLogger } from '@hyperlane-xyz/utils';
import { chainMetadata } from '../consts/chainMetadata';
import { HyperlaneContracts } from '../contracts/types';
@ -51,7 +50,7 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer<
),
) {
super(multiProvider, hookFactories, {
logger: debug('hyperlane:HookDeployer'),
logger: rootLogger.child({ module: 'HookDeployer' }),
contractVerifier,
});
}
@ -112,7 +111,7 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer<
chain: ChainName,
config: ProtocolFeeHookConfig,
): Promise<ProtocolFee> {
this.logger('Deploying ProtocolFeeHook for %s', chain);
this.logger.debug('Deploying ProtocolFeeHook for %s', chain);
return this.deployContract(chain, HookType.PROTOCOL_FEE, [
config.maxProtocolFee,
config.protocolFee,
@ -126,7 +125,7 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer<
config: IgpHookConfig,
coreAddresses = this.core[chain],
): Promise<HyperlaneContracts<IgpFactories>> {
this.logger('Deploying IGP as hook for %s', chain);
this.logger.debug('Deploying IGP as hook for %s', chain);
if (coreAddresses.proxyAdmin) {
this.igpDeployer.writeCache(
chain,
@ -149,7 +148,7 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer<
config: AggregationHookConfig,
coreAddresses = this.core[chain],
): Promise<HyperlaneContracts<HookFactories>> {
this.logger('Deploying AggregationHook for %s', chain);
this.logger.debug('Deploying AggregationHook for %s', chain);
const aggregatedHooks: string[] = [];
let hooks: any = {};
for (const hookConfig of config.hooks) {
@ -161,7 +160,7 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer<
aggregatedHooks.push(subhooks[hookConfig.type].address);
hooks = { ...hooks, ...subhooks };
}
this.logger(
this.logger.debug(
`Deploying aggregation hook of ${config.hooks.map((h) => h.type)}`,
);
const address = await this.ismFactory.deployStaticAddressSet(
@ -183,7 +182,7 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer<
config: OpStackHookConfig,
coreAddresses = this.core[chain],
): Promise<OPStackHook> {
this.logger(
this.logger.debug(
'Deploying OPStackHook for %s to %s',
chain,
config.destinationChain,
@ -220,12 +219,15 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer<
// set authorized hook on opstack ism
const authorizedHook = await opstackIsm.authorizedHook();
if (authorizedHook === addressToBytes32(hook.address)) {
this.logger('Authorized hook already set on ism %s', opstackIsm.address);
this.logger.debug(
'Authorized hook already set on ism %s',
opstackIsm.address,
);
return hook;
} else if (
authorizedHook !== addressToBytes32(ethers.constants.AddressZero)
) {
this.logger(
this.logger.debug(
'Authorized hook mismatch on ism %s, expected %s, got %s',
opstackIsm.address,
addressToBytes32(hook.address),
@ -234,7 +236,7 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer<
throw new Error('Authorized hook mismatch');
}
// check if mismatch and redeploy hook
this.logger(
this.logger.debug(
'Setting authorized hook %s on ism % on destination %s',
hook.address,
opstackIsm.address,
@ -263,7 +265,7 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer<
let routingHook: DomainRoutingHook | FallbackDomainRoutingHook;
switch (config.type) {
case HookType.ROUTING: {
this.logger('Deploying DomainRoutingHook for %s', chain);
this.logger.debug('Deploying DomainRoutingHook for %s', chain);
routingHook = await this.deployContract(chain, HookType.ROUTING, [
mailbox,
deployer,
@ -271,7 +273,7 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer<
break;
}
case HookType.FALLBACK_ROUTING: {
this.logger('Deploying FallbackDomainRoutingHook for %s', chain);
this.logger.debug('Deploying FallbackDomainRoutingHook for %s', chain);
const fallbackHook = await this.deployContracts(
chain,
config.fallback,

@ -1,8 +1,9 @@
/* eslint-disable no-console */
import { expect } from 'chai';
import { ethers } from 'hardhat';
import { DomainRoutingIsm } from '@hyperlane-xyz/core';
import { Address, error } from '@hyperlane-xyz/utils';
import { Address } from '@hyperlane-xyz/utils';
import { TestChains } from '../consts/chains';
import { TestCoreApp } from '../core/TestCoreApp';
@ -130,8 +131,8 @@ describe('HyperlaneIsmFactory', async () => {
const ism = await ismFactory.deploy({ destination: chain, config });
ismAddress = ism.address;
} catch (e) {
error('Failed to deploy random ism config', e);
error(JSON.stringify(config, null, 2));
console.error('Failed to deploy random ism config', e);
console.error(JSON.stringify(config, null, 2));
process.exit(1);
}
@ -145,8 +146,8 @@ describe('HyperlaneIsmFactory', async () => {
);
expect(matches).to.be.true;
} catch (e) {
error('Failed to match random ism config', e);
error(JSON.stringify(config, null, 2));
console.error('Failed to match random ism config', e);
console.error(JSON.stringify(config, null, 2));
process.exit(1);
}
});

@ -1,5 +1,5 @@
import debug, { Debugger } from 'debug';
import { ethers } from 'ethers';
import { Logger } from 'pino';
import {
DefaultFallbackRoutingIsm,
@ -23,7 +23,7 @@ import {
Domain,
eqAddress,
objFilter,
warn,
rootLogger,
} from '@hyperlane-xyz/utils';
import { HyperlaneApp } from '../app/HyperlaneApp';
@ -89,7 +89,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
return new HyperlaneIsmFactory(
helper.contractsMap,
multiProvider,
debug('hyperlane:IsmFactoryApp'),
rootLogger.child({ module: 'ismFactoryApp' }),
);
}
@ -110,9 +110,9 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
}
const ismType = config.type;
const logger = this.logger.extend(`${destination}:${ismType}`);
const logger = this.logger.child({ destination, ismType });
logger(
logger.debug(
`Deploying ${ismType} to ${destination} ${
origin ? `(for verifying ${origin})` : ''
}`,
@ -205,7 +205,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
protected async deployMultisigIsm(
destination: ChainName,
config: MultisigIsmConfig,
logger: Debugger,
logger: Logger,
): Promise<IMultisigIsm> {
const signer = this.multiProvider.getSigner(destination);
const multisigIsmFactory =
@ -230,9 +230,9 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
origin?: ChainName;
mailbox?: Address;
existingIsmAddress?: Address;
logger: Debugger;
logger: Logger;
}): Promise<IRoutingIsm> {
const { destination, config, mailbox, existingIsmAddress } = params;
const { destination, config, mailbox, existingIsmAddress, logger } = params;
const overrides = this.multiProvider.getTransactionOverrides(destination);
const domainRoutingIsmFactory =
this.getContracts(destination).domainRoutingIsmFactory;
@ -245,7 +245,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
chainMetadata[domain]?.domainId ??
this.multiProvider.tryGetDomainId(domain);
if (domainId === null) {
warn(
logger.warn(
`Domain ${domain} doesn't have chain metadata provided, skipping ...`,
);
}
@ -291,7 +291,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
// deploying all the ISMs which have to be updated
for (const originDomain of delta.domainsToEnroll) {
const origin = this.multiProvider.getChainName(originDomain); // already filtered to only include domains in the multiprovider
params.logger(
logger.debug(
`Reconfiguring preexisting routing ISM at for origin ${origin}...`,
);
const ism = await this.deploy({
@ -310,7 +310,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
}
// unenrolling domains if needed
for (const originDomain of delta.domainsToUnenroll) {
params.logger(
logger.debug(
`Unenrolling originDomain ${originDomain} from preexisting routing ISM at ${existingIsmAddress}...`,
);
const tx = await routingIsm.remove(originDomain, overrides);
@ -318,7 +318,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
}
// transfer ownership if needed
if (delta.owner) {
params.logger(`Transferring ownership of routing ISM...`);
logger.debug(`Transferring ownership of routing ISM...`);
const tx = await routingIsm.transferOwnership(delta.owner, overrides);
await this.multiProvider.handleTx(destination, tx);
}
@ -342,13 +342,13 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
'Mailbox address is required for deploying fallback routing ISM',
);
}
params.logger('Deploying fallback routing ISM ...');
logger.debug('Deploying fallback routing ISM ...');
routingIsm = await this.multiProvider.handleDeploy(
destination,
new DefaultFallbackRoutingIsm__factory(),
[mailbox],
);
params.logger('Initialising fallback routing ISM ...');
logger.debug('Initialising fallback routing ISM ...');
receipt = await this.multiProvider.handleTx(
destination,
routingIsm['initialize(address,uint32[],address[])'](
@ -399,7 +399,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
config: AggregationIsmConfig;
origin?: ChainName;
mailbox?: Address;
logger: Debugger;
logger: Logger;
}): Promise<IAggregationIsm> {
const { destination, config, origin, mailbox } = params;
const signer = this.multiProvider.getSigner(destination);
@ -429,7 +429,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
chain: ChainName,
factory: StaticThresholdAddressSetFactory | StaticAddressSetFactory,
values: Address[],
logger: Debugger,
logger: Logger,
threshold = values.length,
): Promise<Address> {
const sorted = [...values].sort();
@ -440,7 +440,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
);
const code = await this.multiProvider.getProvider(chain).getCode(address);
if (code === '0x') {
logger(
logger.debug(
`Deploying new ${threshold} of ${values.length} address set to ${chain}`,
);
const overrides = this.multiProvider.getTransactionOverrides(chain);
@ -452,7 +452,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp<ProxyFactoryFactories> {
await this.multiProvider.handleTx(chain, hash);
// TODO: add proxy verification artifact?
} else {
logger(
logger.debug(
`Recovered ${threshold} of ${values.length} address set on ${chain}`,
);
}

@ -1,4 +1,3 @@
import debug from 'debug';
import { ethers } from 'ethers';
import {
@ -18,6 +17,7 @@ import {
formatMessage,
normalizeAddress,
objMap,
rootLogger,
} from '@hyperlane-xyz/utils';
import { chainMetadata } from '../consts/chainMetadata';
@ -35,7 +35,7 @@ import {
ismTypeToModuleType,
} from './types';
const logger = debug('hyperlane:IsmUtils');
const logger = rootLogger.child({ module: 'IsmUtils' });
// Note that this function may return false negatives, but should
// not return false positives.
@ -117,8 +117,8 @@ export async function moduleCanCertainlyVerify(
} else {
throw new Error(`Unsupported module type: ${moduleType}`);
}
} catch (e) {
logger(`Error checking module ${destModule}: ${e}`);
} catch (err) {
logger.error(`Error checking module ${destModule}`, err);
return false;
}
} else {
@ -381,7 +381,9 @@ export function collectValidators(
): Set<string> {
// TODO: support address configurations in collectValidators
if (typeof config === 'string') {
logger.extend(origin)('Address config unimplemented in collectValidators');
logger
.child({ origin })
.debug('Address config unimplemented in collectValidators');
return new Set([]);
}

@ -1,5 +0,0 @@
import debug from 'debug';
// Default logger for use in utils/scripts
// For classes, prefer to create loggers with more specific namespaces
export const logger = debug('hyperlane');

@ -1,6 +1,6 @@
import { Debugger, debug } from 'debug';
import { Logger } from 'pino';
import { ProtocolType, exclude, pick } from '@hyperlane-xyz/utils';
import { ProtocolType, exclude, pick, rootLogger } from '@hyperlane-xyz/utils';
import { chainMetadata as defaultChainMetadata } from '../consts/chainMetadata';
import { ChainMap, ChainName, ChainNameOrId } from '../types';
@ -20,7 +20,7 @@ import {
} from './chainMetadataTypes';
export interface ChainMetadataManagerOptions {
loggerName?: string;
logger?: Logger;
}
/**
@ -30,7 +30,7 @@ export interface ChainMetadataManagerOptions {
*/
export class ChainMetadataManager<MetaExt = {}> {
public readonly metadata: ChainMap<ChainMetadata<MetaExt>> = {};
public readonly logger: Debugger;
public readonly logger: Logger;
/**
* Create a new ChainMetadataManager with the given chainMetadata,
@ -49,7 +49,11 @@ export class ChainMetadataManager<MetaExt = {}> {
);
this.addChain(cm);
});
this.logger = debug(options?.loggerName || 'hyperlane:MetadataManager');
this.logger =
options?.logger ||
rootLogger.child({
module: 'MetadataManager',
});
}
/**

@ -1,10 +1,14 @@
import { Mailbox__factory } from '@hyperlane-xyz/core';
import { Address, ProtocolType, timeout } from '@hyperlane-xyz/utils';
import {
Address,
ProtocolType,
rootLogger,
timeout,
} from '@hyperlane-xyz/utils';
import { chainIdToMetadata } from '../consts/chainMetadata';
import { CoreChainName } from '../consts/chains';
import { hyperlaneContractAddresses } from '../consts/environments';
import { logger } from '../logger';
import {
CosmJsProvider,
CosmJsWasmProvider,
@ -23,6 +27,8 @@ import { ChainMetadata, RpcUrl } from './chainMetadataTypes';
const HEALTH_CHECK_TIMEOUT = 5000; // 5s
const logger = rootLogger.child({ module: 'metadata-health' });
export async function isRpcHealthy(
rpc: RpcUrl,
chainId: string | number,
@ -52,7 +58,7 @@ export async function isRpcHealthy(
);
return result;
} catch (error) {
logger(`Provider error for ${rpc.http}`, error);
logger.error(`Provider error for ${rpc.http}`, error);
return false;
}
}
@ -61,10 +67,10 @@ export async function isEthersV5ProviderHealthy(
provider: EthersV5Provider['provider'],
chainId: string | number,
): Promise<boolean> {
logger(`Checking ethers provider for ${chainId}`);
logger.debug(`Checking ethers provider for ${chainId}`);
const blockNumber = await provider.getBlockNumber();
if (!blockNumber || blockNumber < 0) return false;
logger(`Block number is okay for ${chainId}`);
logger.debug(`Block number is okay for ${chainId}`);
const chainName = chainIdToMetadata[chainId]?.name as CoreChainName;
if (chainName && hyperlaneContractAddresses[chainName]) {
@ -74,7 +80,7 @@ export async function isEthersV5ProviderHealthy(
mailbox.events['DispatchId(bytes32)'],
[],
);
logger(`Checking mailbox logs for ${chainId}`);
logger.debug(`Checking mailbox logs for ${chainId}`);
const mailboxLogs = await provider.getLogs({
address: mailboxAddr,
topics,
@ -82,7 +88,7 @@ export async function isEthersV5ProviderHealthy(
toBlock: blockNumber,
});
if (!mailboxLogs) return false;
logger(`Mailbox logs okay for ${chainId}`);
logger.debug(`Mailbox logs okay for ${chainId}`);
}
return true;
}
@ -91,10 +97,10 @@ export async function isSolanaWeb3ProviderHealthy(
provider: SolanaWeb3Provider['provider'],
chainId: string | number,
): Promise<boolean> {
logger(`Checking solana provider for ${chainId}`);
logger.debug(`Checking solana provider for ${chainId}`);
const blockNumber = await provider.getBlockHeight();
if (!blockNumber || blockNumber < 0) return false;
logger(`Block number is okay for ${chainId}`);
logger.debug(`Block number is okay for ${chainId}`);
return true;
}
@ -105,7 +111,7 @@ export async function isCosmJsProviderHealthy(
const readyProvider = await provider;
const blockNumber = await readyProvider.getHeight();
if (!blockNumber || blockNumber < 0) return false;
logger(`Block number is okay for ${chainId}`);
logger.debug(`Block number is okay for ${chainId}`);
return true;
}
@ -117,36 +123,36 @@ export async function isBlockExplorerHealthy(
try {
const baseUrl = getExplorerBaseUrl(chainMetadata);
if (!baseUrl) return false;
logger(`Got base url: ${baseUrl}`);
logger.debug(`Got base url: ${baseUrl}`);
logger(`Checking explorer home for ${chainMetadata.name}`);
logger.debug(`Checking explorer home for ${chainMetadata.name}`);
const homeReq = await fetch(baseUrl);
if (!homeReq.ok) return false;
logger(`Explorer home okay for ${chainMetadata.name}`);
logger.debug(`Explorer home okay for ${chainMetadata.name}`);
if (address) {
logger(`Checking explorer address page for ${chainMetadata.name}`);
logger.debug(`Checking explorer address page for ${chainMetadata.name}`);
const addressUrl = getExplorerAddressUrl(chainMetadata, address);
if (!addressUrl) return false;
logger(`Got address url: ${addressUrl}`);
logger.debug(`Got address url: ${addressUrl}`);
const addressReq = await fetch(addressUrl);
if (!addressReq.ok && addressReq.status !== 404) return false;
logger(`Explorer address page okay for ${chainMetadata.name}`);
logger.debug(`Explorer address page okay for ${chainMetadata.name}`);
}
if (txHash) {
logger(`Checking explorer tx page for ${chainMetadata.name}`);
logger.debug(`Checking explorer tx page for ${chainMetadata.name}`);
const txUrl = getExplorerTxUrl(chainMetadata, txHash);
if (!txUrl) return false;
logger(`Got tx url: ${txUrl}`);
logger.debug(`Got tx url: ${txUrl}`);
const txReq = await fetch(txUrl);
if (!txReq.ok && txReq.status !== 404) return false;
logger(`Explorer tx page okay for ${chainMetadata.name}`);
logger.debug(`Explorer tx page okay for ${chainMetadata.name}`);
}
return true;
} catch (error) {
logger(`Explorer error for ${chainMetadata.name}`, error);
logger.error(`Explorer error for ${chainMetadata.name}`, error);
return false;
}
}

@ -94,10 +94,10 @@ export class LiquidityLayerDeployer extends ProxiedRouterDeployer<
configMap: ChainMap<LiquidityLayerConfig>,
foreignRouters: ChainMap<Address>,
): Promise<void> {
this.logger(`Enroll LiquidityLayerRouters with each other`);
this.logger.debug(`Enroll LiquidityLayerRouters with each other`);
await super.enrollRemoteRouters(contractsMap, configMap, foreignRouters);
this.logger(`Enroll CircleBridgeAdapters with each other`);
this.logger.debug(`Enroll CircleBridgeAdapters with each other`);
// Hack to allow use of super.enrollRemoteRouters
await super.enrollRemoteRouters(
objMap(
@ -114,7 +114,7 @@ export class LiquidityLayerDeployer extends ProxiedRouterDeployer<
foreignRouters,
);
this.logger(`Enroll PortalAdapters with each other`);
this.logger.debug(`Enroll PortalAdapters with each other`);
// Hack to allow use of super.enrollRemoteRouters
await super.enrollRemoteRouters(
objMap(
@ -192,7 +192,7 @@ export class LiquidityLayerDeployer extends ProxiedRouterDeployer<
await portalAdapter.hyperlaneDomainToWormholeDomain(hyperlaneDomain);
if (expectedCircleDomain === wormholeDomain) continue;
this.logger(
this.logger.debug(
`Set wormhole domain ${wormholeDomain} for hyperlane domain ${hyperlaneDomain}`,
);
await this.runIfOwner(chain, portalAdapter, () =>
@ -209,7 +209,7 @@ export class LiquidityLayerDeployer extends ProxiedRouterDeployer<
portalAdapter.address,
)
) {
this.logger('Set Portal as LiquidityLayerAdapter on Router');
this.logger.debug('Set Portal as LiquidityLayerAdapter on Router');
await this.runIfOwner(chain, portalAdapter, () =>
this.multiProvider.handleTx(
chain,
@ -249,7 +249,7 @@ export class LiquidityLayerDeployer extends ProxiedRouterDeployer<
adapterConfig.usdcAddress,
)
) {
this.logger(`Set USDC token contract`);
this.logger.debug(`Set USDC token contract`);
await this.runIfOwner(chain, circleBridgeAdapter, () =>
this.multiProvider.handleTx(
chain,
@ -268,7 +268,7 @@ export class LiquidityLayerDeployer extends ProxiedRouterDeployer<
);
if (expectedCircleDomain === circleDomain) continue;
this.logger(
this.logger.debug(
`Set circle domain ${circleDomain} for hyperlane domain ${hyperlaneDomain}`,
);
await this.runIfOwner(chain, circleBridgeAdapter, () =>
@ -285,7 +285,7 @@ export class LiquidityLayerDeployer extends ProxiedRouterDeployer<
circleBridgeAdapter.address,
)
) {
this.logger('Set Circle as LiquidityLayerAdapter on Router');
this.logger.debug('Set Circle as LiquidityLayerAdapter on Router');
await this.runIfOwner(chain, circleBridgeAdapter, () =>
this.multiProvider.handleTx(
chain,

@ -1,4 +1,4 @@
import { Debugger, debug } from 'debug';
import { Logger } from 'pino';
import {
Address,
@ -6,6 +6,7 @@ import {
objFilter,
objMap,
pick,
rootLogger,
} from '@hyperlane-xyz/utils';
import { chainMetadata as defaultChainMetadata } from '../consts/chainMetadata';
@ -36,7 +37,7 @@ import {
} from './transactionFeeEstimators';
export interface MultiProtocolProviderOptions {
loggerName?: string;
logger?: Logger;
providers?: ChainMap<ProviderMap<TypedProvider>>;
providerBuilders?: Partial<ProviderBuilderMap>;
}
@ -59,7 +60,7 @@ export class MultiProtocolProvider<
// Chain name -> provider type -> signer
protected signers: ChainMap<ProviderMap<never>> = {}; // TODO signer support
protected readonly providerBuilders: Partial<ProviderBuilderMap>;
public readonly logger: Debugger;
public readonly logger: Logger;
constructor(
chainMetadata: ChainMap<
@ -68,9 +69,11 @@ export class MultiProtocolProvider<
protected readonly options: MultiProtocolProviderOptions = {},
) {
super(chainMetadata, options);
this.logger = debug(
options?.loggerName || 'hyperlane:MultiProtocolProvider',
);
this.logger =
options?.logger ||
rootLogger.child({
module: 'MultiProtocolProvider',
});
this.providers = options.providers || {};
this.providerBuilders =
options.providerBuilders || defaultProviderBuilderMap;

@ -1,4 +1,3 @@
import { Debugger, debug } from 'debug';
import {
BigNumber,
ContractFactory,
@ -8,8 +7,9 @@ import {
Signer,
providers,
} from 'ethers';
import { Logger } from 'pino';
import { Address, pick } from '@hyperlane-xyz/utils';
import { Address, pick, rootLogger } from '@hyperlane-xyz/utils';
import { chainMetadata as defaultChainMetadata } from '../consts/chainMetadata';
import { CoreChainName, TestChains } from '../consts/chains';
@ -22,7 +22,7 @@ import { ProviderBuilderFn, defaultProviderBuilder } from './providerBuilders';
type Provider = providers.Provider;
export interface MultiProviderOptions {
loggerName?: string;
logger?: Logger;
providers?: ChainMap<Provider>;
providerBuilder?: ProviderBuilderFn<Provider>;
signers?: ChainMap<Signer>;
@ -37,7 +37,7 @@ export class MultiProvider<MetaExt = {}> extends ChainMetadataManager<MetaExt> {
readonly providerBuilder: ProviderBuilderFn<Provider>;
signers: ChainMap<Signer>;
useSharedSigner = false; // A single signer to be used for all chains
readonly logger: Debugger;
readonly logger: Logger;
/**
* Create a new MultiProvider with the given chainMetadata,
@ -48,7 +48,11 @@ export class MultiProvider<MetaExt = {}> extends ChainMetadataManager<MetaExt> {
readonly options: MultiProviderOptions = {},
) {
super(chainMetadata, options);
this.logger = debug(options?.loggerName || 'hyperlane:MultiProvider');
this.logger =
options?.logger ||
rootLogger.child({
module: 'MultiProvider',
});
this.providers = options?.providers || {};
this.providerBuilder = options?.providerBuilder || defaultProviderBuilder;
this.signers = options?.signers || {};
@ -323,7 +327,7 @@ export class MultiProvider<MetaExt = {}> extends ChainMetadataManager<MetaExt> {
this.getChainMetadata(chainNameOrId).blocks?.confirmations ?? 1;
const response = await tx;
const txUrl = this.tryGetExplorerTxUrl(chainNameOrId, response);
this.logger(
this.logger.info(
`Pending ${
txUrl || response.hash
} (waiting ${confirmations} blocks for confirmation)`,
@ -381,7 +385,7 @@ export class MultiProvider<MetaExt = {}> extends ChainMetadataManager<MetaExt> {
const txReq = await this.prepareTx(chainNameOrId, await tx);
const signer = this.getSigner(chainNameOrId);
const response = await signer.sendTransaction(txReq);
this.logger(`Sent tx ${response.hash}`);
this.logger.info(`Sent tx ${response.hash}`);
return this.handleTx(chainNameOrId, response);
}

@ -1,7 +1,6 @@
import debug from 'debug';
import { providers } from 'ethers';
import { objFilter, sleep } from '@hyperlane-xyz/utils';
import { objFilter, rootLogger, sleep } from '@hyperlane-xyz/utils';
import { BlockExplorer } from '../../metadata/chainMetadataTypes';
@ -19,7 +18,7 @@ export class HyperlaneEtherscanProvider
extends providers.EtherscanProvider
implements IProviderMethods
{
protected readonly logger = debug('hyperlane:EtherscanProvider');
protected readonly logger = rootLogger.child({ module: 'EtherscanProvider' });
// Seeing problems with these two methods even though etherscan api claims to support them
public readonly supportedMethods = excludeProviderMethods([
ProviderMethod.Call,
@ -34,7 +33,7 @@ export class HyperlaneEtherscanProvider
) {
super(network, explorerConfig.apiKey);
if (!explorerConfig.apiKey) {
this.logger(
this.logger.warn(
'HyperlaneEtherscanProviders created without an API key will be severely rate limited. Consider using an API key for better reliability.',
);
}
@ -82,7 +81,7 @@ export class HyperlaneEtherscanProvider
let waitTime = this.getQueryWaitTime();
while (waitTime > 0) {
if (this.options?.debug)
this.logger(
this.logger.debug(
`HyperlaneEtherscanProvider waiting ${waitTime}ms to avoid rate limit`,
);
await sleep(waitTime);
@ -95,7 +94,7 @@ export class HyperlaneEtherscanProvider
async perform(method: string, params: any, reqId?: number): Promise<any> {
if (this.options?.debug)
this.logger(
this.logger.debug(
`HyperlaneEtherscanProvider performing method ${method} for reqId ${reqId}`,
);
if (!this.supportedMethods.includes(method as ProviderMethod))

@ -1,7 +1,11 @@
import debug from 'debug';
import { BigNumber, providers, utils } from 'ethers';
import { chunk, isBigNumberish, isNullish } from '@hyperlane-xyz/utils';
import {
chunk,
isBigNumberish,
isNullish,
rootLogger,
} from '@hyperlane-xyz/utils';
import {
AllProviderMethods,
@ -17,7 +21,7 @@ export class HyperlaneJsonRpcProvider
extends providers.StaticJsonRpcProvider
implements IProviderMethods
{
protected readonly logger = debug('hyperlane:JsonRpcProvider');
protected readonly logger = rootLogger.child({ module: 'JsonRpcProvider' });
public readonly supportedMethods = AllProviderMethods;
constructor(
@ -30,7 +34,7 @@ export class HyperlaneJsonRpcProvider
async perform(method: string, params: any, reqId?: number): Promise<any> {
if (this.options?.debug)
this.logger(
this.logger.debug(
`HyperlaneJsonRpcProvider performing method ${method} for reqId ${reqId}`,
);
if (method === ProviderMethod.GetLogs) {
@ -47,7 +51,9 @@ export class HyperlaneJsonRpcProvider
ProviderMethod.GetBlockNumber,
].includes(method as ProviderMethod)
) {
this.logger(`Received 0x result from ${method} for reqId ${reqId}.`);
this.logger.debug(
`Received 0x result from ${method} for reqId ${reqId}.`,
);
throw new Error('Invalid response from provider');
}
return result;
@ -89,7 +95,7 @@ export class HyperlaneJsonRpcProvider
}
if (startBlock > endBlock) {
this.logger(
this.logger.info(
`Start block ${startBlock} greater than end block. Using ${endBlock} instead`,
);
startBlock = endBlock;
@ -98,20 +104,20 @@ export class HyperlaneJsonRpcProvider
? endBlock - maxBlockRange * NUM_LOG_BLOCK_RANGES_TO_QUERY + 1
: 0;
if (startBlock < minForBlockRange) {
this.logger(
this.logger.info(
`Start block ${startBlock} requires too many queries, using ${minForBlockRange}.`,
);
startBlock = minForBlockRange;
}
const minForBlockAge = maxBlockAge ? currentBlockNumber - maxBlockAge : 0;
if (startBlock < minForBlockAge) {
this.logger(
this.logger.info(
`Start block ${startBlock} below max block age, increasing to ${minForBlockAge}`,
);
startBlock = minForBlockAge;
}
if (minBlockNumber && startBlock < minBlockNumber) {
this.logger(
this.logger.info(
`Start block ${startBlock} below config min, increasing to ${minBlockNumber}`,
);
startBlock = minBlockNumber;

@ -1,9 +1,10 @@
import debug, { Debugger } from 'debug';
import { providers } from 'ethers';
import { Logger } from 'pino';
import {
raceWithContext,
retryAsync,
rootLogger,
runWithTimeout,
sleep,
} from '@hyperlane-xyz/utils';
@ -36,7 +37,7 @@ export class HyperlaneSmartProvider
extends providers.BaseProvider
implements IProviderMethods
{
protected logger: Debugger;
protected logger: Logger;
// TODO also support blockscout here
public readonly explorerProviders: HyperlaneEtherscanProvider[];
@ -53,7 +54,9 @@ export class HyperlaneSmartProvider
super(network);
const supportedMethods = new Set<ProviderMethod>();
this.logger = debug(`hyperlane:SmartProvider:${this.network.chainId}`);
this.logger = rootLogger.child({
module: `SmartProvider:${this.network.chainId}`,
});
if (!rpcUrls?.length && !blockExplorers?.length)
throw new Error('At least one RPC URL or block explorer is required');
@ -171,7 +174,7 @@ export class HyperlaneSmartProvider
});
return true;
} catch (error) {
this.logger('Provider is unhealthy', error);
this.logger.error('Provider is unhealthy', error);
return false;
}
}
@ -226,7 +229,7 @@ export class HyperlaneSmartProvider
if (result.status === ProviderStatus.Success) {
return result.value;
} else if (result.status === ProviderStatus.Timeout) {
this.logger(
this.logger.debug(
`Slow response from provider #${pIndex}.${
!isLastProvider ? ' Triggering next provider.' : ''
}`,
@ -234,7 +237,7 @@ export class HyperlaneSmartProvider
providerResultPromises.push(resultPromise);
pIndex += 1;
} else if (result.status === ProviderStatus.Error) {
this.logger(
this.logger.warn(
`Error from provider #${pIndex}.${
!isLastProvider ? ' Triggering next provider.' : ''
}`,
@ -300,14 +303,14 @@ export class HyperlaneSmartProvider
): Promise<ProviderPerformResult> {
try {
if (this.options?.debug)
this.logger(
this.logger.debug(
`Provider #${pIndex} performing method ${method} for reqId ${reqId}`,
);
const result = await provider.perform(method, params, reqId);
return { status: ProviderStatus.Success, value: result };
} catch (error) {
if (this.options?.debug)
this.logger(
this.logger.error(
`Error performing ${method} on provider #${pIndex} for reqId ${reqId}`,
error,
);
@ -353,7 +356,7 @@ export class HyperlaneSmartProvider
errors: unknown[],
fallbackMsg: string,
): void {
this.logger(fallbackMsg);
this.logger.error(fallbackMsg);
// TODO inspect the errors in some clever way to choose which to throw
if (errors.length > 0) throw errors[0];
else throw new Error(fallbackMsg);

@ -24,7 +24,7 @@ export abstract class GasRouterDeployer<
): Promise<void> {
await super.enrollRemoteRouters(contractsMap, configMap, foreignRouters);
this.logger(`Setting enrolled router destination gas...`);
this.logger.debug(`Setting enrolled router destination gas...`);
for (const [chain, contracts] of Object.entries(contractsMap)) {
const remoteDomains = await this.router(contracts).domains();
const remoteChains = remoteDomains.map((domain) =>
@ -45,7 +45,7 @@ export abstract class GasRouterDeployer<
continue;
}
this.logger(`Set destination gas on ${chain} for ${remoteChains}`);
this.logger.debug(`Set destination gas on ${chain} for ${remoteChains}`);
await this.multiProvider.handleTx(
chain,
this.router(contracts)['setDestinationGas((uint32,uint256)[])'](

@ -39,7 +39,7 @@ export abstract class HyperlaneRouterDeployer<
_: ChainMap<Config>,
foreignRouters: ChainMap<Address> = {},
): Promise<void> {
this.logger(
this.logger.debug(
`Enrolling deployed routers with each other (if not already)...`,
);
@ -80,7 +80,7 @@ export abstract class HyperlaneRouterDeployer<
await super.runIfOwner(chain, this.router(contracts), async () => {
const chains = domains.map((id) => this.multiProvider.getChainName(id));
this.logger(
this.logger.debug(
`Enrolling remote routers (${chains.join(', ')}) on ${chain}`,
);
await this.multiProvider.handleTx(
@ -99,7 +99,7 @@ export abstract class HyperlaneRouterDeployer<
contractsMap: HyperlaneContractsMap<Factories>,
configMap: ChainMap<Config>,
): Promise<void> {
this.logger(`Transferring ownership of ownables...`);
this.logger.debug(`Transferring ownership of ownables...`);
for (const chain of Object.keys(contractsMap)) {
const contracts = contractsMap[chain];
const ownables = (await filterOwnableContracts(contracts)) as Partial<
@ -138,7 +138,7 @@ export abstract class HyperlaneRouterDeployer<
);
await this.configureClients(deployedContractsMap, configMap);
await this.transferOwnership(deployedContractsMap, configMap);
this.logger(`Finished deploying router contracts for all chains.`);
this.logger.debug(`Finished deploying router contracts for all chains.`);
return deployedContractsMap;
}

@ -63,10 +63,12 @@ export abstract class ProxiedRouterDeployer<
}
await super.runIfOwner(chain, proxyAdmin, async () => {
this.logger(`Checking ownership of proxy admin to ${adminOwner}`);
this.logger.debug(`Checking ownership of proxy admin to ${adminOwner}`);
if (!eqAddress(await proxyAdmin.owner(), adminOwner)) {
this.logger(`Transferring ownership of proxy admin to ${adminOwner}`);
this.logger.debug(
`Transferring ownership of proxy admin to ${adminOwner}`,
);
return this.multiProvider.handleTx(
chain,
proxyAdmin.transferOwnership(adminOwner),

@ -1,5 +1,5 @@
import debug from 'debug';
import type { BigNumber } from 'ethers';
import { Logger } from 'pino';
import { GasRouter, Router } from '@hyperlane-xyz/core';
import {
@ -26,7 +26,7 @@ export abstract class RouterApp<
constructor(
contractsMap: HyperlaneContractsMap<Factories>,
multiProvider: MultiProvider,
logger?: debug.Debugger,
logger?: Logger,
readonly foreignDeployments: ChainMap<Address> = {},
) {
super(contractsMap, multiProvider, logger);

@ -24,7 +24,6 @@ const PROTOCOL_TO_TX_HASH: Record<ProtocolType, Address> = {
[ProtocolType.Fuel]: '',
};
// Note: run with DEBUG=hyperlane for more detailed logs
async function main() {
const results: ChainMap<{
goodRpcs: number;

@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import debug from 'debug';
import { providers } from 'ethers';
import {
@ -11,7 +10,7 @@ import {
HypERC721Collateral,
HypNative,
} from '@hyperlane-xyz/core';
import { objKeys, objMap } from '@hyperlane-xyz/utils';
import { objKeys, objMap, rootLogger } from '@hyperlane-xyz/utils';
import { HyperlaneContracts } from '../contracts/types';
import { ContractVerifier } from '../deploy/verify/ContractVerifier';
@ -59,7 +58,7 @@ export class HypERC20Deployer extends GasRouterDeployer<
contractVerifier?: ContractVerifier,
) {
super(multiProvider, hypERC20factories, {
logger: debug('hyperlane:HypERC20Deployer'),
logger: rootLogger.child({ module: 'HypERC20Deployer' }),
ismFactory,
contractVerifier,
}); // factories not used in deploy
@ -190,7 +189,7 @@ export class HypERC20Deployer extends GasRouterDeployer<
if (!e.message.includes('already initialized')) {
throw e;
}
this.logger(`${config.type} already initialized`);
this.logger.debug(`${config.type} already initialized`);
}
return router;
}
@ -288,7 +287,7 @@ export class HypERC721Deployer extends GasRouterDeployer<
contractVerifier?: ContractVerifier,
) {
super(multiProvider, hypERC721factories, {
logger: debug('hyperlane:HypERC721Deployer'),
logger: rootLogger.child({ module: 'HypERC721Deployer' }),
contractVerifier,
});
}

@ -1,4 +1,4 @@
import debug, { Debugger } from 'debug';
import { Logger } from 'pino';
import {
Address,
@ -9,6 +9,7 @@ import {
convertToProtocolAddress,
isValidAddress,
isZeroishAddress,
rootLogger,
} from '@hyperlane-xyz/utils';
import { MultiProtocolProvider } from '../providers/MultiProtocolProvider';
@ -37,7 +38,7 @@ import {
} from './types';
export interface WarpCoreOptions {
loggerName?: string;
logger?: Logger;
localFeeConstants?: FeeConstantConfig;
interchainFeeConstants?: FeeConstantConfig;
routeBlacklist?: RouteBlacklist;
@ -49,7 +50,7 @@ export class WarpCore {
public readonly localFeeConstants: FeeConstantConfig;
public readonly interchainFeeConstants: FeeConstantConfig;
public readonly routeBlacklist: RouteBlacklist;
public readonly logger: Debugger;
public readonly logger: Logger;
constructor(
multiProvider: MultiProtocolProvider<{ mailbox?: Address }>,
@ -61,7 +62,11 @@ export class WarpCore {
this.localFeeConstants = options?.localFeeConstants || [];
this.interchainFeeConstants = options?.interchainFeeConstants || [];
this.routeBlacklist = options?.routeBlacklist || [];
this.logger = debug(options?.loggerName || 'hyperlane:WarpCore');
this.logger =
options?.logger ||
rootLogger.child({
module: 'WarpCore',
});
}
/**
@ -119,7 +124,7 @@ export class WarpCore {
originToken: IToken;
destination: ChainNameOrId;
}): Promise<TokenAmount> {
this.logger(`Fetching interchain transfer quote to ${destination}`);
this.logger.debug(`Fetching interchain transfer quote to ${destination}`);
const { chainName: originName } = originToken;
const destinationName = this.multiProvider.getChainName(destination);
@ -158,7 +163,7 @@ export class WarpCore {
igpToken = searchResult;
}
this.logger(
this.logger.debug(
`Quoted interchain transfer fee: ${gasAmount} ${igpToken.symbol}`,
);
return new TokenAmount(gasAmount, igpToken);
@ -264,12 +269,12 @@ export class WarpCore {
const hypAdapter = token.getHypAdapter(this.multiProvider, destinationName);
if (await this.isApproveRequired({ originTokenAmount, owner: sender })) {
this.logger(`Approval required for transfer of ${token.symbol}`);
this.logger.info(`Approval required for transfer of ${token.symbol}`);
const approveTxReq = await hypAdapter.populateApproveTx({
weiAmountOrId: amount.toString(),
recipient: token.addressOrDenom,
});
this.logger(`Approval tx for ${token.symbol} populated`);
this.logger.debug(`Approval tx for ${token.symbol} populated`);
const approveTx = {
category: WarpTxCategory.Approval,
@ -296,7 +301,7 @@ export class WarpCore {
addressOrDenom: interchainFee.token.addressOrDenom,
},
});
this.logger(`Remote transfer tx for ${token.symbol} populated`);
this.logger.debug(`Remote transfer tx for ${token.symbol} populated`);
const transferTx = {
category: WarpTxCategory.Transfer,
@ -322,7 +327,7 @@ export class WarpCore {
sender: Address;
senderPubKey?: HexString;
}): Promise<WarpCoreFeeEstimate> {
this.logger('Fetching remote transfer fee estimates');
this.logger.debug('Fetching remote transfer fee estimates');
// First get interchain gas quote (aka IGP quote)
// Start with this because it's used in the local fee estimation
@ -412,7 +417,7 @@ export class WarpCore {
}): Promise<boolean> {
const { token: originToken, amount } = originTokenAmount;
const destinationName = this.multiProvider.getChainName(destination);
this.logger(
this.logger.debug(
`Checking collateral for ${originToken.symbol} to ${destination}`,
);
@ -421,7 +426,9 @@ export class WarpCore {
assert(destinationToken, `No connection found for ${destinationName}`);
if (!TOKEN_COLLATERALIZED_STANDARDS.includes(destinationToken.standard)) {
this.logger(`${destinationToken.symbol} is not collateralized, skipping`);
this.logger.debug(
`${destinationToken.symbol} is not collateralized, skipping`,
);
return true;
}
@ -436,7 +443,7 @@ export class WarpCore {
);
const isSufficient = BigInt(destinationBalanceInOriginDecimals) >= amount;
this.logger(
this.logger.debug(
`${originTokenAmount.token.symbol} to ${destination} has ${
isSufficient ? 'sufficient' : 'INSUFFICIENT'
} collateral`,
@ -461,7 +468,7 @@ export class WarpCore {
token.addressOrDenom,
amount,
);
this.logger(
this.logger.info(
`Approval is${isRequired ? '' : ' not'} required for transfer of ${
token.symbol
}`,
@ -552,10 +559,10 @@ export class WarpCore {
// Also ensure the address denom is correct if the dest protocol is Cosmos
if (protocol === ProtocolType.Cosmos) {
if (!bech32Prefix) {
this.logger(`No bech32 prefix found for chain ${destination}`);
this.logger.error(`No bech32 prefix found for chain ${destination}`);
return { destination: 'Invalid chain data' };
} else if (!recipient.startsWith(bech32Prefix)) {
this.logger(`Recipient prefix should be ${bech32Prefix}`);
this.logger.error(`Recipient prefix should be ${bech32Prefix}`);
return { recipient: 'Invalid recipient prefix' };
}
}

@ -73,9 +73,9 @@ export {
isS3CheckpointWithId,
} from './src/checkpoints';
export { domainHash } from './src/domains';
export { safelyAccessEnvVar } from './src/env';
export { envVarToBoolean, safelyAccessEnvVar } from './src/env';
export { canonizeId, evmId } from './src/ids';
export { debug, error, log, trace, warn } from './src/logging';
export { isLogPretty, rootLogger } from './src/logging';
export { mean, median, stdDev, sum } from './src/math';
export { formatMessage, messageId, parseMessage } from './src/messages';
export {

@ -6,7 +6,8 @@
"@cosmjs/encoding": "^0.31.3",
"@solana/web3.js": "^1.78.0",
"bignumber.js": "^9.1.1",
"ethers": "^5.7.2"
"ethers": "^5.7.2",
"pino": "^8.19.0"
},
"devDependencies": {
"@types/mocha": "^10.0.1",

@ -1,11 +1,11 @@
import { log } from './logging';
import { rootLogger } from './logging';
export function toBase64(data: any): string | undefined {
try {
if (!data) throw new Error('No data to encode');
return btoa(JSON.stringify(data));
} catch (error) {
log('Unable to serialize + encode data to base64', data);
rootLogger.error('Unable to serialize + encode data to base64', data);
return undefined;
}
}
@ -16,7 +16,7 @@ export function fromBase64<T>(data: string | string[]): T | undefined {
const msg = Array.isArray(data) ? data[0] : data;
return JSON.parse(atob(msg));
} catch (error) {
log('Unable to decode + deserialize data from base64', data);
rootLogger.error('Unable to decode + deserialize data from base64', data);
return undefined;
}
}

@ -7,3 +7,10 @@ export function safelyAccessEnvVar(name: string) {
return undefined;
}
}
export function envVarToBoolean(value: any) {
if (typeof value === 'boolean') return value;
if (typeof value === 'string') return value.toLowerCase() === 'true';
if (typeof value === 'number') return value !== 0;
return !!value;
}

@ -1,47 +1,38 @@
import { safelyAccessEnvVar } from './env';
import { LevelWithSilent, pino } from 'pino';
/* eslint-disable no-console */
type LOG_LEVEL = 'none' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
import { envVarToBoolean, safelyAccessEnvVar } from './env';
const ENV_LOG_LEVEL = (
safelyAccessEnvVar('LOG_LEVEL') ?? 'debug'
).toLowerCase() as LOG_LEVEL;
const LOG_TRACE = ENV_LOG_LEVEL == 'trace';
const LOG_DEBUG = LOG_TRACE || ENV_LOG_LEVEL == 'debug';
const LOG_INFO = LOG_DEBUG || ENV_LOG_LEVEL == 'info';
const LOG_WARN = LOG_INFO || ENV_LOG_LEVEL == 'warn';
const LOG_ERROR = LOG_WARN || ENV_LOG_LEVEL == 'error';
export function trace(message: string, data?: any) {
if (LOG_TRACE) logWithFunction(console.trace, 'trace', message, data);
let logLevel: LevelWithSilent = 'info';
const envLogLevel = safelyAccessEnvVar('LOG_LEVEL')?.toLowerCase();
if (envLogLevel && pino.levels.values[envLogLevel]) {
logLevel = envLogLevel as LevelWithSilent;
}
export function debug(message: string, data?: any) {
if (LOG_DEBUG) logWithFunction(console.debug, 'debug', message, data);
// For backwards compat and also to match agent level options
else if (envLogLevel === 'none' || envLogLevel === 'off') {
logLevel = 'silent';
}
export function log(message: string, data?: any) {
if (LOG_INFO) logWithFunction(console.log, 'info', message, data);
}
export function warn(message: string, data?: any) {
if (LOG_WARN) logWithFunction(console.warn, 'warn', message, data);
}
export function error(message: string, data?: any) {
if (LOG_ERROR) logWithFunction(console.error, 'error', message, data);
}
function logWithFunction(
logFn: (...contents: any[]) => void,
level: LOG_LEVEL,
message: string,
data?: any,
) {
const fullLog = {
...data,
level,
message,
};
logFn(JSON.stringify(fullLog));
}
export const isLogPretty = envVarToBoolean(safelyAccessEnvVar('LOG_PRETTY'));
export const rootLogger = pino({
level: logLevel,
name: 'hyperlane',
formatters: {
// Remove pino's default bindings of hostname but keep pid
bindings: (defaultBindings) => ({ pid: defaultBindings.pid }),
},
hooks: {
logMethod(inputArgs, method, level) {
// Pino has no simple way of setting custom log shapes and they
// recommend against using pino-pretty in production so when
// pretty is enabled we circumvent pino and log directly to console
if (isLogPretty && level >= pino.levels.values[logLevel]) {
// eslint-disable-next-line no-console
console.log(...inputArgs);
// Then return null to prevent pino from logging
return null;
}
return method.apply(this, inputArgs);
},
},
});

@ -5125,7 +5125,6 @@ __metadata:
"@solana/spl-token": "npm:^0.3.8"
"@solana/web3.js": "npm:^1.78.0"
"@types/coingecko-api": "npm:^1.0.10"
"@types/debug": "npm:^4.1.7"
"@types/mocha": "npm:^10.0.1"
"@types/node": "npm:^16.9.1"
"@types/sinon": "npm:^17.0.1"
@ -5137,13 +5136,13 @@ __metadata:
coingecko-api: "npm:^1.0.10"
cosmjs-types: "npm:^0.9.0"
cross-fetch: "npm:^3.1.5"
debug: "npm:^4.3.4"
dotenv: "npm:^10.0.0"
eslint: "npm:^8.43.0"
ethereum-waffle: "npm:^4.0.10"
ethers: "npm:^5.7.2"
hardhat: "npm:^2.19.0"
mocha: "npm:^10.2.0"
pino: "npm:^8.19.0"
prettier: "npm:^2.8.8"
sinon: "npm:^13.0.2"
ts-node: "npm:^10.8.0"
@ -5180,6 +5179,7 @@ __metadata:
chai: "npm:^4.3.6"
ethers: "npm:^5.7.2"
mocha: "npm:^10.2.0"
pino: "npm:^8.19.0"
prettier: "npm:^2.8.8"
typescript: "npm:5.1.6"
languageName: unknown
@ -9161,6 +9161,15 @@ __metadata:
languageName: node
linkType: hard
"abort-controller@npm:^3.0.0":
version: 3.0.0
resolution: "abort-controller@npm:3.0.0"
dependencies:
event-target-shim: "npm:^5.0.0"
checksum: ed84af329f1828327798229578b4fe03a4dd2596ba304083ebd2252666bdc1d7647d66d0b18704477e1f8aa315f055944aa6e859afebd341f12d0a53c37b4b40
languageName: node
linkType: hard
"abortcontroller-polyfill@npm:^1.7.3":
version: 1.7.3
resolution: "abortcontroller-polyfill@npm:1.7.3"
@ -13018,6 +13027,13 @@ __metadata:
languageName: node
linkType: hard
"event-target-shim@npm:^5.0.0":
version: 5.0.1
resolution: "event-target-shim@npm:5.0.1"
checksum: 49ff46c3a7facbad3decb31f597063e761785d7fdb3920d4989d7b08c97a61c2f51183e2f3a03130c9088df88d4b489b1b79ab632219901f184f85158508f4c8
languageName: node
linkType: hard
"eventemitter3@npm:4.0.4":
version: 4.0.4
resolution: "eventemitter3@npm:4.0.4"
@ -13313,6 +13329,13 @@ __metadata:
languageName: node
linkType: hard
"fast-redact@npm:^3.1.1":
version: 3.5.0
resolution: "fast-redact@npm:3.5.0"
checksum: 24b27e2023bd5a62f908d97a753b1adb8d89206b260f97727728e00b693197dea2fc2aa3711147a385d0ec6e713569fd533df37a4ef947e08cb65af3019c7ad5
languageName: node
linkType: hard
"fast-safe-stringify@npm:^2.0.6":
version: 2.1.1
resolution: "fast-safe-stringify@npm:2.1.1"
@ -18739,6 +18762,13 @@ __metadata:
languageName: node
linkType: hard
"on-exit-leak-free@npm:^2.1.0":
version: 2.1.2
resolution: "on-exit-leak-free@npm:2.1.2"
checksum: f7b4b7200026a08f6e4a17ba6d72e6c5cbb41789ed9cf7deaf9d9e322872c7dc5a7898549a894651ee0ee9ae635d34a678115bf8acdfba8ebd2ba2af688b563c
languageName: node
linkType: hard
"on-finished@npm:2.4.1":
version: 2.4.1
resolution: "on-finished@npm:2.4.1"
@ -19189,6 +19219,16 @@ __metadata:
languageName: node
linkType: hard
"pino-abstract-transport@npm:v1.1.0":
version: 1.1.0
resolution: "pino-abstract-transport@npm:1.1.0"
dependencies:
readable-stream: "npm:^4.0.0"
split2: "npm:^4.0.0"
checksum: 39b4496c9e4289e8d44a1d01adfa8dfeebb374e14b7a6451a4f3713561aeb9e181c64ff0272921667abcb95aceb312ab2761b82e253db23a456ab3dd35a42675
languageName: node
linkType: hard
"pino-std-serializers@npm:^4.0.0":
version: 4.0.0
resolution: "pino-std-serializers@npm:4.0.0"
@ -19196,6 +19236,13 @@ __metadata:
languageName: node
linkType: hard
"pino-std-serializers@npm:^6.0.0":
version: 6.2.2
resolution: "pino-std-serializers@npm:6.2.2"
checksum: a00cdff4e1fbc206da9bed047e6dc400b065f43e8b4cef1635b0192feab0e8f932cdeb0faaa38a5d93d2e777ba4cda939c2ed4c1a70f6839ff25f9aef97c27ff
languageName: node
linkType: hard
"pino@npm:7.11.0":
version: 7.11.0
resolution: "pino@npm:7.11.0"
@ -19217,6 +19264,27 @@ __metadata:
languageName: node
linkType: hard
"pino@npm:^8.19.0":
version: 8.19.0
resolution: "pino@npm:8.19.0"
dependencies:
atomic-sleep: "npm:^1.0.0"
fast-redact: "npm:^3.1.1"
on-exit-leak-free: "npm:^2.1.0"
pino-abstract-transport: "npm:v1.1.0"
pino-std-serializers: "npm:^6.0.0"
process-warning: "npm:^3.0.0"
quick-format-unescaped: "npm:^4.0.3"
real-require: "npm:^0.2.0"
safe-stable-stringify: "npm:^2.3.1"
sonic-boom: "npm:^3.7.0"
thread-stream: "npm:^2.0.0"
bin:
pino: bin.js
checksum: c98e8bedb7c9eca5c0e75c2dd910a58b0e470da282c5a4787873a591666cc7cce33561d9ba6d6a20cf6bc4bc8d15b7db84cf6156f262081a5c6b8de134285789
languageName: node
linkType: hard
"pirates@npm:^4.0.4":
version: 4.0.6
resolution: "pirates@npm:4.0.6"
@ -19429,6 +19497,13 @@ __metadata:
languageName: node
linkType: hard
"process-warning@npm:^3.0.0":
version: 3.0.0
resolution: "process-warning@npm:3.0.0"
checksum: 2d82fa641e50a5789eaf0f2b33453760996e373d4591aac576a22d696186ab7e240a0592db86c264d4f28a46c2abbe9b94689752017db7dadc90f169f12b0924
languageName: node
linkType: hard
"process@npm:^0.11.10":
version: 0.11.10
resolution: "process@npm:0.11.10"
@ -19997,6 +20072,19 @@ __metadata:
languageName: node
linkType: hard
"readable-stream@npm:^4.0.0":
version: 4.5.2
resolution: "readable-stream@npm:4.5.2"
dependencies:
abort-controller: "npm:^3.0.0"
buffer: "npm:^6.0.3"
events: "npm:^3.3.0"
process: "npm:^0.11.10"
string_decoder: "npm:^1.3.0"
checksum: 01b128a559c5fd76a898495f858cf0a8839f135e6a69e3409f986e88460134791657eb46a2ff16826f331682a3c4d0c5a75cef5e52ef259711021ba52b1c2e82
languageName: node
linkType: hard
"readdirp@npm:~3.2.0":
version: 3.2.0
resolution: "readdirp@npm:3.2.0"
@ -20029,6 +20117,13 @@ __metadata:
languageName: node
linkType: hard
"real-require@npm:^0.2.0":
version: 0.2.0
resolution: "real-require@npm:0.2.0"
checksum: ddf44ee76301c774e9c9f2826da8a3c5c9f8fc87310f4a364e803ef003aa1a43c378b4323051ced212097fff1af459070f4499338b36a7469df1d4f7e8c0ba4c
languageName: node
linkType: hard
"rechoir@npm:^0.6.2":
version: 0.6.2
resolution: "rechoir@npm:0.6.2"
@ -20576,7 +20671,7 @@ __metadata:
languageName: node
linkType: hard
"safe-stable-stringify@npm:^2.1.0":
"safe-stable-stringify@npm:^2.1.0, safe-stable-stringify@npm:^2.3.1":
version: 2.4.3
resolution: "safe-stable-stringify@npm:2.4.3"
checksum: a6c192bbefe47770a11072b51b500ed29be7b1c15095371c1ee1dc13e45ce48ee3c80330214c56764d006c485b88bd0b24940d868948170dddc16eed312582d8
@ -21209,6 +21304,15 @@ __metadata:
languageName: node
linkType: hard
"sonic-boom@npm:^3.7.0":
version: 3.8.0
resolution: "sonic-boom@npm:3.8.0"
dependencies:
atomic-sleep: "npm:^1.0.0"
checksum: 470a82cb1af3ab99fcd3003bbecb2ce79a6b243d0f6012c59e5f567f71cbe039c8cd810752748b5820ee20d72c8da81aa298e510eec9e41a4ca05c7f419825ff
languageName: node
linkType: hard
"source-map-js@npm:^1.0.2":
version: 1.0.2
resolution: "source-map-js@npm:1.0.2"
@ -21570,7 +21674,7 @@ __metadata:
languageName: node
linkType: hard
"string_decoder@npm:^1.1.1":
"string_decoder@npm:^1.1.1, string_decoder@npm:^1.3.0":
version: 1.3.0
resolution: "string_decoder@npm:1.3.0"
dependencies:
@ -22016,6 +22120,15 @@ __metadata:
languageName: node
linkType: hard
"thread-stream@npm:^2.0.0":
version: 2.4.1
resolution: "thread-stream@npm:2.4.1"
dependencies:
real-require: "npm:^0.2.0"
checksum: baac5bf555912f216a2494bf3f66377733a843306cddd233b1c5ad63084307266f61af35d6122e3936c657836d5db4a14da34300cd25930e013943b807a29c9b
languageName: node
linkType: hard
"through@npm:>=2.2.7 <3, through@npm:^2.3.8":
version: 2.3.8
resolution: "through@npm:2.3.8"

Loading…
Cancel
Save