From 71793cbfdb830b182f423ca4072b4535433a7367 Mon Sep 17 00:00:00 2001 From: Jan Nanista Date: Tue, 5 Dec 2023 12:16:26 -0800 Subject: [PATCH 1/5] feat: Repeatable setup for Endpoint & Uln302 deployment & configuration --- packages/protocol-utils-evm/.eslintignore | 2 + packages/protocol-utils-evm/.prettierignore | 2 + packages/protocol-utils-evm/README.md | 33 ++++ packages/protocol-utils-evm/jest.config.js | 8 + packages/protocol-utils-evm/package.json | 69 +++++++ .../protocol-utils-evm/src/endpoint/index.ts | 1 + .../protocol-utils-evm/src/endpoint/sdk.ts | 62 +++++++ packages/protocol-utils-evm/src/index.ts | 2 + .../protocol-utils-evm/src/uln302/index.ts | 1 + .../protocol-utils-evm/src/uln302/schema.ts | 23 +++ packages/protocol-utils-evm/src/uln302/sdk.ts | 51 ++++++ .../protocol-utils-evm/tsconfig.build.json | 4 + packages/protocol-utils-evm/tsconfig.json | 11 ++ packages/protocol-utils-evm/tsup.config.ts | 14 ++ packages/protocol-utils/.eslintignore | 3 + packages/protocol-utils/.eslintrc.json | 3 + packages/protocol-utils/README.md | 31 ++++ packages/protocol-utils/jest.config.js | 8 + packages/protocol-utils/package.json | 52 ++++++ .../protocol-utils/src/endpoint/config.ts | 58 ++++++ packages/protocol-utils/src/endpoint/index.ts | 2 + packages/protocol-utils/src/endpoint/types.ts | 27 +++ packages/protocol-utils/src/index.ts | 2 + packages/protocol-utils/src/uln302/config.ts | 36 ++++ packages/protocol-utils/src/uln302/index.ts | 2 + packages/protocol-utils/src/uln302/types.ts | 29 +++ packages/protocol-utils/tsconfig.json | 11 ++ packages/protocol-utils/tsup.config.ts | 12 ++ .../ua-utils-evm-hardhat-test/package.json | 2 + .../test/__utils__/endpoint.ts | 172 ++++++++++++++++++ .../test/endpoint/config.test.ts | 76 ++++++++ .../test/oapp/config.test.ts | 5 + 32 files changed, 814 insertions(+) create mode 100644 packages/protocol-utils-evm/.eslintignore create mode 100644 packages/protocol-utils-evm/.prettierignore create mode 100644 packages/protocol-utils-evm/README.md create mode 100644 packages/protocol-utils-evm/jest.config.js create mode 100644 packages/protocol-utils-evm/package.json create mode 100644 packages/protocol-utils-evm/src/endpoint/index.ts create mode 100644 packages/protocol-utils-evm/src/endpoint/sdk.ts create mode 100644 packages/protocol-utils-evm/src/index.ts create mode 100644 packages/protocol-utils-evm/src/uln302/index.ts create mode 100644 packages/protocol-utils-evm/src/uln302/schema.ts create mode 100644 packages/protocol-utils-evm/src/uln302/sdk.ts create mode 100644 packages/protocol-utils-evm/tsconfig.build.json create mode 100644 packages/protocol-utils-evm/tsconfig.json create mode 100644 packages/protocol-utils-evm/tsup.config.ts create mode 100644 packages/protocol-utils/.eslintignore create mode 100644 packages/protocol-utils/.eslintrc.json create mode 100644 packages/protocol-utils/README.md create mode 100644 packages/protocol-utils/jest.config.js create mode 100644 packages/protocol-utils/package.json create mode 100644 packages/protocol-utils/src/endpoint/config.ts create mode 100644 packages/protocol-utils/src/endpoint/index.ts create mode 100644 packages/protocol-utils/src/endpoint/types.ts create mode 100644 packages/protocol-utils/src/index.ts create mode 100644 packages/protocol-utils/src/uln302/config.ts create mode 100644 packages/protocol-utils/src/uln302/index.ts create mode 100644 packages/protocol-utils/src/uln302/types.ts create mode 100644 packages/protocol-utils/tsconfig.json create mode 100644 packages/protocol-utils/tsup.config.ts create mode 100644 packages/ua-utils-evm-hardhat-test/test/__utils__/endpoint.ts create mode 100644 packages/ua-utils-evm-hardhat-test/test/endpoint/config.test.ts diff --git a/packages/protocol-utils-evm/.eslintignore b/packages/protocol-utils-evm/.eslintignore new file mode 100644 index 000000000..db4c6d9b6 --- /dev/null +++ b/packages/protocol-utils-evm/.eslintignore @@ -0,0 +1,2 @@ +dist +node_modules \ No newline at end of file diff --git a/packages/protocol-utils-evm/.prettierignore b/packages/protocol-utils-evm/.prettierignore new file mode 100644 index 000000000..763301fc0 --- /dev/null +++ b/packages/protocol-utils-evm/.prettierignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ \ No newline at end of file diff --git a/packages/protocol-utils-evm/README.md b/packages/protocol-utils-evm/README.md new file mode 100644 index 000000000..948e87333 --- /dev/null +++ b/packages/protocol-utils-evm/README.md @@ -0,0 +1,33 @@ +

+ + LayerZero + +

+ +

@layerzerolabs/protocol-utils-evm

+ + +

+ + NPM Version + + Downloads + + NPM License +

