diff --git a/.changeset/long-dolphins-press.md b/.changeset/long-dolphins-press.md new file mode 100644 index 000000000..f464398f0 --- /dev/null +++ b/.changeset/long-dolphins-press.md @@ -0,0 +1,7 @@ +--- +"@layerzerolabs/ua-devtools-evm-hardhat-test": patch +"@layerzerolabs/ua-devtools-evm": patch +"@layerzerolabs/ua-devtools": patch +--- + +Fixing configureEnforcedOptions diff --git a/packages/ua-devtools-evm/src/oapp/sdk.ts b/packages/ua-devtools-evm/src/oapp/sdk.ts index f064d0181..7575fe46f 100644 --- a/packages/ua-devtools-evm/src/oapp/sdk.ts +++ b/packages/ua-devtools-evm/src/oapp/sdk.ts @@ -1,4 +1,4 @@ -import type { IOApp, EnforcedOptions, OAppEnforcedOptionConfig } from '@layerzerolabs/ua-devtools' +import type { IOApp, OAppEnforcedOptionParam } from '@layerzerolabs/ua-devtools' import { type Bytes32, type OmniAddress, @@ -8,13 +8,13 @@ import { areBytes32Equal, ignoreZero, makeBytes32, + Bytes, } from '@layerzerolabs/devtools' import { type OmniContract, formatOmniContract } from '@layerzerolabs/devtools-evm' import type { EndpointId } from '@layerzerolabs/lz-definitions' import type { EndpointFactory, IEndpoint } from '@layerzerolabs/protocol-devtools' import { OmniSDK } from '@layerzerolabs/devtools-evm' import { printJson } from '@layerzerolabs/io-devtools' -import { ExecutorOptionType, Options } from '@layerzerolabs/lz-v2-utilities' export class OApp extends OmniSDK implements IOApp { constructor( @@ -71,42 +71,41 @@ export class OApp extends OmniSDK implements IOApp { } } - async getEnforcedOptions(eid: EndpointId, msgType: number): Promise { + async getEnforcedOptions(eid: EndpointId, msgType: number): Promise { this.logger.debug(`Getting enforced options for eid ${eid} (${formatEid(eid)}) and message type ${msgType}`) return await this.contract.contract.enforcedOptions(eid, msgType) } - async setEnforcedOptions(enforcedOptions: EnforcedOptions[]): Promise { + async setEnforcedOptions(enforcedOptions: OAppEnforcedOptionParam[]): Promise { this.logger.debug(`Setting enforced options to ${printJson(enforcedOptions)}`) + const serializedConfig = this.serializeExecutorOptions(enforcedOptions) - const data = this.contract.contract.interface.encodeFunctionData('setEnforcedOptions', [enforcedOptions]) + const data = this.contract.contract.interface.encodeFunctionData('setEnforcedOptions', [serializedConfig]) return { ...this.createTransaction(data), description: `Setting enforced options to ${printJson(enforcedOptions)}`, } } - encodeEnforcedOptions(enforcedOptionConfig: OAppEnforcedOptionConfig): Options { - if ('options' in enforcedOptionConfig) return Options.fromOptions(enforcedOptionConfig.options) - - if (enforcedOptionConfig.msgType == ExecutorOptionType.LZ_RECEIVE) { - return Options.newOptions().addExecutorLzReceiveOption(enforcedOptionConfig.gas, enforcedOptionConfig.value) - } else if (enforcedOptionConfig.msgType == ExecutorOptionType.NATIVE_DROP) { - return Options.newOptions().addExecutorNativeDropOption( - enforcedOptionConfig.amount, - enforcedOptionConfig.receiver - ) - } else if (enforcedOptionConfig.msgType == ExecutorOptionType.COMPOSE) { - return Options.newOptions().addExecutorComposeOption( - enforcedOptionConfig.index, - enforcedOptionConfig.gas, - enforcedOptionConfig.value - ) - } else if (enforcedOptionConfig.msgType == ExecutorOptionType.ORDERED) { - return Options.newOptions().addExecutorOrderedExecutionOption() - } else { - throw new Error(`Invalid ExecutorOptionType`) - } + /** + * Prepares the Executor config to be sent to the contract + * + * @param {OAppEnforcedOptionParam[]} + * @returns {SerializedEnforcedOptions[]} + */ + protected serializeExecutorOptions( + oappEnforcedOptionParam: OAppEnforcedOptionParam[] + ): SerializedEnforcedOptions[] { + return oappEnforcedOptionParam.map(({ eid, option: { msgType, options } }) => ({ eid, msgType, options })) } } + +/** + * Helper type that matches the solidity implementation + */ +interface SerializedEnforcedOptions { + eid: number + msgType: number + options: string +} diff --git a/packages/ua-devtools/src/oapp/config.ts b/packages/ua-devtools/src/oapp/config.ts index d2829ffaa..95524b13b 100644 --- a/packages/ua-devtools/src/oapp/config.ts +++ b/packages/ua-devtools/src/oapp/config.ts @@ -1,9 +1,18 @@ -import { OmniAddress, flattenTransactions, OmniPointMap, type OmniTransaction } from '@layerzerolabs/devtools' -import { EnforcedOptions, OAppEnforcedOptionConfig, OAppFactory, OAppOmniGraph } from './types' +import { + Bytes, + flattenTransactions, + formatOmniVector, + isDeepEqual, + OmniAddress, + OmniPointMap, + type OmniTransaction, +} from '@layerzerolabs/devtools' +import { OAppEnforcedOption, OAppEnforcedOptionParam, OAppFactory, OAppOmniGraph } from './types' import { createModuleLogger, printBoolean } from '@layerzerolabs/io-devtools' -import { formatOmniVector, isDeepEqual } from '@layerzerolabs/devtools' import { SetConfigParam } from '@layerzerolabs/protocol-devtools' import assert from 'assert' +import { ExecutorOptionType, Options } from '@layerzerolabs/lz-v2-utilities' + export type OAppConfigurator = (graph: OAppOmniGraph, createSdk: OAppFactory) => Promise export const configureOApp: OAppConfigurator = async (graph: OAppOmniGraph, createSdk: OAppFactory) => @@ -221,30 +230,44 @@ export const configureReceiveConfig: OAppConfigurator = async (graph, createSdk) return buildOmniTransactions(setConfigsByEndpointAndLibrary, createSdk) } -export const configureEnforcedOptions: OAppConfigurator = async (graph, createSdk) => - flattenTransactions( - await Promise.all( - graph.connections.map(async ({ vector: { from, to }, config }): Promise => { - if (config?.enforcedOptions == null) return [] - const enforcedOptions: EnforcedOptions[] = [] - const enforcedOptionsConfig: OAppEnforcedOptionConfig[] = config.enforcedOptions - const oappSdk = await createSdk(from) - for (const enforcedOption of enforcedOptionsConfig) { - const currentEnforcedOption = await oappSdk.getEnforcedOptions(to.eid, enforcedOption.msgType) - const encodedEnforcedOption = oappSdk.encodeEnforcedOptions(enforcedOption).toHex().toLowerCase() - if (currentEnforcedOption !== encodedEnforcedOption) { - enforcedOptions.push({ - eid: to.eid, - msgType: enforcedOption.msgType, - options: encodedEnforcedOption, - }) - } - } - if (enforcedOptions.length === 0) return [] - return [await oappSdk.setEnforcedOptions(enforcedOptions)] - }) +export const configureEnforcedOptions: OAppConfigurator = async (graph, createSdk) => { + // This function builds a map to find all OAppEnforcedOptionParam[] to execute for a given OApp + const setEnforcedOptionsByEndpoint: OmniPointMap = new OmniPointMap() + + for (const { + vector: { from, to }, + config, + } of graph.connections) { + if (config?.enforcedOptions == null) continue + const oappSdk = await createSdk(from) + + // combines enforced options together by msgType + const enforcedOptionsByType = config.enforcedOptions.reduce( + enforcedOptionsReducer, + new Map() ) - ) + + // We ask the oapp SDK whether this config has already been applied + for (const [msgType, options] of enforcedOptionsByType) { + const currentEnforcedOption: Bytes = await oappSdk.getEnforcedOptions(to.eid, msgType) + if (currentEnforcedOption !== options.toHex()) { + // Updates map with new configs for that OApp and OAppEnforcedOptionParam[] + const setConfigsByLibrary = setEnforcedOptionsByEndpoint.getOrElse(from, () => []) + setConfigsByLibrary.push({ + eid: to.eid, + option: { + msgType, + options: options.toHex(), + }, + }) + setEnforcedOptionsByEndpoint.set(from, setConfigsByLibrary) + } + } + } + + // This function iterates over the map (OApp -> OAppEnforcedOptionParam[]) to execute setEnforcedOptions + return buildEnforcedOptionsOmniTransactions(setEnforcedOptionsByEndpoint, createSdk) +} const buildOmniTransactions = async ( setConfigsByEndpointAndLibrary: OmniPointMap>, @@ -260,3 +283,46 @@ const buildOmniTransactions = async ( } return omniTransaction } + +const buildEnforcedOptionsOmniTransactions = async ( + setEnforcedOptionsByEndpoint: OmniPointMap, + createSdk: OAppFactory +): Promise => { + const omniTransaction: OmniTransaction[] = [] + for (const [from, enforcedOptionsConfig] of setEnforcedOptionsByEndpoint) { + const oappSdk = await createSdk(from) + omniTransaction.push(await oappSdk.setEnforcedOptions(enforcedOptionsConfig)) + } + return omniTransaction +} + +const enforcedOptionsReducer = ( + optionsByType: Map, + optionConfig: OAppEnforcedOption +): Map => { + const { msgType } = optionConfig + const currentOptions = optionsByType.get(msgType) ?? Options.newOptions() + + switch (msgType) { + case ExecutorOptionType.LZ_RECEIVE: + return optionsByType.set( + msgType, + currentOptions.addExecutorLzReceiveOption(optionConfig.gas, optionConfig.value) + ) + + case ExecutorOptionType.NATIVE_DROP: + return optionsByType.set( + msgType, + currentOptions.addExecutorNativeDropOption(optionConfig.amount, optionConfig.receiver) + ) + + case ExecutorOptionType.COMPOSE: + return optionsByType.set( + msgType, + currentOptions.addExecutorComposeOption(optionConfig.index, optionConfig.gas, optionConfig.value) + ) + + case ExecutorOptionType.ORDERED: + return optionsByType.set(msgType, currentOptions.addExecutorOrderedExecutionOption()) + } +} diff --git a/packages/ua-devtools/src/oapp/schema.ts b/packages/ua-devtools/src/oapp/schema.ts index 228073c6f..a334990cc 100644 --- a/packages/ua-devtools/src/oapp/schema.ts +++ b/packages/ua-devtools/src/oapp/schema.ts @@ -2,13 +2,12 @@ import { z } from 'zod' import { AddressSchema, UIntBigIntSchema, UIntNumberSchema } from '@layerzerolabs/devtools' import { Uln302ExecutorConfigSchema, Uln302UlnConfigSchema, TimeoutSchema } from '@layerzerolabs/protocol-devtools' import { - EndcodedOption, ExecutorComposeOption, ExecutorLzReceiveOption, ExecutorNativeDropOption, ExecutorOrderedExecutionOption, OAppEdgeConfig, - OAppEnforcedOptionConfig, + OAppEnforcedOption, OAppReceiveConfig, OAppReceiveLibraryConfig, OAppSendConfig, @@ -29,13 +28,6 @@ export const OAppReceiveConfigSchema = z.object({ ulnConfig: Uln302UlnConfigSchema, }) satisfies z.ZodSchema -const ExecutorOptionTypeSchema = z.nativeEnum(ExecutorOptionType) - -export const EncodedOptionSchema = z.object({ - msgType: ExecutorOptionTypeSchema, - options: z.string(), -}) satisfies z.ZodSchema - export const ExecutorLzReceiveOptionSchema = z.object({ msgType: z.literal(ExecutorOptionType.LZ_RECEIVE), gas: UIntNumberSchema, @@ -60,15 +52,14 @@ export const ExecutorOrderedExecutionOptionSchema = z.object({ }) satisfies z.ZodSchema export const OAppEnforcedOptionConfigSchema = z.union([ - EncodedOptionSchema, ExecutorLzReceiveOptionSchema, ExecutorNativeDropOptionSchema, ExecutorComposeOptionSchema, ExecutorOrderedExecutionOptionSchema, -]) satisfies z.ZodSchema +]) satisfies z.ZodSchema export const OAppEnforcedOptionsSchema = z.array(OAppEnforcedOptionConfigSchema) satisfies z.ZodSchema< - OAppEnforcedOptionConfig[], + OAppEnforcedOption[], z.ZodTypeDef, unknown > diff --git a/packages/ua-devtools/src/oapp/types.ts b/packages/ua-devtools/src/oapp/types.ts index 2f67d6ef6..55042705a 100644 --- a/packages/ua-devtools/src/oapp/types.ts +++ b/packages/ua-devtools/src/oapp/types.ts @@ -1,6 +1,7 @@ import type { EndpointId } from '@layerzerolabs/lz-definitions' import type { IEndpoint, Timeout, Uln302ExecutorConfig, Uln302UlnConfig } from '@layerzerolabs/protocol-devtools' import type { + Bytes, Factory, IOmniSDK, OmniAddress, @@ -8,23 +9,17 @@ import type { OmniPoint, OmniTransaction, OmniVector, + PossiblyBigInt, } from '@layerzerolabs/devtools' -import { ExecutorOptionType, Options } from '@layerzerolabs/lz-v2-utilities' +import { ExecutorOptionType } from '@layerzerolabs/lz-v2-utilities' export interface IOApp extends IOmniSDK { getEndpointSDK(): Promise getPeer(eid: EndpointId): Promise hasPeer(eid: EndpointId, address: OmniAddress | null | undefined): Promise setPeer(eid: EndpointId, peer: OmniAddress | null | undefined): Promise - getEnforcedOptions(eid: EndpointId, msgType: number): Promise - setEnforcedOptions(enforcedOptions: EnforcedOptions[]): Promise - encodeEnforcedOptions(enforcedOptionConfig: OAppEnforcedOptionConfig): Options -} - -export type EnforcedOptions = { - eid: EndpointId - msgType: number - options: string + getEnforcedOptions(eid: EndpointId, msgType: number): Promise + setEnforcedOptions(enforcedOptions: OAppEnforcedOptionParam[]): Promise } export interface OAppReceiveLibraryConfig { @@ -47,52 +42,61 @@ export interface OAppEdgeConfig { receiveLibraryTimeoutConfig?: Timeout sendConfig?: OAppSendConfig receiveConfig?: OAppReceiveConfig - enforcedOptions?: OAppEnforcedOptionConfig[] + enforcedOptions?: OAppEnforcedOption[] } export interface BaseExecutorOption { msgType: ExecutorOptionType } -export interface EndcodedOption extends BaseExecutorOption { +export interface EncodedOption extends BaseExecutorOption { options: string } export interface ExecutorLzReceiveOption extends BaseExecutorOption { msgType: ExecutorOptionType.LZ_RECEIVE - gas: string | number - value: string | number + gas: PossiblyBigInt + value: PossiblyBigInt } export interface ExecutorNativeDropOption extends BaseExecutorOption { msgType: ExecutorOptionType.NATIVE_DROP - amount: string | number + amount: PossiblyBigInt receiver: string } export interface ExecutorComposeOption extends BaseExecutorOption { msgType: ExecutorOptionType.COMPOSE index: number - gas: string | number - value: string | number + gas: PossiblyBigInt + value: PossiblyBigInt } export interface ExecutorOrderedExecutionOption extends BaseExecutorOption { msgType: ExecutorOptionType.ORDERED } -export type OAppEnforcedOptionConfig = +export type OAppEnforcedOption = | ExecutorLzReceiveOption | ExecutorNativeDropOption | ExecutorComposeOption | ExecutorOrderedExecutionOption - | EndcodedOption + +export interface OAppEnforcedOptionParam { + eid: EndpointId + option: EncodedOption +} export interface OAppPeers { vector: OmniVector hasPeer: boolean } +export interface OAppEnforcedOptions { + vector: OmniVector + enforcedOptions: EncodedOption[] +} + export type OAppOmniGraph = OmniGraph export type OAppFactory = Factory<[TOmniPoint], TOApp> diff --git a/tests/ua-devtools-evm-hardhat-test/test/oapp/config.test.ts b/tests/ua-devtools-evm-hardhat-test/test/oapp/config.test.ts index 4267513d8..fe5b15b93 100644 --- a/tests/ua-devtools-evm-hardhat-test/test/oapp/config.test.ts +++ b/tests/ua-devtools-evm-hardhat-test/test/oapp/config.test.ts @@ -1,36 +1,36 @@ import 'hardhat' import { EndpointId } from '@layerzerolabs/lz-definitions' -import { deployOApp } from '../__utils__/oapp' +import { deployOApp, getLibraryAddress } from '../__utils__/oapp' import { - OmniContractFactoryHardhat, createConnectedContractFactory, - createSignerFactory, createProviderFactory, + createSignerFactory, + OmniContractFactoryHardhat, } from '@layerzerolabs/devtools-evm-hardhat' -import { OApp, createOAppFactory } from '@layerzerolabs/ua-devtools-evm' +import { createOAppFactory, OApp } from '@layerzerolabs/ua-devtools-evm' import { configureOApp, IOApp, OAppFactory, OAppOmniGraph } from '@layerzerolabs/ua-devtools' import { OmniContract, omniContractToPoint } from '@layerzerolabs/devtools-evm' -import { getLibraryAddress } from '../__utils__/oapp' import { - setupDefaultEndpoint, - avaxExecutor, avaxDvn, avaxDvn_Opt2, avaxDvn_Opt3, - ethSendUln, - deployEndpoint, - bscExecutor, - bscDvn, - ethSendUln2_Opt2, - avaxSendUln2_Opt2, - ethReceiveUln2_Opt2, - avaxReceiveUln2_Opt2, - ethReceiveUln, + avaxExecutor, avaxReceiveUln, + avaxReceiveUln2_Opt2, + avaxSendUln2_Opt2, + bscDvn, + bscExecutor, + deployEndpoint, ethDvn, + ethReceiveUln, + ethReceiveUln2_Opt2, + ethSendUln, + ethSendUln2_Opt2, + setupDefaultEndpoint, } from '../__utils__/endpoint' import { createSignAndSend, OmniPoint, OmniTransaction } from '@layerzerolabs/devtools' import { IEndpoint } from '@layerzerolabs/protocol-devtools' +import { ExecutorOptionType, Options } from '@layerzerolabs/lz-v2-utilities' describe('oapp/config', () => { const ethPointHardhat = { eid: EndpointId.ETHEREUM_V2_MAINNET, contractName: 'DefaultOApp' } @@ -1219,6 +1219,24 @@ describe('oapp/config', () => { describe('configureEnforcedOptions', () => { let graph: OAppOmniGraph + let bscContract: OmniContract, bscPoint: OmniPoint, bscOAppSdk: OApp + + beforeEach(async () => { + bscContract = await contractFactory(bscPointHardhat) + bscPoint = omniContractToPoint(bscContract) + bscOAppSdk = await oappSdkFactory(bscPoint) + // Before we configure the OApp, we'll set some peers + const [_, errors] = await signAndSend([ + await ethOAppSdk.setPeer(bscPoint.eid, bscPoint.address), + await avaxOAppSdk.setPeer(bscPoint.eid, bscPoint.address), + await bscOAppSdk.setPeer(ethPoint.eid, ethPoint.address), + await bscOAppSdk.setPeer(avaxPoint.eid, avaxPoint.address), + ]) + + // eslint-disable-next-line jest/no-standalone-expect + expect(errors).toEqual([]) + }) + it('should return empty transactions when enforcedOptions is empty', async () => { // This is the OApp config that we want to use against our contracts graph = { @@ -1250,7 +1268,8 @@ describe('oapp/config', () => { transactions = await configureOApp(graph, oappSdkFactory) expect(transactions).toEqual([]) }) - it('should return all setEnforcedOption transactions', async () => { + + it("should return addExecutorLzReceiveOption tx's in both directions for msgType: 1", async () => { // This is the OApp config that we want to use against our contracts graph = { contracts: [ @@ -1268,7 +1287,8 @@ describe('oapp/config', () => { enforcedOptions: [ { msgType: 1, - options: '0x00030100110100000000000000000000000000030d40', + gas: '200000', + value: '0', }, ], }, @@ -1289,25 +1309,30 @@ describe('oapp/config', () => { } // Now we configure the OApp transactions = await configureOApp(graph, oappSdkFactory) + const options = Options.newOptions().addExecutorLzReceiveOption(200000, 0).toHex().toLowerCase() expect(transactions).toEqual([ await ethOAppSdk.setEnforcedOptions([ { eid: avaxPoint.eid, - msgType: 1, - options: '0x00030100110100000000000000000000000000030d40', + option: { + msgType: 1, + options: options, + }, }, ]), await avaxOAppSdk.setEnforcedOptions([ { eid: ethPoint.eid, - msgType: 1, - options: '0x00030100110100000000000000000000000000030d40', + option: { + msgType: 1, + options: options, + }, }, ]), ]) }) - it('should return one transactions when enforcedOptions', async () => { + it("should combine addExecutorLzReceiveOption's into one tx for both chains for msgType: 1", async () => { // This is the OApp config that we want to use against our contracts graph = { contracts: [ @@ -1317,6 +1342,9 @@ describe('oapp/config', () => { { point: avaxPoint, }, + { + point: bscPoint, + }, ], connections: [ { @@ -1325,13 +1353,14 @@ describe('oapp/config', () => { enforcedOptions: [ { msgType: 1, - options: '0x00030100110100000000000000000000000000030d40', + gas: '200000', + value: '0', }, ], }, }, { - vector: { from: avaxPoint, to: ethPoint }, + vector: { from: ethPoint, to: bscPoint }, config: { enforcedOptions: [ { @@ -1342,31 +1371,580 @@ describe('oapp/config', () => { ], }, }, + { + vector: { from: avaxPoint, to: ethPoint }, + config: {}, + }, + { + vector: { from: avaxPoint, to: bscPoint }, + config: {}, + }, + { + vector: { from: bscPoint, to: ethPoint }, + config: {}, + }, + { + vector: { from: bscPoint, to: avaxPoint }, + config: {}, + }, ], } + // Now we configure the OApp + transactions = await configureOApp(graph, oappSdkFactory) + const options = Options.newOptions().addExecutorLzReceiveOption(200000, 0).toHex().toLowerCase() + const expectedTransactions = [ + await ethOAppSdk.setEnforcedOptions([ + { + eid: avaxPoint.eid, + option: { + msgType: 1, + options: options, + }, + }, + { + eid: bscPoint.eid, + option: { + msgType: 1, + options: options, + }, + }, + ]), + ] + expect(transactions).toEqual(expectedTransactions) + }) - const [_, errors] = await signAndSend([ + it('should combine addExecutorLzReceiveOption settings for one chain into one transaction for msgType: 1', async () => { + // This is the OApp config that we want to use against our contracts + graph = { + contracts: [ + { + point: ethPoint, + }, + { + point: avaxPoint, + }, + ], + connections: [ + { + vector: { from: ethPoint, to: avaxPoint }, + config: { + enforcedOptions: [ + { + msgType: 1, + gas: '250000', + value: '0', + }, + { + msgType: 1, + gas: '550000', + value: '2', + }, + ], + }, + }, + { + vector: { from: avaxPoint, to: ethPoint }, + config: {}, + }, + ], + } + const options = Options.newOptions() + .addExecutorLzReceiveOption(250000, 0) + .addExecutorLzReceiveOption(550000, 2) + .toHex() + .toLowerCase() + + // Now we configure the OApp + transactions = await configureOApp(graph, oappSdkFactory) + const expectedTransactions = [ await ethOAppSdk.setEnforcedOptions([ { eid: avaxPoint.eid, - msgType: 1, - options: '0x00030100110100000000000000000000000000030d40', + option: { + msgType: 1, + options: options, + }, }, ]), - ]) - expect(errors).toEqual([]) + ] + expect(transactions).toEqual(expectedTransactions) + }) + + it('should combine addExecutorNativeDropOption settings for two chains into one transaction for msgType: 2', async () => { + // This is the OApp config that we want to use against our contracts + graph = { + contracts: [ + { + point: ethPoint, + }, + { + point: avaxPoint, + }, + { + point: bscPoint, + }, + ], + connections: [ + { + vector: { from: ethPoint, to: avaxPoint }, + config: { + enforcedOptions: [ + { + msgType: 2, + amount: 1, + receiver: '0x0000000000000000000000000000000000000001', + }, + { + msgType: 2, + amount: 2, + receiver: '0x000000000000000000000000000000000000002', + }, + ], + }, + }, + { + vector: { from: ethPoint, to: bscPoint }, + config: { + enforcedOptions: [ + { + msgType: 2, + amount: 1, + receiver: '0x000000000000000000000000000000000000003', + }, + ], + }, + }, + ], + } // Now we configure the OApp transactions = await configureOApp(graph, oappSdkFactory) - expect(transactions).toEqual([ - await avaxOAppSdk.setEnforcedOptions([ + + const avaxOptions = Options.newOptions() + .addExecutorNativeDropOption(1, '0x0000000000000000000000000000000000000001') + .addExecutorNativeDropOption(2, '0x0000000000000000000000000000000000000002') + .toHex() + .toLowerCase() + + const bscOptions = Options.newOptions() + .addExecutorNativeDropOption(1, '0x0000000000000000000000000000000000000003') + .toHex() + .toLowerCase() + + const expectedTransactions = [ + await ethOAppSdk.setEnforcedOptions([ { - eid: ethPoint.eid, - msgType: 1, - options: '0x00030100110100000000000000000000000000030d40', + eid: avaxPoint.eid, + option: { + msgType: 2, + options: avaxOptions, + }, + }, + { + eid: bscPoint.eid, + option: { + msgType: 2, + options: bscOptions, + }, }, ]), - ]) + ] + expect(transactions).toEqual(expectedTransactions) + }) + + it('should combine addExecutorComposeOption settings into one transaction for msgType: 3', async () => { + // This is the OApp config that we want to use against our contracts + graph = { + contracts: [ + { + point: ethPoint, + }, + { + point: avaxPoint, + }, + ], + connections: [ + { + vector: { from: ethPoint, to: avaxPoint }, + config: { + enforcedOptions: [ + { + msgType: ExecutorOptionType.COMPOSE, + index: 0, + gas: 200000, + value: 1, + }, + { + msgType: ExecutorOptionType.COMPOSE, + index: 1, + gas: 200500, + value: 0, + }, + { + msgType: ExecutorOptionType.COMPOSE, + index: 2, + gas: 300000, + value: 2, + }, + { + msgType: ExecutorOptionType.COMPOSE, + index: 3, + gas: 100000, + value: 0, + }, + ], + }, + }, + { + vector: { from: avaxPoint, to: ethPoint }, + config: {}, + }, + ], + } + + // Now we configure the OApp + transactions = await configureOApp(graph, oappSdkFactory) + const options = Options.newOptions() + .addExecutorComposeOption(0, 200000, 1) + .addExecutorComposeOption(1, 200500, 0) + .addExecutorComposeOption(2, 300000, 2) + .addExecutorComposeOption(3, 100000, 0) + .toHex() + .toLowerCase() + const expectedTransactions = [ + await ethOAppSdk.setEnforcedOptions([ + { + eid: avaxPoint.eid, + option: { + msgType: 3, + options: options, + }, + }, + ]), + ] + expect(transactions).toEqual(expectedTransactions) + }) + + it('should combine addExecutorComposeOption settings for two chains into one transaction for msgType: 3', async () => { + // This is the OApp config that we want to use against our contracts + graph = { + contracts: [ + { + point: ethPoint, + }, + { + point: avaxPoint, + }, + { + point: bscPoint, + }, + ], + connections: [ + { + vector: { from: ethPoint, to: avaxPoint }, + config: { + enforcedOptions: [ + { + msgType: ExecutorOptionType.COMPOSE, + index: 0, + gas: 200000, + value: 1, + }, + { + msgType: ExecutorOptionType.COMPOSE, + index: 1, + gas: 200500, + value: 0, + }, + ], + }, + }, + { + vector: { from: ethPoint, to: bscPoint }, + config: { + enforcedOptions: [ + { + msgType: ExecutorOptionType.COMPOSE, + index: 0, + gas: 300000, + value: 0, + }, + { + msgType: ExecutorOptionType.COMPOSE, + index: 1, + gas: 200005, + value: 1, + }, + ], + }, + }, + ], + } + + // Now we configure the OApp + transactions = await configureOApp(graph, oappSdkFactory) + + const avaxOptions = Options.newOptions() + .addExecutorComposeOption(0, 200000, 1) + .addExecutorComposeOption(1, 200500, 0) + .toHex() + .toLowerCase() + + const bscOptions = Options.newOptions() + .addExecutorComposeOption(0, 300000, 0) + .addExecutorComposeOption(1, 200005, 1) + .toHex() + .toLowerCase() + + const expectedTransactions = [ + await ethOAppSdk.setEnforcedOptions([ + { + eid: avaxPoint.eid, + option: { + msgType: 3, + options: avaxOptions, + }, + }, + { + eid: bscPoint.eid, + option: { + msgType: 3, + options: bscOptions, + }, + }, + ]), + ] + expect(transactions).toEqual(expectedTransactions) + }) + + it('should combine addExecutorLzReceiveOption, addExecutorNativeDropOption, and addExecutorComposeOption settings for two chains when applicable', async () => { + // This is the OApp config that we want to use against our contracts + graph = { + contracts: [ + { + point: ethPoint, + }, + { + point: avaxPoint, + }, + { + point: bscPoint, + }, + ], + connections: [ + { + vector: { from: ethPoint, to: avaxPoint }, + config: { + enforcedOptions: [ + { + msgType: 1, + gas: '250000', + value: '0', + }, + { + msgType: 1, + gas: '550000', + value: '2', + }, + { + msgType: 1, + gas: '450000', + value: '1', + }, + { + msgType: 2, + amount: 1, + receiver: '0x0000000000000000000000000000000000000001', + }, + { + msgType: 2, + amount: 2, + receiver: '0x000000000000000000000000000000000000002', + }, + { + msgType: 3, + index: 0, + gas: 200000, + value: 1, + }, + { + msgType: 3, + index: 1, + gas: 200500, + value: 0, + }, + { + msgType: 3, + index: 2, + gas: 300000, + value: 2, + }, + ], + }, + }, + { + vector: { from: ethPoint, to: bscPoint }, + config: { + enforcedOptions: [ + { + msgType: 1, + gas: '300000', + value: '1', + }, + { + msgType: 2, + amount: 3, + receiver: '0x000000000000000000000000000000000000001', + }, + { + msgType: 3, + index: 0, + gas: 100000, + value: 0, + }, + ], + }, + }, + ], + } + + const avaxLzReceiveOptions = Options.newOptions() + .addExecutorLzReceiveOption(250000, 0) + .addExecutorLzReceiveOption(550000, 2) + .addExecutorLzReceiveOption(450000, 1) + .toHex() + .toLowerCase() + + const avaxOptionsExecutor = Options.newOptions() + .addExecutorNativeDropOption(1, '0x0000000000000000000000000000000000000001') + .addExecutorNativeDropOption(2, '0x0000000000000000000000000000000000000002') + .toHex() + .toLowerCase() + + const avaxComposeOptions = Options.newOptions() + .addExecutorComposeOption(0, 200000, 1) + .addExecutorComposeOption(1, 200500, 0) + .addExecutorComposeOption(2, 300000, 2) + .toHex() + .toLowerCase() + + const bscLzReceiveOptions = Options.newOptions() + .addExecutorLzReceiveOption(300000, 1) + .toHex() + .toLowerCase() + + const bscOptionsExecutor = Options.newOptions() + .addExecutorNativeDropOption(3, '0x0000000000000000000000000000000000000001') + .toHex() + .toLowerCase() + + const bscComposeOptions = Options.newOptions() + .addExecutorComposeOption(0, 100000, 0) + .toHex() + .toLowerCase() + + // Now we configure the OApp + transactions = await configureOApp(graph, oappSdkFactory) + const expectedTransactions = [ + await ethOAppSdk.setEnforcedOptions([ + { + eid: avaxPoint.eid, + option: { + msgType: 1, + options: avaxLzReceiveOptions, + }, + }, + { + eid: avaxPoint.eid, + option: { + msgType: 2, + options: avaxOptionsExecutor, + }, + }, + { + eid: avaxPoint.eid, + option: { + msgType: 3, + options: avaxComposeOptions, + }, + }, + { + eid: bscPoint.eid, + option: { + msgType: 1, + options: bscLzReceiveOptions, + }, + }, + { + eid: bscPoint.eid, + option: { + msgType: 2, + options: bscOptionsExecutor, + }, + }, + { + eid: bscPoint.eid, + option: { + msgType: 3, + options: bscComposeOptions, + }, + }, + ]), + ] + expect(transactions).toEqual(expectedTransactions) + }) + + it('should combine both addExecutorOrderedExecutionOption into one transaction', async () => { + // This is the OApp config that we want to use against our contracts + graph = { + contracts: [ + { + point: ethPoint, + }, + { + point: avaxPoint, + }, + ], + connections: [ + { + vector: { from: ethPoint, to: avaxPoint }, + config: { + enforcedOptions: [ + { + msgType: 4, + }, + { + msgType: 4, + }, + ], + }, + }, + { + vector: { from: avaxPoint, to: ethPoint }, + config: {}, + }, + ], + } + + // Now we configure the OApp + transactions = await configureOApp(graph, oappSdkFactory) + const options = Options.newOptions() + .addExecutorOrderedExecutionOption() + .addExecutorOrderedExecutionOption() + .toHex() + .toLowerCase() + const expectedTransactions = [ + await ethOAppSdk.setEnforcedOptions([ + { + eid: avaxPoint.eid, + option: { + msgType: 4, + options: options, + }, + }, + ]), + ] + expect(transactions).toEqual(expectedTransactions) }) afterEach(async () => {