From 35d2aa4c955715347de6a963c086e8ba54c411f0 Mon Sep 17 00:00:00 2001 From: Jan Nanista Date: Fri, 8 Dec 2023 15:48:31 -0800 Subject: [PATCH] chore: Stub of OApp wire task --- .../__data__/configs/invalid.config.001.js | 14 +++ .../__data__/configs/invalid.config.empty.js | 0 .../configs/invalid.config.empty.json | 0 .../__data__/configs/valid.config.empty.js | 4 + .../task/oapp/__snapshots__/wire.test.ts.snap | 24 ++++ .../test/task/oapp/wire.test.ts | 107 ++++++++++++++++++ .../ua-utils-evm-hardhat/src/tasks/index.ts | 1 + .../src/tasks/oapp/index.ts | 1 + .../src/tasks/oapp/wire.ts | 62 ++++++++++ 9 files changed, 213 insertions(+) create mode 100644 packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/invalid.config.001.js create mode 100644 packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/invalid.config.empty.js create mode 100644 packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/invalid.config.empty.json create mode 100644 packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/valid.config.empty.js create mode 100644 packages/ua-utils-evm-hardhat-test/test/task/oapp/__snapshots__/wire.test.ts.snap create mode 100644 packages/ua-utils-evm-hardhat-test/test/task/oapp/wire.test.ts create mode 100644 packages/ua-utils-evm-hardhat/src/tasks/oapp/index.ts create mode 100644 packages/ua-utils-evm-hardhat/src/tasks/oapp/wire.ts diff --git a/packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/invalid.config.001.js b/packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/invalid.config.001.js new file mode 100644 index 000000000..56f925b50 --- /dev/null +++ b/packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/invalid.config.001.js @@ -0,0 +1,14 @@ +const { EndpointId } = require('@layerzerolabs/lz-definitions'); + +module.exports = { + contracts: [ + { + eid: EndpointId.EON_MAINNET, + contractName: 'DefaultOApp', + }, + { + eid: 'Invalid EndpointId', + contractName: 'DefaultOApp', + }, + ], +}; diff --git a/packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/invalid.config.empty.js b/packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/invalid.config.empty.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/invalid.config.empty.json b/packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/invalid.config.empty.json new file mode 100644 index 000000000..e69de29bb diff --git a/packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/valid.config.empty.js b/packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/valid.config.empty.js new file mode 100644 index 000000000..6b37271a9 --- /dev/null +++ b/packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/valid.config.empty.js @@ -0,0 +1,4 @@ +module.exports = { + contracts: [], + connections: [], +}; diff --git a/packages/ua-utils-evm-hardhat-test/test/task/oapp/__snapshots__/wire.test.ts.snap b/packages/ua-utils-evm-hardhat-test/test/task/oapp/__snapshots__/wire.test.ts.snap new file mode 100644 index 000000000..597e4c353 --- /dev/null +++ b/packages/ua-utils-evm-hardhat-test/test/task/oapp/__snapshots__/wire.test.ts.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`task/oapp/wire with invalid configs should fail with a malformed JS file (001) 1`] = ` +[Error: Config from file '/app/packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/invalid.config.001.js' is malformed. Please fix the following errors: + + +contracts: +- Property 'contracts.0.contract': Invalid input +- Property 'contracts.1.contract': Invalid input, +connections: +- Property 'connections': Required] +`; + +exports[`task/oapp/wire with invalid configs should fail with an empty JS file 1`] = ` +[Error: Config from file '/app/packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/invalid.config.empty.js' is malformed. Please fix the following errors: + + +contracts: +- Property 'contracts': Required, +connections: +- Property 'connections': Required] +`; + +exports[`task/oapp/wire with invalid configs should fail with an empty JSON file 1`] = `[Error: Unable to read config file '/app/packages/ua-utils-evm-hardhat-test/test/task/oapp/__data__/configs/invalid.config.empty.json': SyntaxError: Unexpected end of JSON input]`; diff --git a/packages/ua-utils-evm-hardhat-test/test/task/oapp/wire.test.ts b/packages/ua-utils-evm-hardhat-test/test/task/oapp/wire.test.ts new file mode 100644 index 000000000..6ba8a47aa --- /dev/null +++ b/packages/ua-utils-evm-hardhat-test/test/task/oapp/wire.test.ts @@ -0,0 +1,107 @@ +import { setupDefaultEndpoint } from '../../__utils__/endpoint' +import hre from 'hardhat' +import { isFile, promptToContinue } from '@layerzerolabs/io-utils' +import { resolve } from 'path' +import { TASK_LZ_WIRE_OAPP } from '@layerzerolabs/ua-utils-evm-hardhat' + +jest.mock('@layerzerolabs/io-utils', () => { + const original = jest.requireActual('@layerzerolabs/io-utils') + + return { + ...original, + promptToContinue: jest.fn().mockRejectedValue('Not mocked'), + } +}) + +const promptToContinueMock = promptToContinue as jest.Mock + +describe('task/oapp/wire', () => { + const CONFIGS_BASE_DIR = resolve(__dirname, '__data__', 'configs') + const configPathFixture = (fileName: string): string => { + const path = resolve(CONFIGS_BASE_DIR, fileName) + + expect(isFile(path)).toBeTruthy() + + return path + } + + beforeEach(async () => { + promptToContinueMock.mockReset() + + await setupDefaultEndpoint() + }) + + describe('with invalid configs', () => { + it('should fail if the config file does not exist', async () => { + await expect(hre.run(TASK_LZ_WIRE_OAPP, { oappConfig: './does-not-exist.js' })).rejects.toThrow( + /Unable to read config file/ + ) + }) + + it('should fail if the config file is not a file', async () => { + await expect(hre.run(TASK_LZ_WIRE_OAPP, { oappConfig: __dirname })).rejects.toThrow( + /Unable to read config file/ + ) + }) + + it('should fail if the config file is not a valid JSON or JS file', async () => { + const readme = resolve(__dirname, '..', '..', '..', 'README.md') + + expect(isFile(readme)).toBeTruthy() + + await expect(hre.run(TASK_LZ_WIRE_OAPP, { oappConfig: readme })).rejects.toThrow( + /Unable to read config file/ + ) + }) + + it('should fail with an empty JSON file', async () => { + const oappConfig = configPathFixture('invalid.config.empty.json') + + await expect(hre.run(TASK_LZ_WIRE_OAPP, { oappConfig })).rejects.toMatchSnapshot() + }) + + it('should fail with an empty JS file', async () => { + const oappConfig = configPathFixture('invalid.config.empty.js') + + await expect(hre.run(TASK_LZ_WIRE_OAPP, { oappConfig })).rejects.toMatchSnapshot() + }) + + it('should fail with a malformed JS file (001)', async () => { + const oappConfig = configPathFixture('invalid.config.001.js') + + await expect(hre.run(TASK_LZ_WIRE_OAPP, { oappConfig })).rejects.toMatchSnapshot() + }) + }) + + describe('with valid configs', () => { + it('should ask the user whether they want to continue', async () => { + const oappConfig = configPathFixture('valid.config.empty.js') + + promptToContinueMock.mockResolvedValue(true) + + await hre.run(TASK_LZ_WIRE_OAPP, { oappConfig }) + + expect(promptToContinueMock).toHaveBeenCalledTimes(1) + }) + + it('should return undefined if the user decides not to continue', async () => { + const oappConfig = configPathFixture('valid.config.empty.js') + + promptToContinueMock.mockResolvedValue(false) + + const result = await hre.run(TASK_LZ_WIRE_OAPP, { oappConfig }) + + expect(result).toBeUndefined() + }) + + it('should return a list of transactions if the user decides to continue', async () => { + const oappConfig = configPathFixture('valid.config.empty.js') + + promptToContinueMock.mockResolvedValue(true) + + const result = await hre.run(TASK_LZ_WIRE_OAPP, { oappConfig }) + + expect(result).toEqual([]) + }) + }) +}) diff --git a/packages/ua-utils-evm-hardhat/src/tasks/index.ts b/packages/ua-utils-evm-hardhat/src/tasks/index.ts index edfb64bc4..be8df89d5 100644 --- a/packages/ua-utils-evm-hardhat/src/tasks/index.ts +++ b/packages/ua-utils-evm-hardhat/src/tasks/index.ts @@ -1 +1,2 @@ +import './oapp' import './getDefaultConfig' diff --git a/packages/ua-utils-evm-hardhat/src/tasks/oapp/index.ts b/packages/ua-utils-evm-hardhat/src/tasks/oapp/index.ts new file mode 100644 index 000000000..8010ab13b --- /dev/null +++ b/packages/ua-utils-evm-hardhat/src/tasks/oapp/index.ts @@ -0,0 +1 @@ +import './wire' diff --git a/packages/ua-utils-evm-hardhat/src/tasks/oapp/wire.ts b/packages/ua-utils-evm-hardhat/src/tasks/oapp/wire.ts new file mode 100644 index 000000000..10236677f --- /dev/null +++ b/packages/ua-utils-evm-hardhat/src/tasks/oapp/wire.ts @@ -0,0 +1,62 @@ +import { task, types } from 'hardhat/config' +import type { ActionType } from 'hardhat/types' +import { TASK_LZ_WIRE_OAPP } from '@/constants/tasks' +import { isFile, isReadable, promptToContinue } from '@layerzerolabs/io-utils' +import { OAppOmniGraphHardhat, OAppOmniGraphHardhatSchema } from '@/oapp' + +interface TaskArgs { + oappConfig: string +} + +const action: ActionType = async ({ oappConfig: oappConfigPath }) => { + // First we check that the config file is indeed there and we can read it + const isConfigReadable = isFile(oappConfigPath) && isReadable(oappConfigPath) + if (!isConfigReadable) { + throw new Error( + `Unable to read config file '${oappConfigPath}'. Check that the file exists and is readable to your terminal user` + ) + } + + // Now let's see if we can load the config file + let rawConfig: unknown + try { + rawConfig = require(oappConfigPath) + } catch (error) { + throw new Error(`Unable to read config file '${oappConfigPath}': ${error}`) + } + + // It's time to make sure that the config is not malformed + // + // At this stage we are only interested in the shape of the data, + // we are not checking whether the information makes sense (e.g. + // whether there are no missing nodes etc) + const configParseResult = OAppOmniGraphHardhatSchema.safeParse(rawConfig) + if (configParseResult.success === false) { + // FIXME Error formatting + const errors = configParseResult.error.flatten( + (issue) => `Property '${issue.path.join('.') ?? '[root]'}': ${issue.message}` + ) + const formErrors = errors.formErrors.map((error) => `- ${error}`).join(`\n`) + const fieldErrors = Object.entries(errors.fieldErrors).map( + ([field, errors]) => `\n${field}:\n${errors.map((error) => `- ${error}`).join(`\n`)}` + ) + const allErrors = [...formErrors, fieldErrors] + + throw new Error( + `Config from file '${oappConfigPath}' is malformed. Please fix the following errors:\n\n${allErrors}` + ) + } + + // At this point we have a correctly typed config + const config: OAppOmniGraphHardhat = configParseResult.data + + const go = await promptToContinue() + if (!go) { + return undefined + } + + return [] +} +task(TASK_LZ_WIRE_OAPP, 'Wire LayerZero OApp') + .addParam('oappConfig', 'Path to your LayerZero OApp config', './layerzero.config.js', types.string) + .setAction(action)