+ +Utilities for LayerZero EVM protocol contracts. + +## Installation + +```bash +npm install --save @layerzerolabs/protocol-utils-evm +``` + +```bash +yarn install @layerzerolabs/protocol-utils-evm +``` + +```bash +pnpm install @layerzerolabs/protocol-utils-evm +``` diff --git a/packages/protocol-utils-evm/jest.config.js b/packages/protocol-utils-evm/jest.config.js new file mode 100644 index 000000000..16148cfb1 --- /dev/null +++ b/packages/protocol-utils-evm/jest.config.js @@ -0,0 +1,8 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + moduleNameMapper: { + '^@/(.*)$': '/src/$1', + }, +}; diff --git a/packages/protocol-utils-evm/package.json b/packages/protocol-utils-evm/package.json new file mode 100644 index 000000000..eb54c9b54 --- /dev/null +++ b/packages/protocol-utils-evm/package.json @@ -0,0 +1,69 @@ +{ + "name": "@layerzerolabs/protocol-utils-evm", + "version": "0.0.1", + "private": true, + "description": "Utilities for LayerZero EVM protocol contracts", + "repository": { + "type": "git", + "url": "git+https://github.com/LayerZero-Labs/lz-utils.git", + "directory": "packages/protocol-utils-evm" + }, + "license": "MIT", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "require": "./dist/index.js", + "import": "./dist/index.mjs" + }, + "./*": { + "types": "./dist/*.d.ts", + "require": "./dist/*.js", + "import": "./dist/*.mjs" + } + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "files": [ + "./dist/index.*" + ], + "scripts": { + "prebuild": "npx tsc --noEmit -p tsconfig.build.json", + "build": "npx tsup", + "clean": "rm -rf dist", + "dev": "npx tsup --watch", + "lint": "npx eslint '**/*.{js,ts,json}'", + "test": "jest --passWithNoTests" + }, + "dependencies": { + "p-memoize": "~4.0.1" + }, + "devDependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/contracts": "^5.7.0", + "@ethersproject/providers": "^5.7.0", + "@layerzerolabs/lz-definitions": "~1.5.68", + "@layerzerolabs/protocol-utils": "~0.0.1", + "@layerzerolabs/test-utils": "~0.0.1", + "@layerzerolabs/utils": "~0.0.1", + "@layerzerolabs/utils-evm": "~0.0.1", + "@types/jest": "^29.5.10", + "fast-check": "^3.14.0", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.1", + "tsup": "~8.0.1", + "typescript": "^5.2.2" + }, + "peerDependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/contracts": "^5.7.0", + "@ethersproject/providers": "^5.7.0", + "@layerzerolabs/lz-definitions": "~1.5.68", + "@layerzerolabs/protocol-utils": "~0.0.1", + "@layerzerolabs/utils": "~0.0.1", + "@layerzerolabs/utils-evm": "~0.0.1" + } +} \ No newline at end of file diff --git a/packages/protocol-utils-evm/src/endpoint/index.ts b/packages/protocol-utils-evm/src/endpoint/index.ts new file mode 100644 index 000000000..7db67b18a --- /dev/null +++ b/packages/protocol-utils-evm/src/endpoint/index.ts @@ -0,0 +1 @@ +export * from './sdk' diff --git a/packages/protocol-utils-evm/src/endpoint/sdk.ts b/packages/protocol-utils-evm/src/endpoint/sdk.ts new file mode 100644 index 000000000..6fd33e56e --- /dev/null +++ b/packages/protocol-utils-evm/src/endpoint/sdk.ts @@ -0,0 +1,62 @@ +import type { IEndpoint } from '@layerzerolabs/protocol-utils' +import { formatEid, type Address, type OmniTransaction } from '@layerzerolabs/utils' +import type { EndpointId } from '@layerzerolabs/lz-definitions' +import { ignoreZero, makeZero, omniContractToPoint, type OmniContract } from '@layerzerolabs/utils-evm' + +export class Endpoint implements IEndpoint { + constructor(public readonly contract: OmniContract) {} + + async defaultReceiveLibrary(eid: EndpointId): Promise { + return ignoreZero(await this.contract.contract.defaultReceiveLibrary(eid)) + } + + async setDefaultReceiveLibrary( + eid: EndpointId, + lib: string | null | undefined, + gracePeriod: number = 0 + ): Promise { + const data = this.contract.contract.interface.encodeFunctionData('setDefaultReceiveLibrary', [ + eid, + makeZero(lib), + gracePeriod, + ]) + + return { + ...this.createTransaction(data), + description: `Setting default receive library for ${formatEid(eid)} to ${makeZero(lib)}`, + } + } + + async defaultSendLibrary(eid: EndpointId): Promise { + return ignoreZero(await this.contract.contract.defaultSendLibrary(eid)) + } + + async setDefaultSendLibrary(eid: EndpointId, lib: Address | null | undefined): Promise { + const data = this.contract.contract.interface.encodeFunctionData('setDefaultSendLibrary', [eid, makeZero(lib)]) + + return { + ...this.createTransaction(data), + description: `Setting default send library for ${formatEid(eid)} to ${lib}`, + } + } + + isRegisteredLibrary(lib: Address): Promise { + return this.contract.contract.isRegisteredLibrary(lib) + } + + async registerLibrary(lib: string): Promise { + const data = this.contract.contract.interface.encodeFunctionData('registerLibrary', [lib]) + + return { + ...this.createTransaction(data), + description: `Registering library ${lib}`, + } + } + + protected createTransaction(data: string): OmniTransaction { + return { + point: omniContractToPoint(this.contract), + data, + } + } +} diff --git a/packages/protocol-utils-evm/src/index.ts b/packages/protocol-utils-evm/src/index.ts new file mode 100644 index 000000000..ff12d505a --- /dev/null +++ b/packages/protocol-utils-evm/src/index.ts @@ -0,0 +1,2 @@ +export * from './endpoint' +export * from './uln302' diff --git a/packages/protocol-utils-evm/src/uln302/index.ts b/packages/protocol-utils-evm/src/uln302/index.ts new file mode 100644 index 000000000..7db67b18a --- /dev/null +++ b/packages/protocol-utils-evm/src/uln302/index.ts @@ -0,0 +1 @@ +export * from './sdk' diff --git a/packages/protocol-utils-evm/src/uln302/schema.ts b/packages/protocol-utils-evm/src/uln302/schema.ts new file mode 100644 index 000000000..e77f21fde --- /dev/null +++ b/packages/protocol-utils-evm/src/uln302/schema.ts @@ -0,0 +1,23 @@ +import { AddressSchema } from '@layerzerolabs/utils' +import { BigNumberishBigintSchema } from '@layerzerolabs/utils-evm' +import { z } from 'zod' + +/** + * Schema for parsing an ethers-specific UlnConfig into a common format + */ +export const Uln302UlnConfigSchema = z.object({ + confirmations: BigNumberishBigintSchema, + requiredDVNs: z.array(AddressSchema), + optionalDVNs: z.array(AddressSchema), + optionalDVNThreshold: z.coerce.number().int().nonnegative(), +}) + +/** + * Schema for parsing a common UlnConfig into a ethers-specific format + */ +export const Uln302UlnConfigInputSchema = Uln302UlnConfigSchema.transform((config) => ({ + ...config, + confirmations: String(config.confirmations), + requiredDVNCount: config.requiredDVNs.length, + optionalDVNCount: config.optionalDVNs.length, +})) diff --git a/packages/protocol-utils-evm/src/uln302/sdk.ts b/packages/protocol-utils-evm/src/uln302/sdk.ts new file mode 100644 index 000000000..cc77763e0 --- /dev/null +++ b/packages/protocol-utils-evm/src/uln302/sdk.ts @@ -0,0 +1,51 @@ +import type { EndpointId } from '@layerzerolabs/lz-definitions' +import type { IUln302, Uln302ExecutorConfig, Uln302UlnConfig } from '@layerzerolabs/protocol-utils' +import { formatEid, type OmniTransaction } from '@layerzerolabs/utils' +import { omniContractToPoint, type OmniContract } from '@layerzerolabs/utils-evm' +import { Uln302UlnConfigInputSchema, Uln302UlnConfigSchema } from './schema' + +export class Uln302 implements IUln302 { + constructor(public readonly contract: OmniContract) {} + + async getUlnConfig(eid: EndpointId, address: string): Promise { + const config = await this.contract.contract.getUlnConfig(address, eid) + + // Now we convert the ethers-specific object into the common structure + // + // Here we need to spread the config into an object because what ethers gives us + // is actually an array with extra properties + return Uln302UlnConfigSchema.parse({ ...config }) + } + + async setDefaultExecutorConfig(eid: EndpointId, config: Uln302ExecutorConfig): Promise { + const data = this.contract.contract.interface.encodeFunctionData('setDefaultExecutorConfigs', [ + [{ eid, config }], + ]) + + return this.createTransaction(data) + } + + async setDefaultUlnConfig(eid: EndpointId, config: Uln302UlnConfig): Promise { + const serializedConfig = Uln302UlnConfigInputSchema.parse(config) + const data = this.contract.contract.interface.encodeFunctionData('setDefaultUlnConfigs', [ + [ + { + eid, + config: serializedConfig, + }, + ], + ]) + + return { + ...this.createTransaction(data), + description: `Setting default ULN config for ${formatEid(eid)}: ${JSON.stringify(serializedConfig)}`, + } + } + + protected createTransaction(data: string): OmniTransaction { + return { + point: omniContractToPoint(this.contract), + data, + } + } +} diff --git a/packages/protocol-utils-evm/tsconfig.build.json b/packages/protocol-utils-evm/tsconfig.build.json new file mode 100644 index 000000000..0507620e8 --- /dev/null +++ b/packages/protocol-utils-evm/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "dist", "test"] +} diff --git a/packages/protocol-utils-evm/tsconfig.json b/packages/protocol-utils-evm/tsconfig.json new file mode 100644 index 000000000..acecf2754 --- /dev/null +++ b/packages/protocol-utils-evm/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "exclude": ["dist", "node_modules"], + "include": ["src", "test", "*.config.ts"], + "compilerOptions": { + "types": ["node", "jest"], + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/packages/protocol-utils-evm/tsup.config.ts b/packages/protocol-utils-evm/tsup.config.ts new file mode 100644 index 000000000..7ef46a5ad --- /dev/null +++ b/packages/protocol-utils-evm/tsup.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'tsup' + +export default defineConfig([ + { + entry: ['src/index.ts'], + outDir: './dist', + clean: true, + dts: true, + sourcemap: true, + splitting: false, + treeshake: true, + format: ['esm', 'cjs'], + }, +]) diff --git a/packages/protocol-utils/.eslintignore b/packages/protocol-utils/.eslintignore new file mode 100644 index 000000000..0f295f243 --- /dev/null +++ b/packages/protocol-utils/.eslintignore @@ -0,0 +1,3 @@ +.turbo +dist +node_modules \ No newline at end of file diff --git a/packages/protocol-utils/.eslintrc.json b/packages/protocol-utils/.eslintrc.json new file mode 100644 index 000000000..be97c53fb --- /dev/null +++ b/packages/protocol-utils/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.json" +} diff --git a/packages/protocol-utils/README.md b/packages/protocol-utils/README.md new file mode 100644 index 000000000..75fac67c1 --- /dev/null +++ b/packages/protocol-utils/README.md @@ -0,0 +1,31 @@ +

