diff --git a/.changeset/nasty-olives-hide.md b/.changeset/nasty-olives-hide.md new file mode 100644 index 00000000000..c6f06443b3f --- /dev/null +++ b/.changeset/nasty-olives-hide.md @@ -0,0 +1,8 @@ +--- +'@hyperlane-xyz/rebalancer': patch +'@hyperlane-xyz/deploy-sdk': patch +'@hyperlane-xyz/utils': minor +'@hyperlane-xyz/cli': patch +--- + +Changed `readJson()` and `readYamlOrJson()` to return `T | null` on empty JSON files, for consistency with YAML behavior and simplified error handling. All call sites updated with explicit null checks using `assert()` or null coalescing. diff --git a/typescript/cli/src/commands/core.ts b/typescript/cli/src/commands/core.ts index ce4836f705e..a938e2004a1 100644 --- a/typescript/cli/src/commands/core.ts +++ b/typescript/cli/src/commands/core.ts @@ -7,7 +7,7 @@ import { DeployedCoreAddressesSchema, normalizeConfig, } from '@hyperlane-xyz/sdk'; -import { diffObjMerge } from '@hyperlane-xyz/utils'; +import { assert, diffObjMerge } from '@hyperlane-xyz/utils'; import { createCoreDeployConfig, @@ -238,7 +238,8 @@ export const check: CommandModuleWithContext<{ handler: async ({ context, chain, mailbox, config: configFilePath }) => { logCommandHeader('Hyperlane Core Check'); - const expectedCoreConfig: CoreConfig = await readYamlOrJson(configFilePath); + const expectedCoreConfig = await readYamlOrJson(configFilePath); + assert(expectedCoreConfig, `Empty core config file at ${configFilePath}`); const onChainCoreConfig = await executeCoreRead({ context, chain, diff --git a/typescript/cli/src/config/submit.ts b/typescript/cli/src/config/submit.ts index 21268963a34..d669a89ce77 100644 --- a/typescript/cli/src/config/submit.ts +++ b/typescript/cli/src/config/submit.ts @@ -4,7 +4,7 @@ import { type AnnotatedEV5Transaction, type ChainName, } from '@hyperlane-xyz/sdk'; -import { type ProtocolType, errorToString } from '@hyperlane-xyz/utils'; +import { type ProtocolType, assert, errorToString } from '@hyperlane-xyz/utils'; import { type WriteCommandContext } from '../context/types.js'; import { getSubmitterByStrategy } from '../deploy/warp.js'; @@ -58,5 +58,12 @@ export async function runSubmit({ export function getTransactions( transactionsFilepath: string, ): AnnotatedEV5Transaction[] { - return readYamlOrJson(transactionsFilepath.trim()); + const transactions = readYamlOrJson( + transactionsFilepath.trim(), + ); + assert( + transactions, + `Empty transactions file at ${transactionsFilepath.trim()}`, + ); + return transactions; } diff --git a/typescript/cli/src/fork/fork.ts b/typescript/cli/src/fork/fork.ts index 77c8408a803..a750d77649f 100644 --- a/typescript/cli/src/fork/fork.ts +++ b/typescript/cli/src/fork/fork.ts @@ -54,9 +54,15 @@ export async function runForkCommand({ ); let port = basePort; + // Wrapper to handle null returns from readYamlOrJson + const readYamlOrJsonNonNull = (path: string): T => { + const result = readYamlOrJson(path); + assert(result, `Empty config file at ${path}`); + return result; + }; const parsedForkConfig = forkedChainConfigByChainFromRaw( forkConfig, - readYamlOrJson, + readYamlOrJsonNonNull, ); const chainMetadataOverrides: ChainMap<{ blocks: ChainMetadata['blocks']; diff --git a/typescript/cli/src/submitters/EV5FileSubmitter.ts b/typescript/cli/src/submitters/EV5FileSubmitter.ts index 43685fd40ca..3dd99a98b24 100644 --- a/typescript/cli/src/submitters/EV5FileSubmitter.ts +++ b/typescript/cli/src/submitters/EV5FileSubmitter.ts @@ -8,7 +8,6 @@ import { import { type Annotated, type ProtocolType, - assert, rootLogger, } from '@hyperlane-xyz/utils'; @@ -60,15 +59,15 @@ export class EV5FileSubmitter const allTxs = [...txs]; // Attempt to append transactions to existing filepath. - try { - const maybeExistingTxs = readYamlOrJson(filepath); // Can throw if file is empty - assert( - Array.isArray(maybeExistingTxs), - `Target filepath ${filepath} has existing data, but is not an array. Overwriting.`, - ); - allTxs.unshift(...maybeExistingTxs); - } catch (e) { - this.logger.debug(`Invalid transactions read from ${filepath}: ${e}`); + const maybeExistingTxs = readYamlOrJson(filepath); + if (maybeExistingTxs !== null) { + if (!Array.isArray(maybeExistingTxs)) { + this.logger.debug( + `Target filepath ${filepath} has existing data, but is not an array. Overwriting.`, + ); + } else { + allTxs.unshift(...maybeExistingTxs); + } } writeYamlOrJson(filepath, allTxs); diff --git a/typescript/cli/src/tests/aleo/core/core-apply-hooks.e2e-test.ts b/typescript/cli/src/tests/aleo/core/core-apply-hooks.e2e-test.ts index 6623ec4f689..d0d57a0b958 100644 --- a/typescript/cli/src/tests/aleo/core/core-apply-hooks.e2e-test.ts +++ b/typescript/cli/src/tests/aleo/core/core-apply-hooks.e2e-test.ts @@ -3,7 +3,10 @@ import { expect } from 'chai'; import { type CoreConfig, type HookConfig, HookType } from '@hyperlane-xyz/sdk'; import { ProtocolType, normalizeConfig } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { CORE_CONFIG_PATH_BY_PROTOCOL, @@ -28,7 +31,7 @@ describe('hyperlane core apply hooks (Aleo E2E tests)', async function () { // Reset the core deploy config before each test beforeEach(async function () { - const coreConfig: CoreConfig = await readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( CORE_CONFIG_PATH_BY_PROTOCOL.aleo, ); writeYamlOrJson( @@ -74,7 +77,7 @@ describe('hyperlane core apply hooks (Aleo E2E tests)', async function () { for (const hookConfig of Object.values(testCases)) { for (const hookField of hookFields) { it(`should update the ${hookField} to a ${hookConfig.type}`, async () => { - const coreConfig: CoreConfig = await readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( CORE_CONFIG_PATH_BY_PROTOCOL.aleo, ); diff --git a/typescript/cli/src/tests/aleo/core/core-apply-ism.e2e-test.ts b/typescript/cli/src/tests/aleo/core/core-apply-ism.e2e-test.ts index 88f5fe5c30d..4afd4bec39f 100644 --- a/typescript/cli/src/tests/aleo/core/core-apply-ism.e2e-test.ts +++ b/typescript/cli/src/tests/aleo/core/core-apply-ism.e2e-test.ts @@ -8,7 +8,10 @@ import { } from '@hyperlane-xyz/sdk'; import { ProtocolType, normalizeConfig } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { CORE_CONFIG_PATH_BY_PROTOCOL, @@ -33,7 +36,7 @@ describe('hyperlane core apply ism (Aleo E2E tests)', async function () { // Reset the core deploy config before each test beforeEach(async function () { - const coreConfig: CoreConfig = await readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( CORE_CONFIG_PATH_BY_PROTOCOL.aleo, ); writeYamlOrJson( @@ -70,7 +73,7 @@ describe('hyperlane core apply ism (Aleo E2E tests)', async function () { for (const ismConfig of Object.values(testCases)) { it(`should update the defaultIsm to a ${ismConfig.type}`, async () => { - const coreConfig: CoreConfig = await readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( CORE_CONFIG_PATH_BY_PROTOCOL.aleo, ); diff --git a/typescript/cli/src/tests/aleo/core/core-apply-mailbox.e2e-test.ts b/typescript/cli/src/tests/aleo/core/core-apply-mailbox.e2e-test.ts index 3957393e2d8..1ae6b411184 100644 --- a/typescript/cli/src/tests/aleo/core/core-apply-mailbox.e2e-test.ts +++ b/typescript/cli/src/tests/aleo/core/core-apply-mailbox.e2e-test.ts @@ -3,7 +3,10 @@ import { expect } from 'chai'; import { type CoreConfig } from '@hyperlane-xyz/sdk'; import { ProtocolType } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { BURN_ADDRESS_BY_PROTOCOL, @@ -28,7 +31,7 @@ describe('hyperlane core apply mailbox (Aleo E2E tests)', async function () { // Reset the core deploy config before each test beforeEach(async function () { - const coreConfig: CoreConfig = await readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( CORE_CONFIG_PATH_BY_PROTOCOL.aleo, ); writeYamlOrJson( @@ -44,7 +47,7 @@ describe('hyperlane core apply mailbox (Aleo E2E tests)', async function () { }); it(`should update the mailbox owner to the specified one`, async () => { - const coreConfig: CoreConfig = await readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( CORE_CONFIG_PATH_BY_PROTOCOL.aleo, ); diff --git a/typescript/cli/src/tests/aleo/core/core-deploy.e2e-test.ts b/typescript/cli/src/tests/aleo/core/core-deploy.e2e-test.ts index 61515345be0..fb528c830a8 100644 --- a/typescript/cli/src/tests/aleo/core/core-deploy.e2e-test.ts +++ b/typescript/cli/src/tests/aleo/core/core-deploy.e2e-test.ts @@ -13,7 +13,10 @@ import { normalizeAddressEvm, } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { BURN_ADDRESS_BY_PROTOCOL, @@ -136,7 +139,7 @@ describe('hyperlane core deploy (Aleo E2E tests)', async function () { for (const { description, expect } of testCases) { it(description, async () => { - const coreConfig: CoreConfig = await readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( CORE_CONFIG_PATH_BY_PROTOCOL.aleo, ); diff --git a/typescript/cli/src/tests/aleo/warp/warp-apply-hook-updates.e2e-test.ts b/typescript/cli/src/tests/aleo/warp/warp-apply-hook-updates.e2e-test.ts index 1e9f01c8bf0..cbce8a9b82b 100644 --- a/typescript/cli/src/tests/aleo/warp/warp-apply-hook-updates.e2e-test.ts +++ b/typescript/cli/src/tests/aleo/warp/warp-apply-hook-updates.e2e-test.ts @@ -2,7 +2,7 @@ import { type ChainAddresses } from '@hyperlane-xyz/registry'; import { TokenType } from '@hyperlane-xyz/sdk'; import { ProtocolType, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson } from '../../../utils/files.js'; +import { readYamlOrJsonOrThrow } from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../commands/warp.js'; import { @@ -61,7 +61,7 @@ describe('hyperlane warp apply Hook updates (Aleo E2E tests)', async function () before(async function () { await hyperlaneCore1.deploy(HYP_KEY_BY_PROTOCOL.aleo); - chain1CoreAddress = readYamlOrJson( + chain1CoreAddress = readYamlOrJsonOrThrow( `${REGISTRY_PATH}/chains/${TEST_CHAIN_NAMES_BY_PROTOCOL.aleo.CHAIN_NAME_1}/addresses.yaml`, ); }); diff --git a/typescript/cli/src/tests/aleo/warp/warp-apply-ism-updates.e2e-test.ts b/typescript/cli/src/tests/aleo/warp/warp-apply-ism-updates.e2e-test.ts index ddaafb1c1cb..3900a675069 100644 --- a/typescript/cli/src/tests/aleo/warp/warp-apply-ism-updates.e2e-test.ts +++ b/typescript/cli/src/tests/aleo/warp/warp-apply-ism-updates.e2e-test.ts @@ -2,7 +2,7 @@ import { type ChainAddresses } from '@hyperlane-xyz/registry'; import { TokenType } from '@hyperlane-xyz/sdk'; import { ProtocolType, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson } from '../../../utils/files.js'; +import { readYamlOrJsonOrThrow } from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../commands/warp.js'; import { @@ -61,7 +61,7 @@ describe('hyperlane warp apply ISM updates (Aleo E2E tests)', async function () before(async function () { await hyperlaneCore1.deploy(HYP_KEY_BY_PROTOCOL.aleo); - chain1CoreAddress = readYamlOrJson( + chain1CoreAddress = readYamlOrJsonOrThrow( `${REGISTRY_PATH}/chains/${TEST_CHAIN_NAMES_BY_PROTOCOL.aleo.CHAIN_NAME_1}/addresses.yaml`, ); }); diff --git a/typescript/cli/src/tests/aleo/warp/warp-apply-ownership-updates.e2e-test.ts b/typescript/cli/src/tests/aleo/warp/warp-apply-ownership-updates.e2e-test.ts index 04c7b660be5..5c047e5c119 100644 --- a/typescript/cli/src/tests/aleo/warp/warp-apply-ownership-updates.e2e-test.ts +++ b/typescript/cli/src/tests/aleo/warp/warp-apply-ownership-updates.e2e-test.ts @@ -8,7 +8,10 @@ import { } from '@hyperlane-xyz/sdk'; import { type Address, ProtocolType, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../commands/warp.js'; import { @@ -78,10 +81,10 @@ describe('hyperlane warp apply ownership (Aleo E2E tests)', async function () { hyperlaneCore2.deploy(HYP_KEY_BY_PROTOCOL.aleo), ]); - chain1CoreAddress = readYamlOrJson( + chain1CoreAddress = readYamlOrJsonOrThrow( `${REGISTRY_PATH}/chains/${TEST_CHAIN_NAMES_BY_PROTOCOL.aleo.CHAIN_NAME_1}/addresses.yaml`, ); - chain2CoreAddress = readYamlOrJson( + chain2CoreAddress = readYamlOrJsonOrThrow( `${REGISTRY_PATH}/chains/${TEST_CHAIN_NAMES_BY_PROTOCOL.aleo.CHAIN_NAME_2}/addresses.yaml`, ); }); @@ -176,8 +179,10 @@ describe('hyperlane warp apply ownership (Aleo E2E tests)', async function () { outputPath: WARP_READ_OUTPUT_PATH, }); - const updatedWarpDeployConfig: DerivedWarpRouteDeployConfig = - readYamlOrJson(WARP_READ_OUTPUT_PATH); + const updatedWarpDeployConfig = + readYamlOrJsonOrThrow( + WARP_READ_OUTPUT_PATH, + ); expect( updatedWarpDeployConfig[TEST_CHAIN_NAMES_BY_PROTOCOL.aleo.CHAIN_NAME_1] diff --git a/typescript/cli/src/tests/aleo/warp/warp-apply-route-extension.e2e-test.ts b/typescript/cli/src/tests/aleo/warp/warp-apply-route-extension.e2e-test.ts index ebe177cb3a9..07371bd9842 100644 --- a/typescript/cli/src/tests/aleo/warp/warp-apply-route-extension.e2e-test.ts +++ b/typescript/cli/src/tests/aleo/warp/warp-apply-route-extension.e2e-test.ts @@ -7,7 +7,10 @@ import { } from '@hyperlane-xyz/sdk'; import { ProtocolType, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../commands/warp.js'; import { @@ -80,10 +83,10 @@ describe('hyperlane warp apply route extension (Aleo E2E tests)', async function hyperlaneCore2.deploy(HYP_KEY_BY_PROTOCOL.aleo), ]); - chain1CoreAddress = readYamlOrJson( + chain1CoreAddress = readYamlOrJsonOrThrow( `${REGISTRY_PATH}/chains/${TEST_CHAIN_NAMES_BY_PROTOCOL.aleo.CHAIN_NAME_1}/addresses.yaml`, ); - chain2CoreAddress = readYamlOrJson( + chain2CoreAddress = readYamlOrJsonOrThrow( `${REGISTRY_PATH}/chains/${TEST_CHAIN_NAMES_BY_PROTOCOL.aleo.CHAIN_NAME_2}/addresses.yaml`, ); @@ -139,8 +142,10 @@ describe('hyperlane warp apply route extension (Aleo E2E tests)', async function outputPath: WARP_READ_OUTPUT_PATH, }); - const updatedWarpDeployConfig: DerivedWarpRouteDeployConfig = - readYamlOrJson(WARP_READ_OUTPUT_PATH); + const updatedWarpDeployConfig = + readYamlOrJsonOrThrow( + WARP_READ_OUTPUT_PATH, + ); for (const chainName of Object.keys(warpDeployConfig)) { assertWarpRouteConfig( diff --git a/typescript/cli/src/tests/aleo/warp/warp-deploy.e2e-test.ts b/typescript/cli/src/tests/aleo/warp/warp-deploy.e2e-test.ts index f78f97f9e45..906aaab278a 100644 --- a/typescript/cli/src/tests/aleo/warp/warp-deploy.e2e-test.ts +++ b/typescript/cli/src/tests/aleo/warp/warp-deploy.e2e-test.ts @@ -9,7 +9,10 @@ import { } from '@hyperlane-xyz/sdk'; import { ProtocolType, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../commands/warp.js'; import { @@ -82,10 +85,10 @@ describe('hyperlane warp deploy (Aleo E2E tests)', async function () { hyperlaneCore2.deploy(HYP_KEY_BY_PROTOCOL.aleo), ]); - chain1CoreAddress = readYamlOrJson( + chain1CoreAddress = readYamlOrJsonOrThrow( `${REGISTRY_PATH}/chains/${TEST_CHAIN_NAMES_BY_PROTOCOL.aleo.CHAIN_NAME_1}/addresses.yaml`, ); - chain2CoreAddress = readYamlOrJson( + chain2CoreAddress = readYamlOrJsonOrThrow( `${REGISTRY_PATH}/chains/${TEST_CHAIN_NAMES_BY_PROTOCOL.aleo.CHAIN_NAME_2}/addresses.yaml`, ); @@ -137,7 +140,7 @@ describe('hyperlane warp deploy (Aleo E2E tests)', async function () { outputPath: WARP_READ_OUTPUT_PATH, }); - const config: DerivedWarpRouteDeployConfig = readYamlOrJson( + const config = readYamlOrJsonOrThrow( WARP_READ_OUTPUT_PATH, ); @@ -174,7 +177,7 @@ describe('hyperlane warp deploy (Aleo E2E tests)', async function () { outputPath: WARP_READ_OUTPUT_PATH, }); - const config: DerivedWarpRouteDeployConfig = readYamlOrJson( + const config = readYamlOrJsonOrThrow( WARP_READ_OUTPUT_PATH, ); diff --git a/typescript/cli/src/tests/aleo/warp/warp-read.e2e-test.ts b/typescript/cli/src/tests/aleo/warp/warp-read.e2e-test.ts index 647d7b0918a..f2c3c26af68 100644 --- a/typescript/cli/src/tests/aleo/warp/warp-read.e2e-test.ts +++ b/typescript/cli/src/tests/aleo/warp/warp-read.e2e-test.ts @@ -9,7 +9,10 @@ import { } from '@hyperlane-xyz/sdk'; import { ProtocolType, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../commands/warp.js'; import { @@ -83,10 +86,10 @@ describe('hyperlane warp read (Aleo E2E tests)', async function () { hyperlaneCore2.deploy(HYP_KEY_BY_PROTOCOL.aleo), ]); - chain1CoreAddress = readYamlOrJson( + chain1CoreAddress = readYamlOrJsonOrThrow( `${REGISTRY_PATH}/chains/${TEST_CHAIN_NAMES_BY_PROTOCOL.aleo.CHAIN_NAME_1}/addresses.yaml`, ); - chain2CoreAddress = readYamlOrJson( + chain2CoreAddress = readYamlOrJsonOrThrow( `${REGISTRY_PATH}/chains/${TEST_CHAIN_NAMES_BY_PROTOCOL.aleo.CHAIN_NAME_2}/addresses.yaml`, ); @@ -157,7 +160,7 @@ describe('hyperlane warp read (Aleo E2E tests)', async function () { expect(output.exitCode).to.equal(0); - const config: DerivedWarpRouteDeployConfig = readYamlOrJson( + const config = readYamlOrJsonOrThrow( WARP_READ_OUTPUT_PATH, ); diff --git a/typescript/cli/src/tests/commands/core.ts b/typescript/cli/src/tests/commands/core.ts index d6eff1f77d1..06a3ed6da54 100644 --- a/typescript/cli/src/tests/commands/core.ts +++ b/typescript/cli/src/tests/commands/core.ts @@ -5,7 +5,7 @@ import { type ChainName, type DerivedCoreConfig } from '@hyperlane-xyz/sdk'; import { ProtocolType } from '@hyperlane-xyz/utils'; import { getContext } from '../../context/context.js'; -import { readYamlOrJson } from '../../utils/files.js'; +import { readYamlOrJsonOrThrow } from '../../utils/files.js'; import { localTestRunCmdPrefix } from './helpers.js'; @@ -102,7 +102,7 @@ export class HyperlaneE2ECoreTestCommands { */ public async readConfig(): Promise { await this.read(); - return readYamlOrJson(this.coreOutputPath); + return readYamlOrJsonOrThrow(this.coreOutputPath); } /** diff --git a/typescript/cli/src/tests/commands/ism.ts b/typescript/cli/src/tests/commands/ism.ts index 99e33521aa1..d3b30c3b29d 100644 --- a/typescript/cli/src/tests/commands/ism.ts +++ b/typescript/cli/src/tests/commands/ism.ts @@ -3,7 +3,7 @@ import { $, type ProcessPromise } from 'zx'; import { type ChainName, type IsmConfig } from '@hyperlane-xyz/sdk'; import { ProtocolType } from '@hyperlane-xyz/utils'; -import { readYamlOrJson } from '../../utils/files.js'; +import { readYamlOrJsonOrThrow } from '../../utils/files.js'; import { localTestRunCmdPrefix } from './helpers.js'; @@ -70,7 +70,7 @@ export class HyperlaneE2EIsmTestCommands { outPath: string, ): Promise { await this.deploy(privateKey, configPath, outPath); - const output = readYamlOrJson<{ address: string }>(outPath); + const output = readYamlOrJsonOrThrow<{ address: string }>(outPath); return output.address; } @@ -82,6 +82,6 @@ export class HyperlaneE2EIsmTestCommands { outPath: string, ): Promise { await this.read(address, outPath); - return readYamlOrJson(outPath); + return readYamlOrJsonOrThrow(outPath); } } diff --git a/typescript/cli/src/tests/commands/warp.ts b/typescript/cli/src/tests/commands/warp.ts index b97de7d8129..cbaf5667a1e 100644 --- a/typescript/cli/src/tests/commands/warp.ts +++ b/typescript/cli/src/tests/commands/warp.ts @@ -7,7 +7,7 @@ import { } from '@hyperlane-xyz/sdk'; import { ProtocolType } from '@hyperlane-xyz/utils'; -import { readYamlOrJson } from '../../utils/files.js'; +import { readYamlOrJsonOrThrow } from '../../utils/files.js'; import { localTestRunCmdPrefix } from './helpers.js'; @@ -58,7 +58,7 @@ export class HyperlaneE2EWarpTestCommands { * Retrieves the deployed Warp address from the Warp core config. */ public getDeployedWarpAddress(chain: string, warpCorePath: string) { - const warpCoreConfig: WarpCoreConfig = readYamlOrJson(warpCorePath); + const warpCoreConfig: WarpCoreConfig = readYamlOrJsonOrThrow(warpCorePath); WarpCoreConfigSchema.parse(warpCoreConfig); const token = warpCoreConfig.tokens.find((t) => t.chainName === chain); @@ -112,7 +112,7 @@ export class HyperlaneE2EWarpTestCommands { ): Promise { const warpAddress = this.getDeployedWarpAddress(chain, warpCorePath); await this.read(chain, warpAddress!); - return readYamlOrJson(this.outputPath); + return readYamlOrJsonOrThrow(this.outputPath); } /** diff --git a/typescript/cli/src/tests/constants.ts b/typescript/cli/src/tests/constants.ts index 9c6e2a4a660..827855f2cb9 100644 --- a/typescript/cli/src/tests/constants.ts +++ b/typescript/cli/src/tests/constants.ts @@ -7,7 +7,7 @@ import { import { type ChainMetadata, type ProtocolMap } from '@hyperlane-xyz/sdk'; import { ProtocolType, assert, objMap } from '@hyperlane-xyz/utils'; -import { readYamlOrJson } from '../utils/files.js'; +import { readYamlOrJsonOrThrow } from '../utils/files.js'; export const DEFAULT_E2E_TEST_TIMEOUT = 100_000; // Long timeout since these tests can take a while @@ -177,7 +177,7 @@ export const TEST_CHAIN_METADATA_BY_PROTOCOL: ProtocolChainMap< TestChainMetadata > = objMap(TEST_CHAIN_NAMES_BY_PROTOCOL, (protocol, chainNames) => { return objMap(chainNames, (chainName, _name): TestChainMetadata => { - const currentChainMetadata: ChainMetadata = readYamlOrJson( + const currentChainMetadata: ChainMetadata = readYamlOrJsonOrThrow( TEST_CHAIN_METADATA_PATH_BY_PROTOCOL[protocol][chainName], ); diff --git a/typescript/cli/src/tests/cosmosnative/core/core-apply.e2e-test.ts b/typescript/cli/src/tests/cosmosnative/core/core-apply.e2e-test.ts index 4dfc1d1cb39..58eb91a3096 100644 --- a/typescript/cli/src/tests/cosmosnative/core/core-apply.e2e-test.ts +++ b/typescript/cli/src/tests/cosmosnative/core/core-apply.e2e-test.ts @@ -13,7 +13,10 @@ import { } from '@hyperlane-xyz/sdk'; import { type Address, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { CHAIN_1_METADATA_PATH, @@ -43,7 +46,9 @@ describe('hyperlane core apply e2e tests', async function () { let initialOwnerAddress: Address; before(async () => { - const chainMetadata: ChainMetadata = readYamlOrJson(CHAIN_1_METADATA_PATH); + const chainMetadata = readYamlOrJsonOrThrow( + CHAIN_1_METADATA_PATH, + ); const wallet = await DirectSecp256k1Wallet.fromKey( Uint8Array.from(Buffer.from(HYP_KEY, 'hex')), diff --git a/typescript/cli/src/tests/cosmosnative/core/core-deploy.e2e-test.ts b/typescript/cli/src/tests/cosmosnative/core/core-deploy.e2e-test.ts index d77f0a98412..ac5431ecdb6 100644 --- a/typescript/cli/src/tests/cosmosnative/core/core-deploy.e2e-test.ts +++ b/typescript/cli/src/tests/cosmosnative/core/core-deploy.e2e-test.ts @@ -16,7 +16,10 @@ import { } from '@hyperlane-xyz/sdk'; import { type Address, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { KeyBoardKeys, @@ -55,7 +58,7 @@ describe('hyperlane cosmosnative core deploy e2e tests', async function () { let chainMetadata: ChainMetadata; before(async () => { - chainMetadata = readYamlOrJson(CHAIN_1_METADATA_PATH); + chainMetadata = readYamlOrJsonOrThrow(CHAIN_1_METADATA_PATH); assert(chainMetadata.gasPrice, 'gasPrice not defined in chain metadata'); @@ -251,7 +254,7 @@ describe('hyperlane cosmosnative core deploy e2e tests', async function () { }); it('should create a core deployment with the mailbox owner set to the address in the config', async () => { - const coreConfig: CoreConfig = await readYamlOrJson(CORE_CONFIG_PATH); + const coreConfig = readYamlOrJsonOrThrow(CORE_CONFIG_PATH); const newOwner = await randomCosmosAddress( chainMetadata.bech32Prefix || 'hyp', @@ -276,7 +279,7 @@ describe('hyperlane cosmosnative core deploy e2e tests', async function () { }); it('should create a core deployment with the provided address as the owner of the defaultHook', async () => { - const coreConfig: CoreConfig = await readYamlOrJson(CORE_CONFIG_PATH); + const coreConfig = readYamlOrJsonOrThrow(CORE_CONFIG_PATH); coreConfig.owner = initialOwnerAddress; @@ -304,7 +307,7 @@ describe('hyperlane cosmosnative core deploy e2e tests', async function () { }); it('should create a core deployment with the provided address as the owner of the defaultIsm', async () => { - const coreConfig: CoreConfig = await readYamlOrJson(CORE_CONFIG_PATH); + const coreConfig = readYamlOrJsonOrThrow(CORE_CONFIG_PATH); coreConfig.owner = initialOwnerAddress; diff --git a/typescript/cli/src/tests/cosmosnative/core/core-read.e2e-test.ts b/typescript/cli/src/tests/cosmosnative/core/core-read.e2e-test.ts index 92f800898b6..1e615bec405 100644 --- a/typescript/cli/src/tests/cosmosnative/core/core-read.e2e-test.ts +++ b/typescript/cli/src/tests/cosmosnative/core/core-read.e2e-test.ts @@ -12,7 +12,7 @@ import { } from '@hyperlane-xyz/sdk'; import { type Address, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson } from '../../../utils/files.js'; +import { readYamlOrJsonOrThrow } from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { CHAIN_1_METADATA_PATH, @@ -42,7 +42,9 @@ describe('hyperlane cosmosnative core read e2e tests', async function () { let initialOwnerAddress: Address; before(async () => { - const chainMetadata: ChainMetadata = readYamlOrJson(CHAIN_1_METADATA_PATH); + const chainMetadata = readYamlOrJsonOrThrow( + CHAIN_1_METADATA_PATH, + ); const wallet = await DirectSecp256k1Wallet.fromKey( Uint8Array.from(Buffer.from(HYP_KEY, 'hex')), diff --git a/typescript/cli/src/tests/cosmosnative/warp/warp-read.e2e-test.ts b/typescript/cli/src/tests/cosmosnative/warp/warp-read.e2e-test.ts index 9b42e3882d3..a5fb8fa2a57 100644 --- a/typescript/cli/src/tests/cosmosnative/warp/warp-read.e2e-test.ts +++ b/typescript/cli/src/tests/cosmosnative/warp/warp-read.e2e-test.ts @@ -5,7 +5,10 @@ import { type ChainAddresses } from '@hyperlane-xyz/registry'; import { TokenType, type WarpRouteDeployConfig } from '@hyperlane-xyz/sdk'; import { type Address, ProtocolType } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { KeyBoardKeys, @@ -159,8 +162,8 @@ describe('hyperlane warp read e2e tests', async function () { expect(finalOutput.exitCode).to.equal(0); - const warpReadResult: WarpRouteDeployConfig = - readYamlOrJson(readOutputPath); + const warpReadResult = + readYamlOrJsonOrThrow(readOutputPath); expect(warpReadResult[CHAIN_NAME_1]).not.to.be.undefined; expect(warpReadResult[CHAIN_NAME_1].type).to.equal(TokenType.collateral); diff --git a/typescript/cli/src/tests/cross-chain/warp/warp-apply.e2e-test.ts b/typescript/cli/src/tests/cross-chain/warp/warp-apply.e2e-test.ts index 2779e98f622..c9332d57f8d 100644 --- a/typescript/cli/src/tests/cross-chain/warp/warp-apply.e2e-test.ts +++ b/typescript/cli/src/tests/cross-chain/warp/warp-apply.e2e-test.ts @@ -22,7 +22,10 @@ import { addressToBytes32, } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../commands/warp.js'; import { @@ -174,7 +177,8 @@ describe('hyperlane warp apply e2e tests', async function () { }); // Update warp core config file with the unsupported token - const warpCoreConfig: WarpCoreConfig = readYamlOrJson(WARP_CORE_PATH); + const warpCoreConfig = + readYamlOrJsonOrThrow(WARP_CORE_PATH); warpCoreConfig.tokens.push(getUnsupportedChainWarpCoreTokenConfig()); writeYamlOrJson(WARP_CORE_PATH, warpCoreConfig); @@ -236,7 +240,7 @@ describe('hyperlane warp apply e2e tests', async function () { outputPath: WARP_READ_OUTPUT_PATH, }); - const config: DerivedWarpRouteDeployConfig = readYamlOrJson( + const config = readYamlOrJsonOrThrow( WARP_READ_OUTPUT_PATH, ); @@ -278,7 +282,8 @@ describe('hyperlane warp apply e2e tests', async function () { unsupportedChainWarpCoreConfig.addressOrDenom!; // Update warp core config file with the unsupported token - const warpCoreConfig: WarpCoreConfig = readYamlOrJson(WARP_CORE_PATH); + const warpCoreConfig = + readYamlOrJsonOrThrow(WARP_CORE_PATH); warpCoreConfig.tokens.push(unsupportedChainWarpCoreConfig); writeYamlOrJson(WARP_CORE_PATH, warpCoreConfig); @@ -309,7 +314,7 @@ describe('hyperlane warp apply e2e tests', async function () { outputPath: WARP_READ_OUTPUT_PATH, }); - const config: DerivedWarpRouteDeployConfig = readYamlOrJson( + const config = readYamlOrJsonOrThrow( WARP_READ_OUTPUT_PATH, ); diff --git a/typescript/cli/src/tests/cross-chain/warp/warp-deploy.e2e-test.ts b/typescript/cli/src/tests/cross-chain/warp/warp-deploy.e2e-test.ts index acdddb7dfb7..b8617ecf6c6 100644 --- a/typescript/cli/src/tests/cross-chain/warp/warp-deploy.e2e-test.ts +++ b/typescript/cli/src/tests/cross-chain/warp/warp-deploy.e2e-test.ts @@ -24,7 +24,10 @@ import { addressToBytes32, } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../commands/warp.js'; import { @@ -182,7 +185,7 @@ describe('hyperlane warp deploy e2e tests', async function () { outputPath: WARP_READ_OUTPUT_PATH, }); - const config: DerivedWarpRouteDeployConfig = readYamlOrJson( + const config = readYamlOrJsonOrThrow( WARP_READ_OUTPUT_PATH, ); @@ -242,7 +245,7 @@ describe('hyperlane warp deploy e2e tests', async function () { outputPath: WARP_READ_OUTPUT_PATH, }); - const config: DerivedWarpRouteDeployConfig = readYamlOrJson( + const config = readYamlOrJsonOrThrow( WARP_READ_OUTPUT_PATH, ); @@ -265,7 +268,8 @@ describe('hyperlane warp deploy e2e tests', async function () { ).to.eql(addressToBytes32(unsupportedChainAddress)); } - const warpCoreConfig: WarpCoreConfig = readYamlOrJson(WARP_CORE_PATH); + const warpCoreConfig = + readYamlOrJsonOrThrow(WARP_CORE_PATH); const unsuportedChainData = warpCoreConfig.tokens.find( (tokenConfig) => tokenConfig.chainName === diff --git a/typescript/cli/src/tests/ethereum/commands/core.ts b/typescript/cli/src/tests/ethereum/commands/core.ts index 2efd96028c5..969c54b80a4 100644 --- a/typescript/cli/src/tests/ethereum/commands/core.ts +++ b/typescript/cli/src/tests/ethereum/commands/core.ts @@ -5,7 +5,7 @@ import { type DerivedCoreConfig } from '@hyperlane-xyz/sdk'; import { type Address } from '@hyperlane-xyz/utils'; import { getContext } from '../../../context/context.js'; -import { readYamlOrJson } from '../../../utils/files.js'; +import { readYamlOrJsonOrThrow } from '../../../utils/files.js'; import { ANVIL_KEY, REGISTRY_PATH } from '../consts.js'; import { localTestRunCmdPrefix } from './helpers.js'; @@ -116,7 +116,7 @@ export async function readCoreConfig( coreConfigPath: string, ): Promise { await hyperlaneCoreRead(chain, coreConfigPath); - return readYamlOrJson(coreConfigPath); + return readYamlOrJsonOrThrow(coreConfigPath); } /** diff --git a/typescript/cli/src/tests/ethereum/commands/helpers.ts b/typescript/cli/src/tests/ethereum/commands/helpers.ts index 625416d9e1c..cce02a1ce8c 100644 --- a/typescript/cli/src/tests/ethereum/commands/helpers.ts +++ b/typescript/cli/src/tests/ethereum/commands/helpers.ts @@ -28,7 +28,10 @@ import { import { type Address, assert, inCIMode } from '@hyperlane-xyz/utils'; import { getContext } from '../../../context/context.js'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { KeyBoardKeys, type TestPromptAction } from '../../commands/helpers.js'; import { ANVIL_KEY, @@ -154,7 +157,7 @@ export function getTokenAddressFromWarpConfig( * Retrieves the deployed Warp address from the Warp core config. */ export function getDeployedWarpAddress(chain: string, warpCorePath: string) { - const warpCoreConfig: WarpCoreConfig = readYamlOrJson(warpCorePath); + const warpCoreConfig: WarpCoreConfig = readYamlOrJsonOrThrow(warpCorePath); WarpCoreConfigSchema.parse(warpCoreConfig); return warpCoreConfig.tokens.find((t) => t.chainName === chain)! .addressOrDenom; diff --git a/typescript/cli/src/tests/ethereum/commands/warp.ts b/typescript/cli/src/tests/ethereum/commands/warp.ts index 9aef94f72eb..a5a5303918b 100644 --- a/typescript/cli/src/tests/ethereum/commands/warp.ts +++ b/typescript/cli/src/tests/ethereum/commands/warp.ts @@ -18,7 +18,11 @@ import { createAltVMSigners } from '../../../context/altvm.js'; import { getContext } from '../../../context/context.js'; import { type CommandContext } from '../../../context/types.js'; import { extendWarpRoute as extendWarpRouteWithoutApplyTransactions } from '../../../deploy/warp.js'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJson, + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { ANVIL_DEPLOYER_ADDRESS, ANVIL_KEY, @@ -314,7 +318,7 @@ export async function readWarpConfig( ): Promise { const warpAddress = getDeployedWarpAddress(chain, warpCorePath); await hyperlaneWarpRead(chain, warpAddress!, warpDeployPath); - return readYamlOrJson(warpDeployPath); + return readYamlOrJsonOrThrow(warpDeployPath); } type GetWarpTokenConfigByTokenTypeOptions = { @@ -512,7 +516,7 @@ export async function updateWarpOwnerConfig( warpDeployPath, ); warpDeployConfig[chain].owner = owner; - await writeYamlOrJson(warpDeployPath, warpDeployConfig); + writeYamlOrJson(warpDeployPath, warpDeployConfig); return warpDeployPath; } diff --git a/typescript/cli/src/tests/ethereum/core/core-apply.e2e-test.ts b/typescript/cli/src/tests/ethereum/core/core-apply.e2e-test.ts index 160eee2dcd6..aae83df596e 100644 --- a/typescript/cli/src/tests/ethereum/core/core-apply.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/core/core-apply.e2e-test.ts @@ -16,7 +16,10 @@ import { addressToBytes32, } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { ANVIL_KEY, @@ -58,8 +61,12 @@ describe('hyperlane core apply e2e tests', async function () { let chain3DomainId: Domain; before(async () => { - const chain2Metadata: ChainMetadata = readYamlOrJson(CHAIN_2_METADATA_PATH); - const chain3Metadata: ChainMetadata = readYamlOrJson(CHAIN_3_METADATA_PATH); + const chain2Metadata = readYamlOrJsonOrThrow( + CHAIN_2_METADATA_PATH, + ); + const chain3Metadata = readYamlOrJsonOrThrow( + CHAIN_3_METADATA_PATH, + ); const provider = new ethers.providers.JsonRpcProvider( chain2Metadata.rpcUrls[0].http, diff --git a/typescript/cli/src/tests/ethereum/core/core-deploy.e2e-test.ts b/typescript/cli/src/tests/ethereum/core/core-deploy.e2e-test.ts index 6fcf1c425c9..5fabd1e0d4d 100644 --- a/typescript/cli/src/tests/ethereum/core/core-deploy.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/core/core-deploy.e2e-test.ts @@ -10,7 +10,10 @@ import { } from '@hyperlane-xyz/sdk'; import { type Address, ProtocolType } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { KeyBoardKeys, @@ -44,7 +47,9 @@ describe('hyperlane core deploy e2e tests', async function () { let initialOwnerAddress: Address; before(async () => { - const chainMetadata: ChainMetadata = readYamlOrJson(CHAIN_2_METADATA_PATH); + const chainMetadata = readYamlOrJsonOrThrow( + CHAIN_2_METADATA_PATH, + ); const provider = new ethers.providers.JsonRpcProvider( chainMetadata.rpcUrls[0].http, @@ -227,7 +232,7 @@ describe('hyperlane core deploy e2e tests', async function () { }); it('should create a core deployment with the mailbox owner set to the address in the config', async () => { - const coreConfig: CoreConfig = await readYamlOrJson(CORE_CONFIG_PATH); + const coreConfig = readYamlOrJsonOrThrow(CORE_CONFIG_PATH); const newOwner = randomAddress().toLowerCase(); @@ -250,7 +255,7 @@ describe('hyperlane core deploy e2e tests', async function () { }); it('should create a core deployment with ProxyAdmin owner of the mailbox set to the address in the config', async () => { - const coreConfig: CoreConfig = await readYamlOrJson(CORE_CONFIG_PATH); + const coreConfig = readYamlOrJsonOrThrow(CORE_CONFIG_PATH); const newOwner = randomAddress().toLowerCase(); diff --git a/typescript/cli/src/tests/ethereum/core/core-init.e2e-test.ts b/typescript/cli/src/tests/ethereum/core/core-init.e2e-test.ts index b5be73919e5..c757b2e3990 100644 --- a/typescript/cli/src/tests/ethereum/core/core-init.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/core/core-init.e2e-test.ts @@ -14,7 +14,7 @@ import { normalizeAddress, } from '@hyperlane-xyz/utils'; -import { readYamlOrJson } from '../../../utils/files.js'; +import { readYamlOrJsonOrThrow } from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { CONFIRM_DETECTED_OWNER_STEP, @@ -100,7 +100,7 @@ describe('hyperlane core init e2e tests', async function () { expect(finalOutput.exitCode).to.equal(0); const deploymentCoreConfig: CoreConfig = - readYamlOrJson(CORE_CONFIG_PATH_2); + readYamlOrJsonOrThrow(CORE_CONFIG_PATH_2); assertCoreInitConfig(deploymentCoreConfig, owner, feeHookOwner); }); }); @@ -124,7 +124,7 @@ describe('hyperlane core init e2e tests', async function () { expect(finalOutput.exitCode).to.equal(0); const deploymentCoreConfig: CoreConfig = - readYamlOrJson(CORE_CONFIG_PATH_2); + readYamlOrJsonOrThrow(CORE_CONFIG_PATH_2); assertCoreInitConfig(deploymentCoreConfig, owner); }); @@ -152,7 +152,7 @@ describe('hyperlane core init e2e tests', async function () { expect(finalOutput.exitCode).to.equal(0); const deploymentCoreConfig: CoreConfig = - readYamlOrJson(CORE_CONFIG_PATH_2); + readYamlOrJsonOrThrow(CORE_CONFIG_PATH_2); assertCoreInitConfig( deploymentCoreConfig, owner, @@ -181,7 +181,7 @@ describe('hyperlane core init e2e tests', async function () { expect(finalOutput.exitCode).to.equal(0); const deploymentCoreConfig: CoreConfig = - readYamlOrJson(CORE_CONFIG_PATH_2); + readYamlOrJsonOrThrow(CORE_CONFIG_PATH_2); assertCoreInitConfig(deploymentCoreConfig, owner); }); @@ -209,7 +209,7 @@ describe('hyperlane core init e2e tests', async function () { expect(finalOutput.exitCode).to.equal(0); const deploymentCoreConfig: CoreConfig = - readYamlOrJson(CORE_CONFIG_PATH_2); + readYamlOrJsonOrThrow(CORE_CONFIG_PATH_2); assertCoreInitConfig( deploymentCoreConfig, owner, diff --git a/typescript/cli/src/tests/ethereum/core/core-read.e2e-test.ts b/typescript/cli/src/tests/ethereum/core/core-read.e2e-test.ts index 163f203f09c..0821f5b214e 100644 --- a/typescript/cli/src/tests/ethereum/core/core-read.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/core/core-read.e2e-test.ts @@ -8,7 +8,7 @@ import { } from '@hyperlane-xyz/sdk'; import { type Address, ProtocolType } from '@hyperlane-xyz/utils'; -import { readYamlOrJson } from '../../../utils/files.js'; +import { readYamlOrJsonOrThrow } from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { ANVIL_KEY, @@ -35,7 +35,9 @@ describe('hyperlane core read e2e tests', async function () { let initialOwnerAddress: Address; before(async () => { - const chainMetadata: ChainMetadata = readYamlOrJson(CHAIN_2_METADATA_PATH); + const chainMetadata = readYamlOrJsonOrThrow( + CHAIN_2_METADATA_PATH, + ); const provider = new ethers.providers.JsonRpcProvider( chainMetadata.rpcUrls[0].http, diff --git a/typescript/cli/src/tests/ethereum/fixtures/warp-test-fixture.ts b/typescript/cli/src/tests/ethereum/fixtures/warp-test-fixture.ts index d42d6866efe..71eac6d77a5 100644 --- a/typescript/cli/src/tests/ethereum/fixtures/warp-test-fixture.ts +++ b/typescript/cli/src/tests/ethereum/fixtures/warp-test-fixture.ts @@ -3,7 +3,10 @@ import { type WarpRouteDeployConfig, } from '@hyperlane-xyz/sdk'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { createSnapshot, restoreSnapshot } from '../commands/helpers.js'; export interface WarpTestFixtureConfig { @@ -53,7 +56,7 @@ export class WarpTestFixture { } loadCoreConfig(): void { - this.coreConfig = readYamlOrJson(this.config.coreConfigPath); + this.coreConfig = readYamlOrJsonOrThrow(this.config.coreConfigPath); } setCoreConfig(config: WarpCoreConfig): void { diff --git a/typescript/cli/src/tests/ethereum/ica/ica-deploy.e2e-test.ts b/typescript/cli/src/tests/ethereum/ica/ica-deploy.e2e-test.ts index 4f9e2dc5468..2d58d7df418 100644 --- a/typescript/cli/src/tests/ethereum/ica/ica-deploy.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/ica/ica-deploy.e2e-test.ts @@ -18,7 +18,7 @@ import { import { getContext } from '../../../context/context.js'; import { IcaDeployStatus } from '../../../deploy/ica.js'; -import { readYamlOrJson } from '../../../utils/files.js'; +import { readYamlOrJsonOrThrow } from '../../../utils/files.js'; import { deployOrUseExistingCore } from '../commands/core.js'; import { hyperlaneIcaDeploy, hyperlaneIcaDeployRaw } from '../commands/ica.js'; import { @@ -58,9 +58,15 @@ describe('hyperlane ica deploy e2e tests', async function () { deployOrUseExistingCore(CHAIN_NAME_4, CORE_CONFIG_PATH, ANVIL_KEY), ]); - const chain2Metadata: ChainMetadata = readYamlOrJson(CHAIN_2_METADATA_PATH); - const chain3Metadata: ChainMetadata = readYamlOrJson(CHAIN_3_METADATA_PATH); - const chain4Metadata: ChainMetadata = readYamlOrJson(CHAIN_4_METADATA_PATH); + const chain2Metadata = readYamlOrJsonOrThrow( + CHAIN_2_METADATA_PATH, + ); + const chain3Metadata = readYamlOrJsonOrThrow( + CHAIN_3_METADATA_PATH, + ); + const chain4Metadata = readYamlOrJsonOrThrow( + CHAIN_4_METADATA_PATH, + ); chain2DomainId = chain2Metadata.domainId!; chain3DomainId = chain3Metadata.domainId!; diff --git a/typescript/cli/src/tests/ethereum/warp/apply/warp-apply-rebalancing-config.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/apply/warp-apply-rebalancing-config.e2e-test.ts index f446f8526ff..33aa57c1323 100644 --- a/typescript/cli/src/tests/ethereum/warp/apply/warp-apply-rebalancing-config.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/apply/warp-apply-rebalancing-config.e2e-test.ts @@ -22,7 +22,10 @@ import { randomInt, } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../../commands/warp.js'; import { @@ -309,7 +312,7 @@ describe('hyperlane warp apply owner update tests', async function () { hypKey: HYP_KEY_BY_PROTOCOL.ethereum, }); - const coreConfig: WarpCoreConfig = readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( DEFAULT_EVM_WARP_CORE_PATH, ); diff --git a/typescript/cli/src/tests/ethereum/warp/apply/warp-apply-simple-updates.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/apply/warp-apply-simple-updates.e2e-test.ts index d8fc86ea19f..0fffd095306 100644 --- a/typescript/cli/src/tests/ethereum/warp/apply/warp-apply-simple-updates.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/apply/warp-apply-simple-updates.e2e-test.ts @@ -14,7 +14,10 @@ import { normalizeAddressEvm, } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../../commands/warp.js'; import { @@ -120,7 +123,7 @@ describe('hyperlane warp apply owner update tests', async function () { // Copy over the warp deploy AND core to custom warp route id filepath // This simulates the user updating the warp route id in the registry const warpRouteId = 'ETH/custom-warp-route-id-2'; - const warpCoreConfig: WarpCoreConfig = readYamlOrJson( + const warpCoreConfig = readYamlOrJsonOrThrow( DEFAULT_EVM_WARP_CORE_PATH, ); const { warpCorePath: updatedWarpCorePath } = exportWarpConfigsToFilePaths({ @@ -154,7 +157,7 @@ describe('hyperlane warp apply owner update tests', async function () { // Copy over the warp deploy AND core to custom warp route id filepath // This simulates the user updating the warp route id in the registry const warpRouteId = 'ETH/custom-warp-route-id-2'; - const warpCoreConfig: WarpCoreConfig = readYamlOrJson( + const warpCoreConfig = readYamlOrJsonOrThrow( DEFAULT_EVM_WARP_CORE_PATH, ); const { warpCorePath: updatedWarpCorePath } = exportWarpConfigsToFilePaths({ diff --git a/typescript/cli/src/tests/ethereum/warp/apply/warp-apply-submitters.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/apply/warp-apply-submitters.e2e-test.ts index e349637ea40..e5d30256d19 100644 --- a/typescript/cli/src/tests/ethereum/warp/apply/warp-apply-submitters.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/apply/warp-apply-submitters.e2e-test.ts @@ -25,7 +25,11 @@ import { assert, } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../../utils/files.js'; +import { + readYamlOrJson, + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../../commands/warp.js'; import { @@ -248,7 +252,7 @@ describe('hyperlane warp apply with submitters', async function () { assert(maybeGeneratedTxFilePath, 'expected the tx file output'); const [generatedTxFilePath] = maybeGeneratedTxFilePath; - const txFile: CallData[] = readYamlOrJson(generatedTxFilePath); + const txFile: CallData[] = readYamlOrJsonOrThrow(generatedTxFilePath); const executeTransaction = txFile.pop(); assert( executeTransaction, @@ -497,7 +501,7 @@ describe('hyperlane warp apply with submitters', async function () { version: string; chainId: string; transactions: { to: string; data: string }[]; - } = readYamlOrJson(filePath); + } = readYamlOrJsonOrThrow(filePath); // Verify Safe Transaction Builder JSON format expect(txBuilderJson).to.have.property('version', '1.0'); diff --git a/typescript/cli/src/tests/ethereum/warp/warp-bridge-utils.ts b/typescript/cli/src/tests/ethereum/warp/warp-bridge-utils.ts index a87f840097d..a87c7375e2c 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-bridge-utils.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-bridge-utils.ts @@ -22,7 +22,11 @@ import { } from '@hyperlane-xyz/sdk'; import { type Address } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJson, + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { deployOrUseExistingCore } from '../commands/core.js'; import { deploy4626Vault, @@ -111,7 +115,8 @@ export async function runWarpBridgeTests( targetChain = CHAIN_NAME_2; } - const warpCoreConfig: WarpCoreConfig = readYamlOrJson(routeConfigPath); + const warpCoreConfig: WarpCoreConfig = + readYamlOrJsonOrThrow(routeConfigPath); if (warpConfig[CHAIN_NAME_2].type.match(/.*xerc20.*/i)) { const tx = await config.xERC202.addBridge({ bridge: getTokenAddressFromWarpConfig(warpCoreConfig, CHAIN_NAME_2), @@ -158,8 +163,12 @@ export async function runWarpBridgeTests( } export async function setupChains(): Promise { - const chain2Metadata: ChainMetadata = readYamlOrJson(CHAIN_2_METADATA_PATH); - const chain3Metadata: ChainMetadata = readYamlOrJson(CHAIN_3_METADATA_PATH); + const chain2Metadata: ChainMetadata = readYamlOrJsonOrThrow( + CHAIN_2_METADATA_PATH, + ); + const chain3Metadata: ChainMetadata = readYamlOrJsonOrThrow( + CHAIN_3_METADATA_PATH, + ); const providerChain2 = new JsonRpcProvider(chain2Metadata.rpcUrls[0].http); const providerChain3 = new JsonRpcProvider(chain3Metadata.rpcUrls[0].http); diff --git a/typescript/cli/src/tests/ethereum/warp/warp-check-2.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/warp-check-2.e2e-test.ts index 77a74edde74..6bcc5faec5e 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-check-2.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-check-2.e2e-test.ts @@ -20,7 +20,11 @@ import { } from '@hyperlane-xyz/sdk'; import { type Address, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJson, + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { deployOrUseExistingCore } from '../commands/core.js'; import { deployToken } from '../commands/helpers.js'; import { @@ -59,7 +63,9 @@ describe('hyperlane warp check e2e tests', async function () { deployOrUseExistingCore(CHAIN_NAME_3, CORE_CONFIG_PATH, ANVIL_KEY), ]); - const chainMetadata: ChainMetadata = readYamlOrJson(CHAIN_2_METADATA_PATH); + const chainMetadata = readYamlOrJsonOrThrow( + CHAIN_2_METADATA_PATH, + ); chain3DomainId = (readYamlOrJson(CHAIN_3_METADATA_PATH) as ChainMetadata) .domainId; diff --git a/typescript/cli/src/tests/ethereum/warp/warp-check-3.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/warp-check-3.e2e-test.ts index bcdd485db61..9a4a7bfb228 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-check-3.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-check-3.e2e-test.ts @@ -17,7 +17,10 @@ import { } from '@hyperlane-xyz/sdk'; import { type Address, addressToBytes32 } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { deployOrUseExistingCore } from '../commands/core.js'; import { deployToken } from '../commands/helpers.js'; import { @@ -218,7 +221,7 @@ describe('hyperlane warp check e2e tests', async function () { warpDeployConfig[CHAIN_NAME_3].remoteRouters = undefined; writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpDeployConfig); - const warpCore: WarpCoreConfig = readYamlOrJson( + const warpCore = readYamlOrJsonOrThrow( WARP_CORE_CONFIG_PATH_2_3, ); diff --git a/typescript/cli/src/tests/ethereum/warp/warp-check-5.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/warp-check-5.e2e-test.ts index 2a83769cbce..c17907b2d6f 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-check-5.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-check-5.e2e-test.ts @@ -15,7 +15,10 @@ import { } from '@hyperlane-xyz/sdk'; import { type Address } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { deployOrUseExistingCore } from '../commands/core.js'; import { deployToken } from '../commands/helpers.js'; import { @@ -52,7 +55,9 @@ describe('hyperlane warp check e2e tests', async function () { deployOrUseExistingCore(CHAIN_NAME_3, CORE_CONFIG_PATH, ANVIL_KEY), ]); - const chainMetadata: ChainMetadata = readYamlOrJson(CHAIN_2_METADATA_PATH); + const chainMetadata = readYamlOrJsonOrThrow( + CHAIN_2_METADATA_PATH, + ); const provider = new ethers.providers.JsonRpcProvider( chainMetadata.rpcUrls[0].http, @@ -201,7 +206,7 @@ describe('hyperlane warp check e2e tests', async function () { writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpConfig); await hyperlaneWarpDeploy(WARP_DEPLOY_OUTPUT_PATH); - const deployConfig: WarpRouteDeployConfig = readYamlOrJson( + const deployConfig = readYamlOrJsonOrThrow( WARP_DEPLOY_OUTPUT_PATH, ); @@ -230,7 +235,7 @@ describe('hyperlane warp check e2e tests', async function () { writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, warpConfig); await hyperlaneWarpDeploy(WARP_DEPLOY_OUTPUT_PATH); - const deployConfig: WarpRouteDeployConfig = readYamlOrJson( + const deployConfig = readYamlOrJsonOrThrow( WARP_DEPLOY_OUTPUT_PATH, ); @@ -238,7 +243,7 @@ describe('hyperlane warp check e2e tests', async function () { deployConfig[CHAIN_NAME_2].scale = 1; deployConfig[CHAIN_NAME_3].decimals = 34; - deployConfig[CHAIN_NAME_2].scale = 2; + deployConfig[CHAIN_NAME_3].scale = 2; writeYamlOrJson(WARP_DEPLOY_OUTPUT_PATH, deployConfig); diff --git a/typescript/cli/src/tests/ethereum/warp/warp-check-ica.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/warp-check-ica.e2e-test.ts index 36ccb1d562d..2caa634744d 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-check-ica.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-check-ica.e2e-test.ts @@ -24,7 +24,10 @@ import { } from '@hyperlane-xyz/utils'; import { getContext } from '../../../context/context.js'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { deployOrUseExistingCore } from '../commands/core.js'; import { deployToken } from '../commands/helpers.js'; import { @@ -73,8 +76,12 @@ describe('hyperlane warp check --ica e2e tests', async function () { // ICA setup icaOwnerAddress = new Wallet(ANVIL_KEY).address; - const chain2Metadata: ChainMetadata = readYamlOrJson(CHAIN_2_METADATA_PATH); - const chain3Metadata: ChainMetadata = readYamlOrJson(CHAIN_3_METADATA_PATH); + const chain2Metadata = readYamlOrJsonOrThrow( + CHAIN_2_METADATA_PATH, + ); + const chain3Metadata = readYamlOrJsonOrThrow( + CHAIN_3_METADATA_PATH, + ); const providerChain2 = new ethers.providers.JsonRpcProvider( chain2Metadata.rpcUrls[0].http, diff --git a/typescript/cli/src/tests/ethereum/warp/warp-deploy-1.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/warp-deploy-1.e2e-test.ts index f5932c7b25f..741d157dedb 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-deploy-1.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-deploy-1.e2e-test.ts @@ -23,7 +23,10 @@ import { } from '@hyperlane-xyz/sdk'; import { type Address } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { KeyBoardKeys, type TestPromptAction, @@ -74,7 +77,7 @@ describe('hyperlane warp deploy e2e tests', async function () { let providerChain2: JsonRpcProvider; before(async function () { - chain2Metadata = readYamlOrJson(CHAIN_2_METADATA_PATH); + chain2Metadata = readYamlOrJsonOrThrow(CHAIN_2_METADATA_PATH); providerChain2 = new JsonRpcProvider(chain2Metadata.rpcUrls[0].http); walletChain2 = new Wallet(ANVIL_KEY).connect(providerChain2); ownerAddress = walletChain2.address; diff --git a/typescript/cli/src/tests/ethereum/warp/warp-deploy-2.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/warp-deploy-2.e2e-test.ts index d41548dceaa..d1a2e7b83ef 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-deploy-2.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-deploy-2.e2e-test.ts @@ -31,7 +31,10 @@ import { pick, } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { deployOrUseExistingCore } from '../commands/core.js'; import { GET_WARP_DEPLOY_CORE_CONFIG_OUTPUT_PATH, @@ -95,15 +98,21 @@ describe('hyperlane warp deploy e2e tests', async function () { let providerChain2: JsonRpcProvider; before(async function () { - chain2Metadata = readYamlOrJson(CHAIN_2_METADATA_PATH); + chain2Metadata = readYamlOrJsonOrThrow( + CHAIN_2_METADATA_PATH, + ); providerChain2 = new JsonRpcProvider(chain2Metadata.rpcUrls[0].http); walletChain2 = new Wallet(ANVIL_KEY).connect(providerChain2); ownerAddress = walletChain2.address; - const chain3Metadata: ChainMetadata = readYamlOrJson(CHAIN_3_METADATA_PATH); + const chain3Metadata = readYamlOrJsonOrThrow( + CHAIN_3_METADATA_PATH, + ); chain3DomainId = chain3Metadata.domainId; - const chain4Metadata: ChainMetadata = readYamlOrJson(CHAIN_4_METADATA_PATH); + const chain4Metadata = readYamlOrJsonOrThrow( + CHAIN_4_METADATA_PATH, + ); chain4DomainId = chain4Metadata.domainId; // Deploy core contracts to populate the registry @@ -159,7 +168,7 @@ describe('hyperlane warp deploy e2e tests', async function () { await tokenChain2.symbol(), ); - const coreConfig: WarpCoreConfig = readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( COMBINED_WARP_CORE_CONFIG_PATH, ); @@ -232,7 +241,7 @@ describe('hyperlane warp deploy e2e tests', async function () { await tokenChain2.symbol(), ); - const coreConfig: WarpCoreConfig = readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( COMBINED_WARP_CORE_CONFIG_PATH, ); @@ -423,7 +432,7 @@ describe('hyperlane warp deploy e2e tests', async function () { await tokenChain2.symbol(), ); - const coreConfig: WarpCoreConfig = readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( COMBINED_WARP_CORE_CONFIG_PATH, ); const [syntheticTokenConfig] = coreConfig.tokens.filter( @@ -531,7 +540,7 @@ describe('hyperlane warp deploy e2e tests', async function () { await tokenChain2.symbol(), ); - const coreConfig: WarpCoreConfig = readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( COMBINED_WARP_CORE_CONFIG_PATH, ); diff --git a/typescript/cli/src/tests/ethereum/warp/warp-extend-basic.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/warp-extend-basic.e2e-test.ts index 2651dbe409d..0d565f38915 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-extend-basic.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-extend-basic.e2e-test.ts @@ -9,7 +9,10 @@ import { type WarpRouteDeployConfig, } from '@hyperlane-xyz/sdk'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { deployOrUseExistingCore } from '../commands/core.js'; import { getDomainId } from '../commands/helpers.js'; import { @@ -47,7 +50,7 @@ describe('hyperlane warp apply basic extension tests', async function () { ]); // Create a new warp config using the example - const warpConfig: WarpRouteDeployConfig = readYamlOrJson( + const warpConfig = readYamlOrJsonOrThrow( WARP_CONFIG_PATH_EXAMPLE, ); @@ -308,7 +311,7 @@ describe('hyperlane warp apply basic extension tests', async function () { const TEST_COINGECKO_ID = 'ethereum'; // Read and modify warp core config to add metadata - const warpCoreConfig: WarpCoreConfig = readYamlOrJson( + const warpCoreConfig = readYamlOrJsonOrThrow( WARP_CORE_CONFIG_PATH_2, ); warpCoreConfig.tokens[0].logoURI = TEST_LOGO_URI; @@ -339,7 +342,7 @@ describe('hyperlane warp apply basic extension tests', async function () { ]); // Read resulting config and verify metadata preserved - const resultConfig: WarpCoreConfig = readYamlOrJson( + const resultConfig = readYamlOrJsonOrThrow( COMBINED_WARP_CORE_CONFIG_PATH, ); const chain2Token = resultConfig.tokens.find( diff --git a/typescript/cli/src/tests/ethereum/warp/warp-extend-config.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/warp-extend-config.e2e-test.ts index 854fde48a94..bec391b9992 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-extend-config.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-extend-config.e2e-test.ts @@ -11,7 +11,10 @@ import { } from '@hyperlane-xyz/sdk'; import { addressToBytes32 } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { deployOrUseExistingCore } from '../commands/core.js'; import { getDomainId } from '../commands/helpers.js'; import { @@ -45,7 +48,7 @@ describe('hyperlane warp apply config extension tests', async function () { ]); // Create a new warp config using the example - const warpConfig: WarpRouteDeployConfig = readYamlOrJson( + const warpConfig = readYamlOrJsonOrThrow( WARP_CONFIG_PATH_EXAMPLE, ); const anvil2Config = { anvil2: { ...warpConfig.anvil1 } }; @@ -93,7 +96,7 @@ describe('hyperlane warp apply config extension tests', async function () { }; // Write the updated config - await writeYamlOrJson(warpDeployPath, warpDeployConfig); + writeYamlOrJson(warpDeployPath, warpDeployConfig); // Apply the changes await hyperlaneWarpApply( @@ -156,7 +159,7 @@ describe('hyperlane warp apply config extension tests', async function () { }; // Write the updated config - await writeYamlOrJson(warpDeployPath, warpDeployConfig); + writeYamlOrJson(warpDeployPath, warpDeployConfig); // Apply the changes await hyperlaneWarpApply( @@ -204,7 +207,7 @@ describe('hyperlane warp apply config extension tests', async function () { // Remove remoteRouters and destinationGas as they are written in readWarpConfig delete warpDeployConfig[CHAIN_NAME_2].remoteRouters; delete warpDeployConfig[CHAIN_NAME_2].destinationGas; - await writeYamlOrJson(warpDeployPath, warpDeployConfig); + writeYamlOrJson(warpDeployPath, warpDeployConfig); await hyperlaneWarpApply( warpDeployPath, WARP_CORE_CONFIG_PATH_2, @@ -212,7 +215,8 @@ describe('hyperlane warp apply config extension tests', async function () { WARP_DEPLOY_2_ID, ); - const updatedConfig: WarpRouteDeployConfig = readYamlOrJson(warpDeployPath); + const updatedConfig = + readYamlOrJsonOrThrow(warpDeployPath); expect(normalizeConfig(warpDeployConfig)).to.deep.equal( normalizeConfig(updatedConfig), diff --git a/typescript/cli/src/tests/ethereum/warp/warp-extend-recovery.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/warp-extend-recovery.e2e-test.ts index 264f2f016fe..0e6f1ad0e1b 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-extend-recovery.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-extend-recovery.e2e-test.ts @@ -10,7 +10,10 @@ import { } from '@hyperlane-xyz/sdk'; import { getContext } from '../../../context/context.js'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { deployOrUseExistingCore } from '../commands/core.js'; import { getDomainId } from '../commands/helpers.js'; import { @@ -46,7 +49,7 @@ describe('hyperlane warp apply recovery extension tests', async function () { ]); // Create a new warp config using the example - const warpConfig: WarpRouteDeployConfig = readYamlOrJson( + const warpConfig = readYamlOrJsonOrThrow( WARP_CONFIG_PATH_EXAMPLE, ); const anvil2Config = { anvil2: { ...warpConfig.anvil1 } }; @@ -86,9 +89,9 @@ describe('hyperlane warp apply recovery extension tests', async function () { CHAIN_NAME_3, ]); - const warpCoreConfig = readYamlOrJson( + const warpCoreConfig = readYamlOrJsonOrThrow( COMBINED_WARP_CORE_CONFIG_PATH, - ) as WarpCoreConfig; + ); const deployedTokenRoute = warpCoreConfig.tokens.find( (t) => t.chainName === CHAIN_NAME_2, )?.addressOrDenom; diff --git a/typescript/cli/src/tests/ethereum/warp/warp-init.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/warp-init.e2e-test.ts index e050e8f2e22..f4955dcce41 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-init.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-init.e2e-test.ts @@ -8,7 +8,7 @@ import { } from '@hyperlane-xyz/sdk'; import { type Address } from '@hyperlane-xyz/utils'; -import { readYamlOrJson } from '../../../utils/files.js'; +import { readYamlOrJsonOrThrow } from '../../../utils/files.js'; import { CONFIRM_DETECTED_OWNER_STEP, KeyBoardKeys, @@ -71,8 +71,8 @@ describe('hyperlane warp init e2e tests', async function () { await handlePrompts(output, steps); - const warpConfig: WarpRouteDeployConfig = - readYamlOrJson(WARP_CONFIG_PATH_2); + const warpConfig = + readYamlOrJsonOrThrow(WARP_CONFIG_PATH_2); assertWarpConfig(warpConfig, CHAIN_NAME_2); }); @@ -91,8 +91,8 @@ describe('hyperlane warp init e2e tests', async function () { await handlePrompts(output, steps); - const warpConfig: WarpRouteDeployConfig = - readYamlOrJson(WARP_CONFIG_PATH_2); + const warpConfig = + readYamlOrJsonOrThrow(WARP_CONFIG_PATH_2); [CHAIN_NAME_2, CHAIN_NAME_3].map((chainName) => assertWarpConfig(warpConfig, chainName), @@ -131,8 +131,8 @@ describe('hyperlane warp init e2e tests', async function () { await handlePrompts(output, steps); - const warpConfig: WarpRouteDeployConfig = - readYamlOrJson(WARP_CONFIG_PATH_2); + const warpConfig = + readYamlOrJsonOrThrow(WARP_CONFIG_PATH_2); expect(warpConfig[CHAIN_NAME_2]).not.to.be.undefined; @@ -178,7 +178,7 @@ describe('hyperlane warp init e2e tests', async function () { await handlePrompts(output, steps); - const warpConfig: WarpRouteDeployConfig = readYamlOrJson( + const warpConfig = readYamlOrJsonOrThrow( `${E2E_TEST_CONFIGS_PATH}/anvil/deployments/warp_routes/${warpRouteId}-deploy.yaml`, ); assertWarpConfig(warpConfig, CHAIN_NAME_2); diff --git a/typescript/cli/src/tests/ethereum/warp/warp-read.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/warp-read.e2e-test.ts index d7b8aa3144f..e8f18fc3022 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-read.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-read.e2e-test.ts @@ -5,7 +5,10 @@ import { type ChainAddresses } from '@hyperlane-xyz/registry'; import { TokenType, type WarpRouteDeployConfig } from '@hyperlane-xyz/sdk'; import { type Address, ProtocolType } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { KeyBoardKeys, @@ -75,9 +78,8 @@ describe('hyperlane warp read e2e tests', async function () { await hyperlaneCore2.deployOrUseExistingCore(ANVIL_KEY); // Create a new warp config using the example - const exampleWarpConfig: WarpRouteDeployConfig = readYamlOrJson( - WARP_CONFIG_PATH_EXAMPLE, - ); + const exampleWarpConfig: WarpRouteDeployConfig = + await readYamlOrJsonOrThrow(WARP_CONFIG_PATH_EXAMPLE); anvil2Config = { [CHAIN_NAME_2]: { ...exampleWarpConfig.anvil1 } }; writeYamlOrJson(WARP_CONFIG_PATH_2, anvil2Config); }); @@ -107,7 +109,7 @@ describe('hyperlane warp read e2e tests', async function () { expect(output.exitCode).to.equal(0); const warpReadResult: WarpRouteDeployConfig = - readYamlOrJson(WARP_CONFIG_PATH_2); + await readYamlOrJsonOrThrow(WARP_CONFIG_PATH_2); expect(warpReadResult[CHAIN_NAME_2]).not.to.be.undefined; expect(warpReadResult[CHAIN_NAME_2].type).to.equal(TokenType.native); }); @@ -155,7 +157,7 @@ describe('hyperlane warp read e2e tests', async function () { expect(finalOutput.exitCode).to.equal(0); const warpReadResult: WarpRouteDeployConfig = - readYamlOrJson(readOutputPath); + await readYamlOrJsonOrThrow(readOutputPath); expect(warpReadResult[CHAIN_NAME_2]).not.to.be.undefined; expect(warpReadResult[CHAIN_NAME_2].type).to.equal(TokenType.synthetic); @@ -212,8 +214,8 @@ describe('hyperlane warp read e2e tests', async function () { expect(finalOutput.exitCode).to.equal(0); - const warpReadResult: WarpRouteDeployConfig = - readYamlOrJson(readOutputPath); + const warpReadResult = + readYamlOrJsonOrThrow(readOutputPath); expect(warpReadResult[CHAIN_NAME_2]).not.to.be.undefined; expect(warpReadResult[CHAIN_NAME_2].type).to.equal(TokenType.synthetic); diff --git a/typescript/cli/src/tests/ethereum/warp/warp-rebalancer.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/warp-rebalancer.e2e-test.ts index 1b2c14bb6be..8b839cc96d7 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-rebalancer.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-rebalancer.e2e-test.ts @@ -31,7 +31,10 @@ import { toWei, } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { deployOrUseExistingCore } from '../commands/core.js'; import { createSnapshot, @@ -151,10 +154,10 @@ describe('hyperlane warp rebalancer e2e tests', async function () { await hyperlaneWarpDeploy(warpDeploymentPath, warpRouteId); // After deployment, read the core config that was generated - warpCoreConfig = readYamlOrJson(warpCoreConfigPath); - chain2Metadata = readYamlOrJson(CHAIN_2_METADATA_PATH); - chain3Metadata = readYamlOrJson(CHAIN_3_METADATA_PATH); - chain4Metadata = readYamlOrJson(CHAIN_4_METADATA_PATH); + warpCoreConfig = readYamlOrJsonOrThrow(warpCoreConfigPath); + chain2Metadata = readYamlOrJsonOrThrow(CHAIN_2_METADATA_PATH); + chain3Metadata = readYamlOrJsonOrThrow(CHAIN_3_METADATA_PATH); + chain4Metadata = readYamlOrJsonOrThrow(CHAIN_4_METADATA_PATH); console.log('Bridging tokens...'); diff --git a/typescript/cli/src/tests/ethereum/warp/warp-send.e2e-test.ts b/typescript/cli/src/tests/ethereum/warp/warp-send.e2e-test.ts index 04510abc058..337e0da7d59 100644 --- a/typescript/cli/src/tests/ethereum/warp/warp-send.e2e-test.ts +++ b/typescript/cli/src/tests/ethereum/warp/warp-send.e2e-test.ts @@ -22,7 +22,11 @@ import { import { type Address, randomInt } from '@hyperlane-xyz/utils'; import { WarpSendLogs } from '../../../send/transfer.js'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJson, + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { deployOrUseExistingCore } from '../commands/core.js'; import { deployToken } from '../commands/helpers.js'; import { @@ -57,8 +61,12 @@ describe('hyperlane warp send e2e tests', async function () { deployOrUseExistingCore(CHAIN_NAME_3, CORE_CONFIG_PATH, ANVIL_KEY), ]); - const chain2Metadata: ChainMetadata = readYamlOrJson(CHAIN_2_METADATA_PATH); - const chain3Metadata: ChainMetadata = readYamlOrJson(CHAIN_3_METADATA_PATH); + const chain2Metadata = readYamlOrJsonOrThrow( + CHAIN_2_METADATA_PATH, + ); + const chain3Metadata = readYamlOrJsonOrThrow( + CHAIN_3_METADATA_PATH, + ); const providerChain2 = new JsonRpcProvider(chain2Metadata.rpcUrls[0].http); const providerChain3 = new JsonRpcProvider(chain3Metadata.rpcUrls[0].http); diff --git a/typescript/cli/src/tests/helpers/warp-hook-test-factory.ts b/typescript/cli/src/tests/helpers/warp-hook-test-factory.ts index 0445e464aa5..dfbd37f24cd 100644 --- a/typescript/cli/src/tests/helpers/warp-hook-test-factory.ts +++ b/typescript/cli/src/tests/helpers/warp-hook-test-factory.ts @@ -8,7 +8,7 @@ import { } from '@hyperlane-xyz/sdk'; import { type ProtocolType, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../utils/files.js'; +import { readYamlOrJsonOrThrow, writeYamlOrJson } from '../../utils/files.js'; import { type HyperlaneE2EWarpTestCommands } from '../commands/warp.js'; /** @@ -84,8 +84,10 @@ export function createHookUpdateTests( outputPath: config.warpReadOutputPath, }); - const updatedWarpDeployConfig: DerivedWarpRouteDeployConfig = - readYamlOrJson(config.warpReadOutputPath); + const updatedWarpDeployConfig = + readYamlOrJsonOrThrow( + config.warpReadOutputPath, + ); const hookConfig = updatedWarpDeployConfig[config.chainName].hook; @@ -151,8 +153,10 @@ export function createHookUpdateTests( outputPath: config.warpReadOutputPath, }); - const updatedWarpDeployConfig: DerivedWarpRouteDeployConfig = - readYamlOrJson(config.warpReadOutputPath); + const updatedWarpDeployConfig = + readYamlOrJsonOrThrow( + config.warpReadOutputPath, + ); const hookConfig = updatedWarpDeployConfig[config.chainName].hook; @@ -193,8 +197,10 @@ export function createHookUpdateTests( outputPath: config.warpReadOutputPath, }); - let updatedWarpDeployConfig: DerivedWarpRouteDeployConfig = - readYamlOrJson(config.warpReadOutputPath); + let updatedWarpDeployConfig = + readYamlOrJsonOrThrow( + config.warpReadOutputPath, + ); expect(updatedWarpDeployConfig[config.chainName].hook).to.not.be .undefined; @@ -215,7 +221,10 @@ export function createHookUpdateTests( outputPath: config.warpReadOutputPath, }); - updatedWarpDeployConfig = readYamlOrJson(config.warpReadOutputPath); + updatedWarpDeployConfig = + readYamlOrJsonOrThrow( + config.warpReadOutputPath, + ); const hookConfig = updatedWarpDeployConfig[config.chainName].hook; @@ -261,8 +270,10 @@ export function createHookUpdateTests( outputPath: config.warpReadOutputPath, }); - let updatedWarpDeployConfig: DerivedWarpRouteDeployConfig = - readYamlOrJson(config.warpReadOutputPath); + let updatedWarpDeployConfig = + readYamlOrJsonOrThrow( + config.warpReadOutputPath, + ); const initialHook = updatedWarpDeployConfig[config.chainName].hook; @@ -312,7 +323,10 @@ export function createHookUpdateTests( outputPath: config.warpReadOutputPath, }); - updatedWarpDeployConfig = readYamlOrJson(config.warpReadOutputPath); + updatedWarpDeployConfig = + readYamlOrJsonOrThrow( + config.warpReadOutputPath, + ); const finalHook = updatedWarpDeployConfig[config.chainName].hook; @@ -361,7 +375,7 @@ export function createHookUpdateTests( outputPath: config.warpReadOutputPath, }); - const firstConfig: DerivedWarpRouteDeployConfig = readYamlOrJson( + const firstConfig = readYamlOrJsonOrThrow( config.warpReadOutputPath, ); const firstHookConfig = firstConfig[config.chainName].hook; @@ -388,7 +402,7 @@ export function createHookUpdateTests( outputPath: config.warpReadOutputPath, }); - const secondConfig: DerivedWarpRouteDeployConfig = readYamlOrJson( + const secondConfig = readYamlOrJsonOrThrow( config.warpReadOutputPath, ); const secondHookConfig = secondConfig[config.chainName].hook; @@ -446,7 +460,7 @@ export function createHookUpdateTests( outputPath: config.warpReadOutputPath, }); - const firstConfig: DerivedWarpRouteDeployConfig = readYamlOrJson( + const firstConfig = readYamlOrJsonOrThrow( config.warpReadOutputPath, ); const firstHookConfig = firstConfig[config.chainName].hook; @@ -499,7 +513,7 @@ export function createHookUpdateTests( outputPath: config.warpReadOutputPath, }); - const secondConfig: DerivedWarpRouteDeployConfig = readYamlOrJson( + const secondConfig = readYamlOrJsonOrThrow( config.warpReadOutputPath, ); const secondHookConfig = secondConfig[config.chainName].hook; diff --git a/typescript/cli/src/tests/helpers/warp-ism-test-factory.ts b/typescript/cli/src/tests/helpers/warp-ism-test-factory.ts index 5bdac4ee9ca..7d7de5d8e67 100644 --- a/typescript/cli/src/tests/helpers/warp-ism-test-factory.ts +++ b/typescript/cli/src/tests/helpers/warp-ism-test-factory.ts @@ -15,7 +15,7 @@ import { normalizeAddress, } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../utils/files.js'; +import { readYamlOrJsonOrThrow, writeYamlOrJson } from '../../utils/files.js'; import { type HyperlaneE2ECoreTestCommands } from '../commands/core.js'; import { type HyperlaneE2EWarpTestCommands } from '../commands/warp.js'; @@ -94,7 +94,7 @@ export function createIsmUpdateTests( }); const updatedWarpDeployConfig: DerivedWarpRouteDeployConfig = - readYamlOrJson(config.warpReadOutputPath); + readYamlOrJsonOrThrow(config.warpReadOutputPath); const ismConfig = updatedWarpDeployConfig[config.chainName].interchainSecurityModule; @@ -155,7 +155,7 @@ export function createIsmUpdateTests( }); const updatedWarpDeployConfig: DerivedWarpRouteDeployConfig = - readYamlOrJson(config.warpReadOutputPath); + readYamlOrJsonOrThrow(config.warpReadOutputPath); const ismConfig = updatedWarpDeployConfig[config.chainName].interchainSecurityModule; @@ -201,7 +201,7 @@ export function createIsmUpdateTests( }); let updatedWarpDeployConfig: DerivedWarpRouteDeployConfig = - readYamlOrJson(config.warpReadOutputPath); + readYamlOrJsonOrThrow(config.warpReadOutputPath); expect(updatedWarpDeployConfig[config.chainName].interchainSecurityModule) .to.not.be.undefined; @@ -222,7 +222,9 @@ export function createIsmUpdateTests( outputPath: config.warpReadOutputPath, }); - updatedWarpDeployConfig = readYamlOrJson(config.warpReadOutputPath); + updatedWarpDeployConfig = readYamlOrJsonOrThrow( + config.warpReadOutputPath, + ); const ismConfig = updatedWarpDeployConfig[config.chainName].interchainSecurityModule; @@ -265,7 +267,7 @@ export function createIsmUpdateTests( }); let updatedWarpDeployConfig: DerivedWarpRouteDeployConfig = - readYamlOrJson(config.warpReadOutputPath); + readYamlOrJsonOrThrow(config.warpReadOutputPath); const initialIsm = updatedWarpDeployConfig[config.chainName].interchainSecurityModule; @@ -321,7 +323,9 @@ export function createIsmUpdateTests( outputPath: config.warpReadOutputPath, }); - updatedWarpDeployConfig = readYamlOrJson(config.warpReadOutputPath); + updatedWarpDeployConfig = readYamlOrJsonOrThrow( + config.warpReadOutputPath, + ); const finalIsm = updatedWarpDeployConfig[config.chainName].interchainSecurityModule; @@ -383,7 +387,7 @@ export function createIsmUpdateTests( outputPath: config.warpReadOutputPath, }); - const firstConfig: DerivedWarpRouteDeployConfig = readYamlOrJson( + const firstConfig: DerivedWarpRouteDeployConfig = readYamlOrJsonOrThrow( config.warpReadOutputPath, ); const firstIsmConfig = @@ -408,7 +412,7 @@ export function createIsmUpdateTests( outputPath: config.warpReadOutputPath, }); - const secondConfig: DerivedWarpRouteDeployConfig = readYamlOrJson( + const secondConfig: DerivedWarpRouteDeployConfig = readYamlOrJsonOrThrow( config.warpReadOutputPath, ); const secondIsmConfig = @@ -455,7 +459,7 @@ export function createIsmUpdateTests( outputPath: config.warpReadOutputPath, }); - const firstConfig: DerivedWarpRouteDeployConfig = readYamlOrJson( + const firstConfig: DerivedWarpRouteDeployConfig = readYamlOrJsonOrThrow( config.warpReadOutputPath, ); const firstIsmConfig = @@ -493,7 +497,7 @@ export function createIsmUpdateTests( outputPath: config.warpReadOutputPath, }); - const secondConfig: DerivedWarpRouteDeployConfig = readYamlOrJson( + const secondConfig: DerivedWarpRouteDeployConfig = readYamlOrJsonOrThrow( config.warpReadOutputPath, ); const secondIsmConfig = diff --git a/typescript/cli/src/tests/radix/core/core-apply.e2e-test.ts b/typescript/cli/src/tests/radix/core/core-apply.e2e-test.ts index 4f7106e9ecd..874bf0fb240 100644 --- a/typescript/cli/src/tests/radix/core/core-apply.e2e-test.ts +++ b/typescript/cli/src/tests/radix/core/core-apply.e2e-test.ts @@ -10,7 +10,10 @@ import { } from '@hyperlane-xyz/sdk'; import { ProtocolType, normalizeConfig } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { BURN_ADDRESS_BY_PROTOCOL, @@ -36,7 +39,7 @@ describe('hyperlane core apply (Radix E2E tests)', async function () { // Reset the core deploy config before each test beforeEach(async function () { - const coreConfig: CoreConfig = await readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( CORE_CONFIG_PATH_BY_PROTOCOL.radix, ); writeYamlOrJson( @@ -55,7 +58,7 @@ describe('hyperlane core apply (Radix E2E tests)', async function () { describe('hyperlane core apply (mailbox updates)', function () { it(`should update the mailbox owner to the specified one`, async () => { - const coreConfig: CoreConfig = await readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( CORE_CONFIG_PATH_BY_PROTOCOL.radix, ); @@ -102,7 +105,7 @@ describe('hyperlane core apply (Radix E2E tests)', async function () { for (const hookConfig of Object.values(testCases)) { for (const hookField of hookFields) { it(`should update the ${hookField} to a ${hookConfig.type}`, async () => { - const coreConfig: CoreConfig = await readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( CORE_CONFIG_PATH_BY_PROTOCOL.radix, ); @@ -155,7 +158,7 @@ describe('hyperlane core apply (Radix E2E tests)', async function () { for (const ismConfig of Object.values(testCases)) { it(`should update the defaultIsm to a ${ismConfig.type}`, async () => { - const coreConfig: CoreConfig = await readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( CORE_CONFIG_PATH_BY_PROTOCOL.radix, ); diff --git a/typescript/cli/src/tests/radix/core/core-deploy.e2e-test.ts b/typescript/cli/src/tests/radix/core/core-deploy.e2e-test.ts index f4fd425e574..373e777ec20 100644 --- a/typescript/cli/src/tests/radix/core/core-deploy.e2e-test.ts +++ b/typescript/cli/src/tests/radix/core/core-deploy.e2e-test.ts @@ -13,7 +13,10 @@ import { objLength, } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { BURN_ADDRESS_BY_PROTOCOL, @@ -168,7 +171,7 @@ describe('hyperlane core deploy (Radix E2E tests)', async function () { for (const { description, expect } of testCases) { it(description, async () => { - const coreConfig: CoreConfig = await readYamlOrJson( + const coreConfig = readYamlOrJsonOrThrow( CORE_CONFIG_PATH_BY_PROTOCOL.radix, ); diff --git a/typescript/cli/src/tests/radix/warp/warp-apply-ownership-updates.e2e-test.ts b/typescript/cli/src/tests/radix/warp/warp-apply-ownership-updates.e2e-test.ts index 69df6c29bcc..e7a186950dc 100644 --- a/typescript/cli/src/tests/radix/warp/warp-apply-ownership-updates.e2e-test.ts +++ b/typescript/cli/src/tests/radix/warp/warp-apply-ownership-updates.e2e-test.ts @@ -8,7 +8,10 @@ import { } from '@hyperlane-xyz/sdk'; import { type Address, ProtocolType, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../commands/warp.js'; import { @@ -168,8 +171,10 @@ describe('hyperlane warp apply (Radix E2E tests)', async function () { outputPath: WARP_READ_OUTPUT_PATH, }); - const updatedWarpDeployConfig: DerivedWarpRouteDeployConfig = - readYamlOrJson(WARP_READ_OUTPUT_PATH); + const updatedWarpDeployConfig = + readYamlOrJsonOrThrow( + WARP_READ_OUTPUT_PATH, + ); expect( updatedWarpDeployConfig[TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_1] diff --git a/typescript/cli/src/tests/radix/warp/warp-apply-route-extension.e2e-test.ts b/typescript/cli/src/tests/radix/warp/warp-apply-route-extension.e2e-test.ts index cd6b8b91bd8..b3130e7e299 100644 --- a/typescript/cli/src/tests/radix/warp/warp-apply-route-extension.e2e-test.ts +++ b/typescript/cli/src/tests/radix/warp/warp-apply-route-extension.e2e-test.ts @@ -7,7 +7,10 @@ import { } from '@hyperlane-xyz/sdk'; import { ProtocolType, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../commands/warp.js'; import { @@ -132,8 +135,10 @@ describe('hyperlane warp apply (Radix E2E tests)', async function () { outputPath: WARP_READ_OUTPUT_PATH, }); - const updatedWarpDeployConfig: DerivedWarpRouteDeployConfig = - readYamlOrJson(WARP_READ_OUTPUT_PATH); + const updatedWarpDeployConfig = + readYamlOrJsonOrThrow( + WARP_READ_OUTPUT_PATH, + ); for (const chainName of Object.keys(warpDeployConfig)) { assertWarpRouteConfig( diff --git a/typescript/cli/src/tests/radix/warp/warp-deploy.e2e-test.ts b/typescript/cli/src/tests/radix/warp/warp-deploy.e2e-test.ts index 974fcfeb8d2..da9691568f2 100644 --- a/typescript/cli/src/tests/radix/warp/warp-deploy.e2e-test.ts +++ b/typescript/cli/src/tests/radix/warp/warp-deploy.e2e-test.ts @@ -9,7 +9,10 @@ import { } from '@hyperlane-xyz/sdk'; import { ProtocolType, assert } from '@hyperlane-xyz/utils'; -import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js'; +import { + readYamlOrJsonOrThrow, + writeYamlOrJson, +} from '../../../utils/files.js'; import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js'; import { HyperlaneE2EWarpTestCommands } from '../../commands/warp.js'; import { @@ -135,7 +138,7 @@ describe('hyperlane warp deploy (Radix E2E tests)', async function () { outputPath: WARP_READ_OUTPUT_PATH, }); - const config: DerivedWarpRouteDeployConfig = readYamlOrJson( + const config = readYamlOrJsonOrThrow( WARP_READ_OUTPUT_PATH, ); @@ -170,7 +173,7 @@ describe('hyperlane warp deploy (Radix E2E tests)', async function () { outputPath: WARP_READ_OUTPUT_PATH, }); - const config: DerivedWarpRouteDeployConfig = readYamlOrJson( + const config = readYamlOrJsonOrThrow( WARP_READ_OUTPUT_PATH, ); diff --git a/typescript/cli/src/utils/files.ts b/typescript/cli/src/utils/files.ts index 951ae37118f..a7bcf1dd30d 100644 --- a/typescript/cli/src/utils/files.ts +++ b/typescript/cli/src/utils/files.ts @@ -4,7 +4,9 @@ import fs from 'fs'; import path from 'path'; import { LineCounter, stringify as yamlStringify } from 'yaml'; +import { assert } from '@hyperlane-xyz/utils'; import { + type FileFormat, indentYamlOrJson, // Re-export core fs utilities from utils/fs isFile, @@ -60,6 +62,29 @@ export type ArtifactsFile = { description: string; }; +/** + * Reads and parses a JSON file, throwing an error if the file is empty. + * Use this in tests or CLI commands where empty files indicate a configuration error. + */ +export function readJsonOrThrow(filepath: string): T { + const result = readJson(filepath); + assert(result, `Empty JSON file at ${filepath}`); + return result; +} + +/** + * Reads and parses a YAML or JSON file, throwing an error if the file is empty. + * Use this in tests or CLI commands where empty files indicate a configuration error. + */ +export function readYamlOrJsonOrThrow( + filepath: string, + format?: FileFormat, +): T { + const result = readYamlOrJson(filepath, format); + assert(result, `Empty config file at ${filepath}`); + return result; +} + /** * @deprecated Use tryReadYaml from @hyperlane-xyz/utils/fs instead */ diff --git a/typescript/deploy-sdk/src/AltVMFileSubmitter.ts b/typescript/deploy-sdk/src/AltVMFileSubmitter.ts index 90a56e5103a..d989e9dc95f 100644 --- a/typescript/deploy-sdk/src/AltVMFileSubmitter.ts +++ b/typescript/deploy-sdk/src/AltVMFileSubmitter.ts @@ -4,7 +4,7 @@ import { ITransactionSubmitter, } from '@hyperlane-xyz/provider-sdk'; import { AnnotatedTx, TxReceipt } from '@hyperlane-xyz/provider-sdk/module'; -import { Logger, assert, rootLogger } from '@hyperlane-xyz/utils'; +import { Logger, rootLogger } from '@hyperlane-xyz/utils'; import { readYamlOrJson, writeYamlOrJson } from '@hyperlane-xyz/utils/fs'; export class AltVMFileSubmitter implements ITransactionSubmitter { @@ -29,15 +29,15 @@ export class AltVMFileSubmitter implements ITransactionSubmitter { } // Attempt to append transactions to existing filepath. - try { - const maybeExistingTxs = readYamlOrJson(filepath); // Can throw if file is empty - assert( - Array.isArray(maybeExistingTxs), - `Target filepath ${filepath} has existing data, but is not an array. Overwriting.`, - ); - allTxs.unshift(...maybeExistingTxs); - } catch (e) { - this.logger.error(`Invalid transactions read from ${filepath}`, e); + const maybeExistingTxs = readYamlOrJson(filepath); + if (maybeExistingTxs !== null) { + if (!Array.isArray(maybeExistingTxs)) { + this.logger.debug( + `Target filepath ${filepath} has existing data, but is not an array. Overwriting.`, + ); + } else { + allTxs.unshift(...maybeExistingTxs); + } } writeYamlOrJson(filepath, allTxs); diff --git a/typescript/infra/scripts/agent-utils.ts b/typescript/infra/scripts/agent-utils.ts index a380d203508..787596fcff7 100644 --- a/typescript/infra/scripts/agent-utils.ts +++ b/typescript/infra/scripts/agent-utils.ts @@ -16,6 +16,7 @@ import { import { Address, ProtocolType, + assert, difference, inCIMode, objFilter, @@ -726,9 +727,10 @@ export function getAddresses( if (isRegistryModule(environment, module)) { addresses = getChainAddresses(); } else { - addresses = readJson>( - getInfraLandfillPath(environment, module), - ); + const path = getInfraLandfillPath(environment, module); + const loadedAddresses = readJson>(path); + assert(loadedAddresses, `Empty addresses file at ${path}`); + addresses = loadedAddresses; } // Filter by chains if specified, otherwise use environment chains diff --git a/typescript/infra/scripts/agents/update-agent-config.ts b/typescript/infra/scripts/agents/update-agent-config.ts index 6309d745802..4bff8986af6 100644 --- a/typescript/infra/scripts/agents/update-agent-config.ts +++ b/typescript/infra/scripts/agents/update-agent-config.ts @@ -251,7 +251,8 @@ export async function writeAgentConfig( const filepath = getAgentConfigJsonPath(envNameToAgentEnv[environment]); console.log(`Writing config to ${filepath}`); if (fs.existsSync(filepath)) { - const currentAgentConfig: AgentConfig = readJson(filepath); + const currentAgentConfig = readJson(filepath); + assert(currentAgentConfig, `Empty agent config at ${filepath}`); // Remove transactionOverrides from each chain in the agent config // To ensure all overrides are configured in infra code or the registry, and not in JSON for (const chainConfig of Object.values(currentAgentConfig.chains)) { @@ -289,6 +290,7 @@ export async function writeAgentAppContexts( console.log(`Writing config to ${filepath}`); if (fs.existsSync(filepath)) { const currentAgentConfigMap = readJson(filepath); + assert(currentAgentConfigMap, `Empty relayer config at ${filepath}`); writeAndFormatJsonAtPath( filepath, objMerge(currentAgentConfigMap, agentConfigMap), diff --git a/typescript/infra/scripts/funding/write-alert.ts b/typescript/infra/scripts/funding/write-alert.ts index ce01616002e..75fdc6f6201 100644 --- a/typescript/infra/scripts/funding/write-alert.ts +++ b/typescript/infra/scripts/funding/write-alert.ts @@ -3,7 +3,7 @@ import { ChildProcess } from 'child_process'; import yargs from 'yargs'; import { ChainMap } from '@hyperlane-xyz/sdk'; -import { rootLogger } from '@hyperlane-xyz/utils'; +import { assert, rootLogger } from '@hyperlane-xyz/utils'; import { readJson } from '@hyperlane-xyz/utils/fs'; import { @@ -85,10 +85,10 @@ async function main() { const alertRule = await fetchGrafanaAlert(alert, saToken); // read the proposed thresholds from the config file - let proposedThresholds: ChainMap = {}; - proposedThresholds = readJson( + const proposedThresholds = readJson>( `${THRESHOLD_CONFIG_PATH}/${alertConfigMapping[alert].configFileName}`, ); + assert(proposedThresholds, `Empty thresholds config for ${alert}`); // parse the current thresholds from the existing query const existingQuery = alertRule.queries[0]; @@ -172,9 +172,10 @@ async function validateBalanceThresholdConfigs() { const balanceThresholdTypes = Object.values(BalanceThresholdType); const balanceThresholdConfigs = balanceThresholdTypes.reduce( (acc, balanceThresholdType) => { - const thresholds = readJson( + const thresholds = readJson>( `${THRESHOLD_CONFIG_PATH}/${balanceThresholdConfigMapping[balanceThresholdType].configFileName}`, - ) as ChainMap; + ); + assert(thresholds, `Empty thresholds config for ${balanceThresholdType}`); return { ...acc, diff --git a/typescript/infra/scripts/safes/combine-txs.ts b/typescript/infra/scripts/safes/combine-txs.ts index c483bb462da..9cbf053d54e 100644 --- a/typescript/infra/scripts/safes/combine-txs.ts +++ b/typescript/infra/scripts/safes/combine-txs.ts @@ -5,7 +5,7 @@ import * as fs from 'fs'; import * as path from 'path'; import yargs from 'yargs'; -import { rootLogger } from '@hyperlane-xyz/utils'; +import { assert, rootLogger } from '@hyperlane-xyz/utils'; import { readJson } from '@hyperlane-xyz/utils/fs'; import { writeAndFormatJsonAtPath } from '../../src/utils/utils.js'; @@ -39,7 +39,9 @@ function readJSONFiles(directory: string): Record { } txs = arr[0]; } else { - txs = readJson(filePath); + const loadedTxs = readJson(filePath); + assert(loadedTxs, `Empty transaction file at ${filePath}`); + txs = loadedTxs; } const chainId = txs.chainId; diff --git a/typescript/infra/scripts/sealevel-helpers/update-multisig-ism-config.ts b/typescript/infra/scripts/sealevel-helpers/update-multisig-ism-config.ts index fa5a2d36c87..9d6d10623d2 100644 --- a/typescript/infra/scripts/sealevel-helpers/update-multisig-ism-config.ts +++ b/typescript/infra/scripts/sealevel-helpers/update-multisig-ism-config.ts @@ -13,7 +13,7 @@ import { MultiProtocolProvider, SvmMultiProtocolSignerAdapter, } from '@hyperlane-xyz/sdk'; -import { ProtocolType, rootLogger } from '@hyperlane-xyz/utils'; +import { ProtocolType, assert, rootLogger } from '@hyperlane-xyz/utils'; import { readJson } from '@hyperlane-xyz/utils/fs'; import { Contexts } from '../../config/contexts.js'; @@ -373,7 +373,8 @@ async function processChain( // Load configuration from file const configPath = multisigIsmConfigPath(environment, context, chain); - const config: SvmMultisigConfigMap = readJson(configPath); + const config = readJson(configPath); + assert(config, `Empty MultisigIsm config file at ${configPath}`); rootLogger.info( chalk.gray( diff --git a/typescript/infra/scripts/verify.ts b/typescript/infra/scripts/verify.ts index 64228204c54..034bd726130 100644 --- a/typescript/infra/scripts/verify.ts +++ b/typescript/infra/scripts/verify.ts @@ -4,6 +4,7 @@ import { PostDeploymentContractVerifier, VerificationInput, } from '@hyperlane-xyz/sdk'; +import { assert } from '@hyperlane-xyz/utils'; import { readJson } from '@hyperlane-xyz/utils/fs'; import { assertEnvironment } from '../src/config/environment.js'; @@ -33,9 +34,13 @@ async function main() { const multiProvider = await config.getMultiProvider(); // grab verification artifacts - const verification: ChainMap = readJson( + const verification = readJson>( verificationArtifactPath, ); + assert( + verification, + `Empty verification artifact at ${verificationArtifactPath}`, + ); // fetch explorer API keys from GCP const apiKeys = await fetchExplorerApiKeys(); diff --git a/typescript/infra/src/agents/key-utils.ts b/typescript/infra/src/agents/key-utils.ts index 2b42904135e..e898fb00862 100644 --- a/typescript/infra/src/agents/key-utils.ts +++ b/typescript/infra/src/agents/key-utils.ts @@ -6,6 +6,7 @@ import { ChainMap, ChainName } from '@hyperlane-xyz/sdk'; import { Address, ProtocolType, + assert, deepEquals, objMap, rootLogger, @@ -578,10 +579,11 @@ export async function persistValidatorAddressesToLocalArtifacts( export function fetchLocalKeyAddresses(role: Role): LocalRoleAddresses { try { - const addresses: LocalRoleAddresses = readJsonFromDir( + const addresses = readJsonFromDir( CONFIG_DIRECTORY_PATH, `${role}.json`, ); + assert(addresses, `Empty key addresses file for role ${role}`); logger.debug(`Fetching addresses locally for ${role} role ...`); return addresses; diff --git a/typescript/infra/src/config/gas-oracle.ts b/typescript/infra/src/config/gas-oracle.ts index 2cbd1c190aa..6eb0dc4962e 100644 --- a/typescript/infra/src/config/gas-oracle.ts +++ b/typescript/infra/src/config/gas-oracle.ts @@ -77,6 +77,7 @@ export function loadAndValidateGasOracleConfig( configPath: string, ): ChainMap> { const rawConfig = readJson(configPath); + assert(rawConfig, `Empty gas oracle config file at ${configPath}`); try { const validated = GasOracleConfigFileSchema.parse(rawConfig); diff --git a/typescript/infra/src/deployment/deploy.ts b/typescript/infra/src/deployment/deploy.ts index f96ae8e8f63..d31acd1a633 100644 --- a/typescript/infra/src/deployment/deploy.ts +++ b/typescript/infra/src/deployment/deploy.ts @@ -250,7 +250,7 @@ async function postDeploy( let savedVerification = {}; try { - savedVerification = readJson(cache.verification); + savedVerification = readJson(cache.verification) ?? {}; } catch (e) { console.error( chalk.red('Failed to load cached verification inputs. Error: ', e), diff --git a/typescript/infra/src/deployment/verify.ts b/typescript/infra/src/deployment/verify.ts index 808863ee316..9275e299ffe 100644 --- a/typescript/infra/src/deployment/verify.ts +++ b/typescript/infra/src/deployment/verify.ts @@ -1,4 +1,5 @@ import { BuildArtifact, ChainMap } from '@hyperlane-xyz/sdk'; +import { assert } from '@hyperlane-xyz/utils'; import { readJson } from '@hyperlane-xyz/utils/fs'; import { fetchGCPSecret } from '../utils/gcloud.js'; @@ -11,7 +12,9 @@ export function extractBuildArtifact(buildArtifactPath: string): BuildArtifact { } // return as BuildArtifact - return readJson(buildArtifactPath); + const artifact = readJson(buildArtifactPath); + assert(artifact, `Empty build artifact at ${buildArtifactPath}`); + return artifact; } // fetch explorer API keys from GCP diff --git a/typescript/infra/src/funding/balances.ts b/typescript/infra/src/funding/balances.ts index b2a46bd3716..5b91aa1b5b4 100644 --- a/typescript/infra/src/funding/balances.ts +++ b/typescript/infra/src/funding/balances.ts @@ -1,5 +1,5 @@ import { ChainMap } from '@hyperlane-xyz/sdk'; -import { rootLogger } from '@hyperlane-xyz/utils'; +import { assert, rootLogger } from '@hyperlane-xyz/utils'; import { readJson } from '@hyperlane-xyz/utils/fs'; import { @@ -107,6 +107,7 @@ export function readAllThresholds(): ThresholdsData { const thresholdsFile = `${THRESHOLD_CONFIG_PATH}/${balanceThresholdConfigMapping[thresholdType].configFileName}`; const chainMap = readJson>(thresholdsFile); + assert(chainMap, `Empty thresholds file at ${thresholdsFile}`); result[thresholdType] = chainMap; } diff --git a/typescript/infra/src/utils/sealevel.ts b/typescript/infra/src/utils/sealevel.ts index 6647e51b393..e113eb4b26d 100644 --- a/typescript/infra/src/utils/sealevel.ts +++ b/typescript/infra/src/utils/sealevel.ts @@ -27,7 +27,7 @@ import { SvmMultiProtocolSignerAdapter, } from '@hyperlane-xyz/sdk'; import { SealevelMultisigIsmInstructionType as SdkMultisigIsmInstructionType } from '@hyperlane-xyz/sdk'; -import { rootLogger } from '@hyperlane-xyz/utils'; +import { assert, rootLogger } from '@hyperlane-xyz/utils'; import { readJson } from '@hyperlane-xyz/utils/fs'; import { Contexts } from '../../config/contexts.js'; @@ -501,7 +501,9 @@ export function loadCoreProgramIds( ); try { - return readJson(programIdsPath); + const programIds = readJson(programIdsPath); + assert(programIds, `Empty program IDs file at ${programIdsPath}`); + return programIds; } catch (error) { throw new Error(`Failed to load program IDs from ${programIdsPath}.`); } diff --git a/typescript/infra/src/utils/utils.ts b/typescript/infra/src/utils/utils.ts index fb11833c873..240e106ba6f 100644 --- a/typescript/infra/src/utils/utils.ts +++ b/typescript/infra/src/utils/utils.ts @@ -161,9 +161,11 @@ export async function writeJsonWithAppendMode( let data = newData; if (appendMode && pathExists(filepath)) { const existing = readJson>(filepath); - data = Object.fromEntries( - Object.keys(newData).map((key) => [key, existing[key] ?? newData[key]]), - ); + if (existing) { + data = Object.fromEntries( + Object.keys(newData).map((key) => [key, existing[key] ?? newData[key]]), + ); + } } await writeAndFormatJsonAtPath(filepath, data); } diff --git a/typescript/infra/test/agent-configs.test.ts b/typescript/infra/test/agent-configs.test.ts index ebbe04a6a88..d8f584dbe5a 100644 --- a/typescript/infra/test/agent-configs.test.ts +++ b/typescript/infra/test/agent-configs.test.ts @@ -21,14 +21,14 @@ const environmentChainConfigs = { // to get around the agent JSON configs living outside the typescript rootDir agentJsonConfig: readJson( getAgentConfigJsonPath(AgentEnvironment.Mainnet), - ), + )!, supportedChainNames: mainnet3SupportedChainNames, }, testnet4: { agentChainConfig: testnet4AgentChainConfig, agentJsonConfig: readJson( getAgentConfigJsonPath(AgentEnvironment.Testnet), - ), + )!, supportedChainNames: testnet4SupportedChainNames, }, }; diff --git a/typescript/infra/test/balance-alerts.test.ts b/typescript/infra/test/balance-alerts.test.ts index 26b5a1d501b..1c862a08c99 100644 --- a/typescript/infra/test/balance-alerts.test.ts +++ b/typescript/infra/test/balance-alerts.test.ts @@ -54,6 +54,9 @@ describe('Balance Alert Thresholds', async function () { const proposedThresholds = readJson>( `${THRESHOLD_CONFIG_PATH}/${alertConfigMapping[alert].configFileName}`, ); + if (!proposedThresholds) { + throw new Error(`Empty thresholds file for ${alert}`); + } // Compare thresholds const allChains = new Set([ diff --git a/typescript/rebalancer/src/config/RebalancerConfig.ts b/typescript/rebalancer/src/config/RebalancerConfig.ts index 149f873f346..4fd241535cb 100644 --- a/typescript/rebalancer/src/config/RebalancerConfig.ts +++ b/typescript/rebalancer/src/config/RebalancerConfig.ts @@ -1,5 +1,6 @@ import { fromZodError } from 'zod-validation-error'; +import { assert } from '@hyperlane-xyz/utils'; import { readYamlOrJson } from '@hyperlane-xyz/utils/fs'; import { @@ -20,7 +21,8 @@ export class RebalancerConfig { * @param configFilePath Path to the config file */ static load(configFilePath: string) { - const config: RebalancerConfigFileInput = readYamlOrJson(configFilePath); + const config = readYamlOrJson(configFilePath); + assert(config, `Empty rebalancer config file at ${configFilePath}`); const validationResult = RebalancerConfigSchema.safeParse(config); diff --git a/typescript/utils/src/fs/format.test.ts b/typescript/utils/src/fs/format.test.ts index 8113f80037e..17928a8ad7e 100644 --- a/typescript/utils/src/fs/format.test.ts +++ b/typescript/utils/src/fs/format.test.ts @@ -117,6 +117,70 @@ describe('Format utilities', () => { }); }); + describe('readYamlOrJson - empty files', () => { + it('returns null for empty JSON file', () => { + const jsonFile = path.join(testDir, 'empty.json'); + fs.writeFileSync(jsonFile, ''); + const result = readYamlOrJson(jsonFile); + expect(result).to.be.null; + }); + + it('returns null for empty YAML file (.yaml)', () => { + const yamlFile = path.join(testDir, 'empty.yaml'); + fs.writeFileSync(yamlFile, ''); + const result = readYamlOrJson(yamlFile); + expect(result).to.be.null; + }); + + it('returns null for empty YAML file (.yml)', () => { + const ymlFile = path.join(testDir, 'empty.yml'); + fs.writeFileSync(ymlFile, ''); + const result = readYamlOrJson(ymlFile); + expect(result).to.be.null; + }); + + it('returns null for empty file with explicit json format', () => { + const txtFile = path.join(testDir, 'empty.txt'); + fs.writeFileSync(txtFile, ''); + const result = readYamlOrJson(txtFile, 'json'); + expect(result).to.be.null; + }); + + it('returns null for empty file with explicit yaml format', () => { + const txtFile = path.join(testDir, 'empty.txt'); + fs.writeFileSync(txtFile, ''); + const result = readYamlOrJson(txtFile, 'yaml'); + expect(result).to.be.null; + }); + + it('returns null for JSON file with only whitespace', () => { + const jsonFile = path.join(testDir, 'whitespace.json'); + fs.writeFileSync(jsonFile, ' \n\t \n'); + const result = readYamlOrJson(jsonFile); + expect(result).to.be.null; + }); + + it('returns null for YAML file with only whitespace', () => { + const yamlFile = path.join(testDir, 'whitespace.yaml'); + fs.writeFileSync(yamlFile, ' \n\t \n'); + const result = readYamlOrJson(yamlFile); + expect(result).to.be.null; + }); + + it('returns null for YAML file with only comments', () => { + const yamlFile = path.join(testDir, 'comments.yaml'); + fs.writeFileSync(yamlFile, '# comment\n# another comment\n'); + const result = readYamlOrJson(yamlFile); + expect(result).to.be.null; + }); + + it('throws for malformed JSON file', () => { + const jsonFile = path.join(testDir, 'malformed.json'); + fs.writeFileSync(jsonFile, '{ invalid json }'); + expect(() => readYamlOrJson(jsonFile)).to.throw(); + }); + }); + describe('writeYamlOrJson', () => { it('writes JSON file based on extension', () => { const jsonFile = path.join(testDir, 'test.json'); @@ -184,5 +248,21 @@ describe('Format utilities', () => { const result = readYamlOrJson>(yamlFile); expect(result).to.deep.equal({ existing: 'data', new: 'data' }); }); + + it('writes only new data when JSON file is empty', () => { + const jsonFile = path.join(testDir, 'empty.json'); + fs.writeFileSync(jsonFile, ''); + mergeYamlOrJson(jsonFile, { new: 'data' }, 'json'); + const result = readYamlOrJson>(jsonFile); + expect(result).to.deep.equal({ new: 'data' }); + }); + + it('writes only new data when YAML file is empty', () => { + const yamlFile = path.join(testDir, 'empty.yaml'); + fs.writeFileSync(yamlFile, ''); + mergeYamlOrJson(yamlFile, { new: 'data' }); + const result = readYamlOrJson>(yamlFile); + expect(result).to.deep.equal({ new: 'data' }); + }); }); }); diff --git a/typescript/utils/src/fs/format.ts b/typescript/utils/src/fs/format.ts index 532e9d04192..ed0ac6835a0 100644 --- a/typescript/utils/src/fs/format.ts +++ b/typescript/utils/src/fs/format.ts @@ -61,8 +61,16 @@ function resolveYamlOrJsonFn( /** * Reads and parses a YAML or JSON file based on extension or explicit format. */ -export function readYamlOrJson(filepath: string, format?: FileFormat): T { - return resolveYamlOrJsonFn(filepath, readJson, readYaml, format); +export function readYamlOrJson( + filepath: string, + format?: FileFormat, +): T | null { + return resolveYamlOrJsonFn( + filepath, + readJson, + readYaml, + format, + ); } /** diff --git a/typescript/utils/src/fs/json.ts b/typescript/utils/src/fs/json.ts index 652fcadcdcd..06f33f2c660 100644 --- a/typescript/utils/src/fs/json.ts +++ b/typescript/utils/src/fs/json.ts @@ -6,9 +6,14 @@ import { isFile, pathExists, readFileAtPath, writeToFile } from './utils.js'; /** * Reads and parses a JSON file. + * Returns null for empty or whitespace-only files (matching YAML behavior). */ -export function readJson(filepath: string): T { - return JSON.parse(readFileAtPath(filepath)) as T; +export function readJson(filepath: string): T | null { + const content = readFileAtPath(filepath); + if (content.trim() === '') { + return null; + } + return JSON.parse(content) as T; } /** @@ -40,7 +45,7 @@ export function mergeJson>( ): void { if (isFile(filepath)) { const previous = readJson(filepath); - writeJson(filepath, objMerge(previous, obj)); + writeJson(filepath, previous ? objMerge(previous, obj) : obj); } else { writeJson(filepath, obj); } @@ -49,7 +54,10 @@ export function mergeJson>( /** * Reads JSON from a directory with the specified filename. */ -export function readJsonFromDir(directory: string, filename: string): T { +export function readJsonFromDir( + directory: string, + filename: string, +): T | null { return readJson(path.join(directory, filename)); } @@ -88,7 +96,7 @@ export function writeJsonWithAppendMode( let data = newData; if (appendMode && pathExists(filepath)) { const existing = readJson>(filepath); - // Merge newData into existing, preserving existing values for keys that already exist + // Spreading null is safe: {...newData, ...null} equals {...newData} data = { ...newData, ...existing }; } writeJson(filepath, data); diff --git a/typescript/utils/src/fs/yaml.ts b/typescript/utils/src/fs/yaml.ts index 35a6da464b3..d25148cb303 100644 --- a/typescript/utils/src/fs/yaml.ts +++ b/typescript/utils/src/fs/yaml.ts @@ -60,7 +60,7 @@ export function mergeYaml>( ): void { if (isFile(filepath)) { const previous = readYaml(filepath); - writeYaml(filepath, objMerge(previous, obj)); + writeYaml(filepath, previous ? objMerge(previous, obj) : obj); } else { writeYaml(filepath, obj); }