+ + LayerZero + +

+ +

@layerzerolabs/protocol-utils

+ + +

+ + NPM Version + + Downloads + + NPM License +

+ +## Installation + +```bash +npm install --save @layerzerolabs/protocol-utils +``` + +```bash +yarn install @layerzerolabs/protocol-utils +``` + +```bash +pnpm install @layerzerolabs/protocol-utils +``` diff --git a/packages/protocol-utils/jest.config.js b/packages/protocol-utils/jest.config.js new file mode 100644 index 000000000..16148cfb1 --- /dev/null +++ b/packages/protocol-utils/jest.config.js @@ -0,0 +1,8 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + moduleNameMapper: { + '^@/(.*)$': '/src/$1', + }, +}; diff --git a/packages/protocol-utils/package.json b/packages/protocol-utils/package.json new file mode 100644 index 000000000..f875c9f06 --- /dev/null +++ b/packages/protocol-utils/package.json @@ -0,0 +1,52 @@ +{ + "name": "@layerzerolabs/protocol-utils", + "version": "0.0.1", + "private": true, + "description": "Utilities for working with LayerZero protocol contracts", + "repository": { + "type": "git", + "url": "git+https://github.com/LayerZero-Labs/lz-utils.git", + "directory": "packages/protocol-utils" + }, + "license": "MIT", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "require": "./dist/index.js", + "import": "./dist/index.mjs" + } + }, + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "files": [ + "dist/", + "LICENSE" + ], + "scripts": { + "prebuild": "tsc -noEmit", + "build": "npx tsup", + "clean": "rm -rf dist", + "lint": "npx eslint '**/*.{js,ts,json}'", + "test": "jest --passWithNoTests" + }, + "devDependencies": { + "@layerzerolabs/lz-definitions": "~1.5.68", + "@layerzerolabs/test-utils": "~0.0.1", + "@layerzerolabs/utils": "~0.0.1", + "@types/jest": "^29.5.10", + "fast-check": "^3.14.0", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.1", + "tslib": "~2.6.2", + "tsup": "~8.0.1", + "typescript": "^5.2.2", + "zod": "^3.22.4" + }, + "peerDependencies": { + "@layerzerolabs/lz-definitions": "~1.5.68", + "@layerzerolabs/utils": "~0.0.1", + "zod": "^3.22.4" + } +} \ No newline at end of file diff --git a/packages/protocol-utils/src/endpoint/config.ts b/packages/protocol-utils/src/endpoint/config.ts new file mode 100644 index 000000000..78d7a87cd --- /dev/null +++ b/packages/protocol-utils/src/endpoint/config.ts @@ -0,0 +1,58 @@ +import { flattenTransactions, type OmniTransaction } from '@layerzerolabs/utils' +import type { EndpointFactory, EndpointOmniGraph } from './types' + +export type EndpointConfigurator = (graph: EndpointOmniGraph, createSdk: EndpointFactory) => Promise + +export const configureEndpoint: EndpointConfigurator = async (graph, createSdk) => + flattenTransactions([ + ...(await configureEndpointDefaultReceiveLibraries(graph, createSdk)), + ...(await configureEndpointDefaultSendLibraries(graph, createSdk)), + ]) + +export const configureEndpointDefaultReceiveLibraries: EndpointConfigurator = async (graph, createSdk) => + flattenTransactions( + await Promise.all( + graph.connections.map(async ({ vector: { from, to }, config }): Promise => { + const sdk = await createSdk(from) + const address = await sdk.defaultReceiveLibrary(to.eid) + + // If the library is already set as default, do nothing + if (config.defaultReceiveLibrary === address) return [] + + // We need to check whether the library has been registered before we set is as default + const isRegistered = await sdk.isRegisteredLibrary(config.defaultReceiveLibrary) + + return flattenTransactions([ + // We only want to register the library if it has not been registered yet + isRegistered ? undefined : await sdk.registerLibrary(config.defaultReceiveLibrary), + await sdk.setDefaultReceiveLibrary( + to.eid, + config.defaultReceiveLibrary, + config.defaultReceiveLibraryGracePeriod + ), + ]) + }) + ) + ) + +export const configureEndpointDefaultSendLibraries: EndpointConfigurator = async (graph, createSdk) => + flattenTransactions( + await Promise.all( + graph.connections.map(async ({ vector: { from, to }, config }): Promise => { + const sdk = await createSdk(from) + const address = await sdk.defaultSendLibrary(to.eid) + + // If the library is already set as default, do nothing + if (config.defaultSendLibrary === address) return [] + + // We need to check whether the library has been registered before we set is as default + const isRegistered = await sdk.isRegisteredLibrary(config.defaultSendLibrary) + + return flattenTransactions([ + // We only want to register the library if it has not been registered yet + isRegistered ? undefined : await sdk.registerLibrary(config.defaultSendLibrary), + await sdk.setDefaultSendLibrary(to.eid, config.defaultSendLibrary), + ]) + }) + ) + ) diff --git a/packages/protocol-utils/src/endpoint/index.ts b/packages/protocol-utils/src/endpoint/index.ts new file mode 100644 index 000000000..39bdac610 --- /dev/null +++ b/packages/protocol-utils/src/endpoint/index.ts @@ -0,0 +1,2 @@ +export * from './config' +export * from './types' diff --git a/packages/protocol-utils/src/endpoint/types.ts b/packages/protocol-utils/src/endpoint/types.ts new file mode 100644 index 000000000..d3f8ec1f5 --- /dev/null +++ b/packages/protocol-utils/src/endpoint/types.ts @@ -0,0 +1,27 @@ +import type { Address, OmniGraph, OmniPointBasedFactory, OmniTransaction } from '@layerzerolabs/utils' +import type { EndpointId } from '@layerzerolabs/lz-definitions' + +export interface IEndpoint { + defaultReceiveLibrary(eid: EndpointId): Promise
+ setDefaultReceiveLibrary( + eid: EndpointId, + lib: Address | null | undefined, + gracePeriod?: number + ): Promise + + defaultSendLibrary(eid: EndpointId): Promise
+ setDefaultSendLibrary(eid: EndpointId, lib: Address | null | undefined): Promise + + isRegisteredLibrary(lib: Address): Promise + registerLibrary(lib: Address): Promise +} + +export interface EndpointEdgeConfig { + defaultReceiveLibrary: Address + defaultReceiveLibraryGracePeriod?: number + defaultSendLibrary: Address +} + +export type EndpointOmniGraph = OmniGraph + +export type EndpointFactory = OmniPointBasedFactory diff --git a/packages/protocol-utils/src/index.ts b/packages/protocol-utils/src/index.ts new file mode 100644 index 000000000..ff12d505a --- /dev/null +++ b/packages/protocol-utils/src/index.ts @@ -0,0 +1,2 @@ +export * from './endpoint' +export * from './uln302' diff --git a/packages/protocol-utils/src/uln302/config.ts b/packages/protocol-utils/src/uln302/config.ts new file mode 100644 index 000000000..ff8a6f074 --- /dev/null +++ b/packages/protocol-utils/src/uln302/config.ts @@ -0,0 +1,36 @@ +import { flattenTransactions, type OmniTransaction } from '@layerzerolabs/utils' +import type { Uln302Factory, Uln302OmniGraph } from './types' + +export type Uln302Configurator = (graph: Uln302OmniGraph, createSdk: Uln302Factory) => Promise + +export const configureUln302: Uln302Configurator = async (graph, createSdk) => + flattenTransactions([ + ...(await configureUln302DefaultExecutorConfigs(graph, createSdk)), + ...(await configureUln302DefaultUlnConfigs(graph, createSdk)), + ]) + +export const configureUln302DefaultExecutorConfigs: Uln302Configurator = async (graph, createSdk) => + flattenTransactions( + await Promise.all( + graph.contracts.map(async ({ point, config }): Promise => { + const sdk = await createSdk(point) + + return Promise.all( + config.defaultExecutorConfigs.map(([eid, config]) => sdk.setDefaultExecutorConfig(eid, config)) + ) + }) + ) + ) + +export const configureUln302DefaultUlnConfigs: Uln302Configurator = async (graph, createSdk) => + flattenTransactions( + await Promise.all( + graph.contracts.map(async ({ point, config }): Promise => { + const sdk = await createSdk(point) + + return Promise.all( + config.defaultUlnConfigs.map(([eid, config]) => sdk.setDefaultUlnConfig(eid, config)) + ) + }) + ) + ) diff --git a/packages/protocol-utils/src/uln302/index.ts b/packages/protocol-utils/src/uln302/index.ts new file mode 100644 index 000000000..39bdac610 --- /dev/null +++ b/packages/protocol-utils/src/uln302/index.ts @@ -0,0 +1,2 @@ +export * from './config' +export * from './types' diff --git a/packages/protocol-utils/src/uln302/types.ts b/packages/protocol-utils/src/uln302/types.ts new file mode 100644 index 000000000..9cbe79577 --- /dev/null +++ b/packages/protocol-utils/src/uln302/types.ts @@ -0,0 +1,29 @@ +import type { Address, OmniGraph, OmniPointBasedFactory, OmniTransaction } from '@layerzerolabs/utils' +import type { EndpointId } from '@layerzerolabs/lz-definitions' + +export interface IUln302 { + getUlnConfig(eid: EndpointId, address: Address): Promise + setDefaultExecutorConfig(eid: EndpointId, config: Uln302ExecutorConfig): Promise + setDefaultUlnConfig(eid: EndpointId, config: Uln302UlnConfig): Promise +} + +export interface Uln302ExecutorConfig { + maxMessageSize: number + executor: string +} + +export interface Uln302UlnConfig { + confirmations: bigint | string | number + optionalDVNThreshold: number + requiredDVNs: string[] + optionalDVNs: string[] +} + +export interface Uln302NodeConfig { + defaultExecutorConfigs: [eid: EndpointId, config: Uln302ExecutorConfig][] + defaultUlnConfigs: [eid: EndpointId, config: Uln302UlnConfig][] +} + +export type Uln302OmniGraph = OmniGraph + +export type Uln302Factory = OmniPointBasedFactory diff --git a/packages/protocol-utils/tsconfig.json b/packages/protocol-utils/tsconfig.json new file mode 100644 index 000000000..f083b2ecb --- /dev/null +++ b/packages/protocol-utils/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "exclude": ["dist", "node_modules"], + "include": ["src", "test"], + "compilerOptions": { + "types": ["node", "jest"], + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/packages/protocol-utils/tsup.config.ts b/packages/protocol-utils/tsup.config.ts new file mode 100644 index 000000000..b0e373950 --- /dev/null +++ b/packages/protocol-utils/tsup.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'tsup' + +export default defineConfig({ + entry: ['src/index.ts'], + outDir: './dist', + clean: true, + dts: true, + sourcemap: true, + splitting: false, + treeshake: true, + format: ['esm', 'cjs'], +}) diff --git a/packages/ua-utils-evm-hardhat-test/package.json b/packages/ua-utils-evm-hardhat-test/package.json index 058bcaa10..c179a3a18 100644 --- a/packages/ua-utils-evm-hardhat-test/package.json +++ b/packages/ua-utils-evm-hardhat-test/package.json @@ -26,6 +26,8 @@ "@layerzerolabs/lz-definitions": "~1.5.68", "@layerzerolabs/lz-evm-sdk-v1": "~1.5.68", "@layerzerolabs/lz-evm-sdk-v2": "~1.5.68", + "@layerzerolabs/protocol-utils": "~0.0.1", + "@layerzerolabs/protocol-utils-evm": "~0.0.1", "@layerzerolabs/ua-utils": "~0.1.0", "@layerzerolabs/ua-utils-evm": "~0.0.1", "@layerzerolabs/ua-utils-evm-hardhat": "~0.0.1", diff --git a/packages/ua-utils-evm-hardhat-test/test/__utils__/endpoint.ts b/packages/ua-utils-evm-hardhat-test/test/__utils__/endpoint.ts new file mode 100644 index 000000000..748115058 --- /dev/null +++ b/packages/ua-utils-evm-hardhat-test/test/__utils__/endpoint.ts @@ -0,0 +1,172 @@ +import { + createConnectedContractFactory, + createLogger, + createNetworkEnvironmentFactory, + createSignerFactory, + OmniGraphBuilderHardhat, + type OmniGraphHardhat, +} from '@layerzerolabs/utils-evm-hardhat' +import deploy from '../../deploy/001_bootstrap' +import { EndpointId } from '@layerzerolabs/lz-definitions' +import { omniContractToPoint } from '@layerzerolabs/utils-evm' +import { + configureEndpoint, + EndpointEdgeConfig, + EndpointFactory, + Uln302NodeConfig, + Uln302ExecutorConfig, + configureUln302, + Uln302Factory, + Uln302UlnConfig, +} from '@layerzerolabs/protocol-utils' +import { Endpoint, Uln302 } from '@layerzerolabs/protocol-utils-evm' +import { formatOmniPoint } from '@layerzerolabs/utils' + +export const ethEndpoint = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'EndpointV2' } +export const ethReceiveUln = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'ReceiveUln302' } +export const ethSendUln = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'SendUln302' } +export const avaxEndpoint = { eid: EndpointId.AVALANCHE_MAINNET, contractName: 'EndpointV2' } +export const avaxReceiveUln = { eid: EndpointId.AVALANCHE_MAINNET, contractName: 'ReceiveUln302' } +export const avaxSendUln = { eid: EndpointId.AVALANCHE_MAINNET, contractName: 'SendUln302' } + +export const defaultExecutorConfig: Uln302ExecutorConfig = { + maxMessageSize: 1024, + executor: '0x0000000000000000000000000000000000000001', +} + +export const defaultUlnConfig: Uln302UlnConfig = { + confirmations: BigInt(1), + requiredDVNs: ['0x0000000000000000000000000000000000000002', '0x0000000000000000000000000000000000000003'], + optionalDVNs: [], + optionalDVNThreshold: 0, +} + +/** + * Helper function that deploys a fresh endpoint infrastructure: + * + * - EndpointV2 + * - ReceiveUln302 + * - SendUln302 + * + * After deploying, it will wire up the elements with minimal configuration + */ +export const setupDefaultEndpoint = async (): Promise => { + // This is the tooling we are going to need + const logger = createLogger() + const environmentFactory = createNetworkEnvironmentFactory() + const contractFactory = createConnectedContractFactory() + const signerFactory = createSignerFactory() + const endpointSdkFactory: EndpointFactory = async (point) => new Endpoint(await contractFactory(point)) + const ulnSdkFactory: Uln302Factory = async (point) => new Uln302(await contractFactory(point)) + + // First we deploy the endpoint + await deploy(await environmentFactory(EndpointId.ETHEREUM_MAINNET)) + await deploy(await environmentFactory(EndpointId.AVALANCHE_MAINNET)) + + // For the graphs, we'll also need the pointers to the contracts + const ethSendUlnPoint = omniContractToPoint(await contractFactory(ethSendUln)) + const avaxSendUlnPoint = omniContractToPoint(await contractFactory(avaxSendUln)) + const ethReceiveUlnPoint = omniContractToPoint(await contractFactory(ethReceiveUln)) + const avaxReceiveUlnPoint = omniContractToPoint(await contractFactory(avaxReceiveUln)) + + // This is the graph for SendUln302 + const sendUlnConfig: OmniGraphHardhat = { + contracts: [ + { + contract: ethSendUln, + config: { + defaultUlnConfigs: [[EndpointId.AVALANCHE_MAINNET, defaultUlnConfig]], + defaultExecutorConfigs: [[EndpointId.AVALANCHE_MAINNET, defaultExecutorConfig]], + }, + }, + { + contract: avaxSendUln, + config: { + defaultUlnConfigs: [[EndpointId.ETHEREUM_MAINNET, defaultUlnConfig]], + defaultExecutorConfigs: [[EndpointId.ETHEREUM_MAINNET, defaultExecutorConfig]], + }, + }, + ], + connections: [], + } + + // This is the graph for ReceiveUln302 + const receiveUlnConfig: OmniGraphHardhat = { + contracts: [ + { + contract: ethReceiveUln, + config: { + defaultUlnConfigs: [[EndpointId.AVALANCHE_MAINNET, defaultUlnConfig]], + defaultExecutorConfigs: [], + }, + }, + { + contract: avaxReceiveUln, + config: { + defaultUlnConfigs: [[EndpointId.ETHEREUM_MAINNET, defaultUlnConfig]], + defaultExecutorConfigs: [], + }, + }, + ], + connections: [], + } + + // This is the graph for EndpointV2 + const config: OmniGraphHardhat = { + contracts: [ + { + contract: ethEndpoint, + config: undefined, + }, + { + contract: avaxEndpoint, + config: undefined, + }, + ], + connections: [ + { + from: ethEndpoint, + to: avaxEndpoint, + config: { + defaultReceiveLibrary: ethReceiveUlnPoint.address, + defaultSendLibrary: ethSendUlnPoint.address, + }, + }, + { + from: avaxEndpoint, + to: ethEndpoint, + config: { + defaultReceiveLibrary: avaxReceiveUlnPoint.address, + defaultSendLibrary: avaxSendUlnPoint.address, + }, + }, + ], + } + + // Now we compile a list of all the transactions that need to be executed for the ULNs and Endpoints + const builderEndpoint = await OmniGraphBuilderHardhat.fromConfig(config, contractFactory) + const endpointTransactions = await configureEndpoint(builderEndpoint.graph, endpointSdkFactory) + const builderSendUln = await OmniGraphBuilderHardhat.fromConfig(sendUlnConfig, contractFactory) + const sendUlnTransactions = await configureUln302(builderSendUln.graph, ulnSdkFactory) + const builderReceiveUln = await OmniGraphBuilderHardhat.fromConfig(receiveUlnConfig, contractFactory) + const receiveUlnTransactions = await configureUln302(builderReceiveUln.graph, ulnSdkFactory) + + const transactions = [...sendUlnTransactions, ...receiveUlnTransactions, ...endpointTransactions] + + logger.info(`Executing ${transactions.length} transactions`) + + for (const transaction of transactions) { + const signer = await signerFactory(transaction.point.eid) + const description = transaction.description ?? '[no description]' + + logger.info(`${formatOmniPoint(transaction.point)}: ${description}`) + + const response = await signer.signAndSend(transaction) + logger.info(`${formatOmniPoint(transaction.point)}: ${description}: ${response.transactionHash}`) + + const receipt = await response.wait() + logger.info(`${formatOmniPoint(transaction.point)}: ${description}: ${receipt.transactionHash}`) + } + + logger.info(`Done configuring endpoint`) +} diff --git a/packages/ua-utils-evm-hardhat-test/test/endpoint/config.test.ts b/packages/ua-utils-evm-hardhat-test/test/endpoint/config.test.ts new file mode 100644 index 000000000..b9e0246c3 --- /dev/null +++ b/packages/ua-utils-evm-hardhat-test/test/endpoint/config.test.ts @@ -0,0 +1,76 @@ +import { createConnectedContractFactory } from '@layerzerolabs/utils-evm-hardhat' +import type { OmniPoint } from '@layerzerolabs/utils' +import { omniContractToPoint } from '@layerzerolabs/utils-evm' +import { expect } from 'chai' +import { describe } from 'mocha' +import { EndpointId } from '@layerzerolabs/lz-definitions' +import { defaultUlnConfig, setupDefaultEndpoint } from '../__utils__/endpoint' +import { Endpoint, Uln302 } from '@layerzerolabs/protocol-utils-evm' + +describe('endpoint/config', () => { + const ethEndpoint = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'EndpointV2' } + const ethReceiveUln = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'ReceiveUln302' } + const ethSendUln = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'SendUln302' } + const avaxEndpoint = { eid: EndpointId.AVALANCHE_MAINNET, contractName: 'EndpointV2' } + const avaxReceiveUln = { eid: EndpointId.AVALANCHE_MAINNET, contractName: 'ReceiveUln302' } + const avaxSendUln = { eid: EndpointId.AVALANCHE_MAINNET, contractName: 'SendUln302' } + + beforeEach(async () => { + await setupDefaultEndpoint() + }) + + describe('endpoint', () => { + it('should have default libraries configured', async () => { + // This is the required tooling we need to set up + const connectedContractFactory = createConnectedContractFactory() + const sdkFactory = async (point: OmniPoint) => new Endpoint(await connectedContractFactory(point)) + + // Now for the purposes of the test, we need to get coordinates of our contracts + const ethEndpointPoint = omniContractToPoint(await connectedContractFactory(ethEndpoint)) + const avaxEndpointPoint = omniContractToPoint(await connectedContractFactory(avaxEndpoint)) + + const ethEndpointSdk = await sdkFactory(ethEndpointPoint) + const avaxEndpointSdk = await sdkFactory(avaxEndpointPoint) + + // First let's check the send libraries + const ethDefaultSendLib = await ethEndpointSdk.defaultSendLibrary(avaxEndpointPoint.eid) + const avaxDefaultSendLib = await avaxEndpointSdk.defaultSendLibrary(ethEndpointPoint.eid) + + const ethSendUlnPoint = omniContractToPoint(await connectedContractFactory(ethSendUln)) + const avaxSendUlnPoint = omniContractToPoint(await connectedContractFactory(avaxSendUln)) + + expect(ethDefaultSendLib).to.equal(ethSendUlnPoint.address) + expect(avaxDefaultSendLib).to.equal(avaxSendUlnPoint.address) + + // Then let's check the receive libraries + const ethDefaultReceiveLib = await ethEndpointSdk.defaultReceiveLibrary(avaxEndpointPoint.eid) + const avaxDefaultReceiveLib = await avaxEndpointSdk.defaultReceiveLibrary(ethEndpointPoint.eid) + + const ethReceiveUlnPoint = omniContractToPoint(await connectedContractFactory(ethReceiveUln)) + const avaxReceiveUlnPoint = omniContractToPoint(await connectedContractFactory(avaxReceiveUln)) + + expect(ethDefaultReceiveLib).to.equal(ethReceiveUlnPoint.address) + expect(avaxDefaultReceiveLib).to.equal(avaxReceiveUlnPoint.address) + }) + }) + + describe('sendUln302', () => { + it('should have default executors configured', async () => { + // This is the required tooling we need to set up + const connectedContractFactory = createConnectedContractFactory() + const sdkFactory = async (point: OmniPoint) => new Uln302(await connectedContractFactory(point)) + + const ethSendUlnPoint = omniContractToPoint(await connectedContractFactory(ethSendUln)) + const avaxSendUlnPoint = omniContractToPoint(await connectedContractFactory(avaxSendUln)) + + const ethSendUlnSdk = await sdkFactory(ethSendUlnPoint) + const avaxSendUlnSdk = await sdkFactory(avaxSendUlnPoint) + + const ethConfig = await ethSendUlnSdk.getUlnConfig(avaxSendUlnPoint.eid, avaxSendUlnPoint.address) + const avaxConfig = await avaxSendUlnSdk.getUlnConfig(ethSendUlnPoint.eid, ethSendUlnPoint.address) + + expect(ethConfig).to.eql(defaultUlnConfig) + expect(avaxConfig).to.eql(defaultUlnConfig) + }) + }) +}) diff --git a/packages/ua-utils-evm-hardhat-test/test/oapp/config.test.ts b/packages/ua-utils-evm-hardhat-test/test/oapp/config.test.ts index 635230ae3..e69e39f1b 100644 --- a/packages/ua-utils-evm-hardhat-test/test/oapp/config.test.ts +++ b/packages/ua-utils-evm-hardhat-test/test/oapp/config.test.ts @@ -12,6 +12,7 @@ import { omniContractToPoint } from '@layerzerolabs/utils-evm' import { expect } from 'chai' import { describe } from 'mocha' import { EndpointId } from '@layerzerolabs/lz-definitions' +import { setupDefaultEndpoint } from '../__utils__/endpoint' describe('oapp/config', () => { const ethContract = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'DefaultOApp' } @@ -43,6 +44,10 @@ describe('oapp/config', () => { ], } + beforeEach(async () => { + await setupDefaultEndpoint() + }) + it('should return all setPeer transactions', async () => { // This is the required tooling we need to set up const contractFactory = createContractFactory() From 74dfb193a7f11a8a1cdc37a756363ef1747f8e73 Mon Sep 17 00:00:00 2001 From: Jan Nanista Date: Tue, 5 Dec 2023 12:33:34 -0800 Subject: [PATCH 2/5] chore: Use debug instead of info when logging in setupDefaultEndpoint --- .../test/__utils__/endpoint.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/ua-utils-evm-hardhat-test/test/__utils__/endpoint.ts b/packages/ua-utils-evm-hardhat-test/test/__utils__/endpoint.ts index 748115058..aa9d6235d 100644 --- a/packages/ua-utils-evm-hardhat-test/test/__utils__/endpoint.ts +++ b/packages/ua-utils-evm-hardhat-test/test/__utils__/endpoint.ts @@ -153,20 +153,20 @@ export const setupDefaultEndpoint = async (): Promise => { const transactions = [...sendUlnTransactions, ...receiveUlnTransactions, ...endpointTransactions] - logger.info(`Executing ${transactions.length} transactions`) + logger.debug(`Executing ${transactions.length} transactions`) for (const transaction of transactions) { const signer = await signerFactory(transaction.point.eid) const description = transaction.description ?? '[no description]' - logger.info(`${formatOmniPoint(transaction.point)}: ${description}`) + logger.debug(`${formatOmniPoint(transaction.point)}: ${description}`) const response = await signer.signAndSend(transaction) - logger.info(`${formatOmniPoint(transaction.point)}: ${description}: ${response.transactionHash}`) + logger.debug(`${formatOmniPoint(transaction.point)}: ${description}: ${response.transactionHash}`) const receipt = await response.wait() - logger.info(`${formatOmniPoint(transaction.point)}: ${description}: ${receipt.transactionHash}`) + logger.debug(`${formatOmniPoint(transaction.point)}: ${description}: ${receipt.transactionHash}`) } - logger.info(`Done configuring endpoint`) + logger.debug(`Done configuring endpoint`) } From 6e1a957bbc69c8abb90ddeeaf7d341f969346b8a Mon Sep 17 00:00:00 2001 From: Jan Nanista Date: Tue, 5 Dec 2023 12:33:48 -0800 Subject: [PATCH 3/5] chore: Remove default endpoint deployment --- packages/ua-utils-evm-hardhat-test/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ua-utils-evm-hardhat-test/package.json b/packages/ua-utils-evm-hardhat-test/package.json index c179a3a18..2d76e2e4c 100644 --- a/packages/ua-utils-evm-hardhat-test/package.json +++ b/packages/ua-utils-evm-hardhat-test/package.json @@ -11,7 +11,6 @@ "license": "MIT", "scripts": { "lint": "npx eslint '**/*.{js,ts,json}'", - "pretest": "npx hardhat --network vengaboys deploy --reset && npx hardhat --network britney deploy --reset", "test": "npx hardhat test" }, "devDependencies": { From f70dec6c3b70539710d288d297b34f27eab0aa41 Mon Sep 17 00:00:00 2001 From: Jan Nanista Date: Tue, 5 Dec 2023 12:40:58 -0800 Subject: [PATCH 4/5] chore: Remove obsolete test --- .../test/config.test.ts | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 packages/ua-utils-evm-hardhat-test/test/config.test.ts diff --git a/packages/ua-utils-evm-hardhat-test/test/config.test.ts b/packages/ua-utils-evm-hardhat-test/test/config.test.ts deleted file mode 100644 index 9d9ca6527..000000000 --- a/packages/ua-utils-evm-hardhat-test/test/config.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { expect } from 'chai' -import { describe } from 'mocha' -import { getNetworkRuntimeEnvironment } from '@layerzerolabs/utils-evm-hardhat' -import { HardhatRuntimeEnvironment } from 'hardhat/types' - -const NETWORK_NAMES = ['vengaboys', 'britney'] - -describe('config', () => { - NETWORK_NAMES.forEach((networkName) => { - describe(`Network '${networkName}`, () => { - let environment: HardhatRuntimeEnvironment - - before(async () => { - environment = await getNetworkRuntimeEnvironment(networkName) - }) - - it('should have an endpoint deployed', async () => { - const endpoint = await environment.ethers.getContract('EndpointV2') - const eid = await endpoint.eid() - - expect(environment.network.config.eid).to.be.a('number') - expect(eid).to.eql(environment.network.config.eid) - }) - }) - }) -}) From 91360058dbaea4d67a8c8e989a6eeb95287ae427 Mon Sep 17 00:00:00 2001 From: Jan Nanista Date: Tue, 5 Dec 2023 12:50:06 -0800 Subject: [PATCH 5/5] chore: Add deployOApp test helper --- .../ua-utils-evm-hardhat-test/test/__utils__/oapp.ts | 10 ++++++++++ .../ua-utils-evm-hardhat-test/test/oapp/config.test.ts | 2 ++ 2 files changed, 12 insertions(+) create mode 100644 packages/ua-utils-evm-hardhat-test/test/__utils__/oapp.ts diff --git a/packages/ua-utils-evm-hardhat-test/test/__utils__/oapp.ts b/packages/ua-utils-evm-hardhat-test/test/__utils__/oapp.ts new file mode 100644 index 000000000..886b012ea --- /dev/null +++ b/packages/ua-utils-evm-hardhat-test/test/__utils__/oapp.ts @@ -0,0 +1,10 @@ +import { EndpointId } from '@layerzerolabs/lz-definitions' +import { createNetworkEnvironmentFactory } from '@layerzerolabs/utils-evm-hardhat' +import deploy from '../../deploy/002_oapp' + +export const deployOApp = async () => { + const environmentFactory = createNetworkEnvironmentFactory() + + await deploy(await environmentFactory(EndpointId.ETHEREUM_MAINNET)) + await deploy(await environmentFactory(EndpointId.AVALANCHE_MAINNET)) +} diff --git a/packages/ua-utils-evm-hardhat-test/test/oapp/config.test.ts b/packages/ua-utils-evm-hardhat-test/test/oapp/config.test.ts index e69e39f1b..975b13bf5 100644 --- a/packages/ua-utils-evm-hardhat-test/test/oapp/config.test.ts +++ b/packages/ua-utils-evm-hardhat-test/test/oapp/config.test.ts @@ -13,6 +13,7 @@ import { expect } from 'chai' import { describe } from 'mocha' import { EndpointId } from '@layerzerolabs/lz-definitions' import { setupDefaultEndpoint } from '../__utils__/endpoint' +import { deployOApp } from '../__utils__/oapp' describe('oapp/config', () => { const ethContract = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'DefaultOApp' } @@ -46,6 +47,7 @@ describe('oapp/config', () => { beforeEach(async () => { await setupDefaultEndpoint() + await deployOApp() }) it('should return all setPeer transactions', async () => {