diff --git a/packages/ua-devtools-evm/package.json b/packages/ua-devtools-evm/package.json index d42fd1339..edc603d9f 100644 --- a/packages/ua-devtools-evm/package.json +++ b/packages/ua-devtools-evm/package.json @@ -38,6 +38,7 @@ "@layerzerolabs/devtools": "~0.0.2", "@layerzerolabs/devtools-evm": "~0.0.2", "@layerzerolabs/lz-definitions": "~2.0.7", + "@layerzerolabs/lz-utility-v2": "~2.0.7", "@layerzerolabs/protocol-devtools": "~0.0.2", "@layerzerolabs/protocol-devtools-evm": "~0.0.2", "@layerzerolabs/test-devtools": "~0.0.2", @@ -55,6 +56,7 @@ "peerDependencies": { "@layerzerolabs/devtools-evm": "~0.0.2", "@layerzerolabs/lz-definitions": "~2.0.7", + "@layerzerolabs/lz-utility-v2": "~2.0.7", "@layerzerolabs/protocol-devtools": "~0.0.2", "@layerzerolabs/protocol-devtools-evm": "~0.0.2", "@layerzerolabs/ua-devtools": "~0.0.2", diff --git a/packages/ua-devtools-evm/src/oapp/sdk.ts b/packages/ua-devtools-evm/src/oapp/sdk.ts index 9137c19b1..87179872d 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 } from '@layerzerolabs/ua-devtools' +import type { IOApp, EnforcedOptions, OAppEnforcedOptionConfig } from '@layerzerolabs/ua-devtools' import type { Bytes32, Address, OmniTransaction } from '@layerzerolabs/devtools' import { type OmniContract, @@ -11,6 +11,7 @@ import { import type { EndpointId } from '@layerzerolabs/lz-definitions' import type { EndpointFactory, IEndpoint } from '@layerzerolabs/protocol-devtools' import { OmniSDK } from '@layerzerolabs/devtools-evm' +import { ExecutorOptionType, Options } from '@layerzerolabs/lz-utility-v2' export class OApp extends OmniSDK implements IOApp { constructor( @@ -55,4 +56,36 @@ export class OApp extends OmniSDK implements IOApp { const data = this.contract.contract.interface.encodeFunctionData('setPeer', [eid, makeBytes32(address)]) return this.createTransaction(data) } + + async getEnforcedOptions(eid: EndpointId, msgType: number): Promise { + return await this.contract.contract.enforcedOptions(eid, msgType) + } + + async setEnforcedOptions(enforcedOptions: EnforcedOptions[]): Promise { + const data = this.contract.contract.interface.encodeFunctionData('setEnforcedOptions', [enforcedOptions]) + return this.createTransaction(data) + } + + 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`) + } + } } diff --git a/packages/ua-devtools/package.json b/packages/ua-devtools/package.json index c603f130e..570e8a5c1 100644 --- a/packages/ua-devtools/package.json +++ b/packages/ua-devtools/package.json @@ -33,6 +33,7 @@ "@layerzerolabs/devtools": "~0.0.2", "@layerzerolabs/io-devtools": "~0.0.2", "@layerzerolabs/lz-definitions": "~2.0.7", + "@layerzerolabs/lz-utility-v2": "~2.0.7", "@layerzerolabs/protocol-devtools": "~0.0.2", "@layerzerolabs/test-devtools": "~0.0.2", "@types/jest": "^29.5.11", @@ -49,6 +50,7 @@ "@layerzerolabs/devtools": "~0.0.2", "@layerzerolabs/io-devtools": "~0.0.2", "@layerzerolabs/lz-definitions": "~2.0.7", + "@layerzerolabs/lz-utility-v2": "~2.0.7", "@layerzerolabs/protocol-devtools": "~0.0.2", "zod": "^3.22.4" }, diff --git a/packages/ua-devtools/src/oapp/config.ts b/packages/ua-devtools/src/oapp/config.ts index 51517d7c1..1b5d692cb 100644 --- a/packages/ua-devtools/src/oapp/config.ts +++ b/packages/ua-devtools/src/oapp/config.ts @@ -1,5 +1,5 @@ import { flattenTransactions, type OmniTransaction } from '@layerzerolabs/devtools' -import { OAppFactory, OAppOmniGraph } from './types' +import { EnforcedOptions, OAppEnforcedOptionConfig, OAppFactory, OAppOmniGraph } from './types' import { createModuleLogger, printBoolean } from '@layerzerolabs/io-devtools' import { formatOmniVector, isDeepEqual } from '@layerzerolabs/devtools' import { Uln302ExecutorConfig, Uln302UlnConfig } from '@layerzerolabs/protocol-devtools' @@ -15,6 +15,7 @@ export const configureOApp: OAppConfigurator = async (graph: OAppOmniGraph, crea await configureReceiveLibraryTimeouts(graph, createSdk), await configureSendConfig(graph, createSdk), await configureReceiveConfig(graph, createSdk), + await configureEnforcedOptions(graph, createSdk), ]) export const configureOAppPeers: OAppConfigurator = async (graph, createSdk) => { @@ -186,3 +187,37 @@ export const configureReceiveConfig: OAppConfigurator = async (graph, createSdk) }) ) ) + +export const configureEnforcedOptions: OAppConfigurator = async (graph, createSdk) => + flattenTransactions( + await Promise.all( + graph.connections.map(async ({ vector: { from, to }, config }): Promise => { + const transactions: OmniTransaction[] = [] + + if (config?.enforcedOptions) { + 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) { + transactions.push(await oappSdk.setEnforcedOptions(enforcedOptions)) + } + } + + return transactions + }) + ) + ) diff --git a/packages/ua-devtools/src/oapp/schema.ts b/packages/ua-devtools/src/oapp/schema.ts index 33c457e47..37ccc964c 100644 --- a/packages/ua-devtools/src/oapp/schema.ts +++ b/packages/ua-devtools/src/oapp/schema.ts @@ -1,7 +1,13 @@ import { z } from 'zod' import { AddressSchema, UIntSchema } from '@layerzerolabs/devtools' import { Uln302ExecutorConfigSchema, Uln302UlnConfigSchema, TimeoutSchema } from '@layerzerolabs/protocol-devtools' -import type { OAppEdgeConfig, OAppReceiveConfig, OAppReceiveLibraryConfig, OAppSendConfig } from './types' +import { + OAppEdgeConfig, + OAppEnforcedOptionConfig, + OAppReceiveConfig, + OAppReceiveLibraryConfig, + OAppSendConfig, +} from './types' export const OAppReceiveLibraryConfigSchema = z.object({ gracePeriod: UIntSchema, @@ -17,10 +23,18 @@ export const OAppReceiveConfigSchema = z.object({ ulnConfig: Uln302UlnConfigSchema, }) satisfies z.ZodSchema +export const OAppEnforcedOptionsSchema = z.array( + z.object({ + msgType: z.number(), + options: z.string(), + }) +) satisfies z.ZodSchema + export const OAppEdgeConfigSchema = z.object({ sendLibrary: AddressSchema.optional(), receiveLibraryConfig: OAppReceiveLibraryConfigSchema.optional(), receiveLibraryTimeoutConfig: TimeoutSchema.optional(), sendConfig: OAppSendConfigSchema.optional(), receiveConfig: OAppReceiveConfigSchema.optional(), + enforcedOptions: OAppEnforcedOptionsSchema.optional(), }) satisfies z.ZodSchema diff --git a/packages/ua-devtools/src/oapp/types.ts b/packages/ua-devtools/src/oapp/types.ts index 837914cd7..a6d4324a7 100644 --- a/packages/ua-devtools/src/oapp/types.ts +++ b/packages/ua-devtools/src/oapp/types.ts @@ -1,5 +1,5 @@ import type { EndpointId } from '@layerzerolabs/lz-definitions' -import type { IEndpoint, Timeout } from '@layerzerolabs/protocol-devtools' +import type { IEndpoint, Timeout, Uln302ExecutorConfig, Uln302UlnConfig } from '@layerzerolabs/protocol-devtools' import type { Address, Bytes32, @@ -10,13 +10,22 @@ import type { OmniTransaction, OmniVector, } from '@layerzerolabs/devtools' -import type { Uln302ExecutorConfig, Uln302UlnConfig } from '@layerzerolabs/protocol-devtools' +import { ExecutorOptionType, Options } from '@layerzerolabs/lz-utility-v2' export interface IOApp extends IOmniSDK { getEndpointSDK(): Promise getPeer(eid: EndpointId): Promise hasPeer(eid: EndpointId, address: Bytes32 | Address | null | undefined): Promise setPeer(eid: EndpointId, peer: Bytes32 | Address | 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 } export interface OAppReceiveLibraryConfig { @@ -39,8 +48,47 @@ export interface OAppEdgeConfig { receiveLibraryTimeoutConfig?: Timeout sendConfig?: OAppSendConfig receiveConfig?: OAppReceiveConfig + enforcedOptions?: OAppEnforcedOptionConfig[] +} + +interface BaseExecutorOption { + msgType: ExecutorOptionType +} + +interface EndcodedOption extends BaseExecutorOption { + options: string +} + +interface ExecutorLzReceiveOption extends BaseExecutorOption { + msgType: ExecutorOptionType.LZ_RECEIVE + gas: string | number + value: string | number } +interface ExecutorNativeDropOption extends BaseExecutorOption { + msgType: ExecutorOptionType.NATIVE_DROP + amount: string | number + receiver: string +} + +interface ExecutorComposeOption extends BaseExecutorOption { + msgType: ExecutorOptionType.COMPOSE + index: number + gas: string | number + value: string | number +} + +interface ExecutorOrderedExecutionOption extends BaseExecutorOption { + msgType: ExecutorOptionType.ORDERED +} + +export type OAppEnforcedOptionConfig = + | ExecutorLzReceiveOption + | ExecutorNativeDropOption + | ExecutorComposeOption + | ExecutorOrderedExecutionOption + | EndcodedOption + export interface OAppPeers { vector: OmniVector hasPeer: boolean diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fac0c1b61..23b86c824 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -765,6 +765,9 @@ importers: '@layerzerolabs/lz-definitions': specifier: ~2.0.7 version: 2.0.7 + '@layerzerolabs/lz-utility-v2': + specifier: ~2.0.7 + version: 2.0.7 '@layerzerolabs/protocol-devtools': specifier: ~0.0.2 version: link:../protocol-devtools @@ -820,6 +823,9 @@ importers: '@layerzerolabs/lz-definitions': specifier: ~2.0.7 version: 2.0.7 + '@layerzerolabs/lz-utility-v2': + specifier: ~2.0.7 + version: 2.0.7 '@layerzerolabs/protocol-devtools': specifier: ~0.0.2 version: link:../protocol-devtools diff --git a/tests/ua-devtools-evm-hardhat-test/contracts/DefaultOApp.sol b/tests/ua-devtools-evm-hardhat-test/contracts/DefaultOApp.sol index 9787e71b1..2c03d8a1f 100644 --- a/tests/ua-devtools-evm-hardhat-test/contracts/DefaultOApp.sol +++ b/tests/ua-devtools-evm-hardhat-test/contracts/DefaultOApp.sol @@ -2,8 +2,9 @@ pragma solidity ^0.8.22; import { OApp, Origin } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OApp.sol"; +import { OAppOptionsType3 } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/libs/OAppOptionsType3.sol"; -contract DefaultOApp is OApp { +contract DefaultOApp is OApp, OAppOptionsType3 { constructor(address _endpoint, address _owner) OApp(_endpoint, _owner) {} function _lzReceive(Origin calldata, bytes32, bytes calldata, address, bytes calldata) internal virtual override {} diff --git a/tests/ua-devtools-evm-hardhat-test/test/__utils__/oapp.ts b/tests/ua-devtools-evm-hardhat-test/test/__utils__/oapp.ts index 90ceb25dc..92f39903e 100644 --- a/tests/ua-devtools-evm-hardhat-test/test/__utils__/oapp.ts +++ b/tests/ua-devtools-evm-hardhat-test/test/__utils__/oapp.ts @@ -176,7 +176,7 @@ export const getDefaultAvaxConfig = async (): Promise => { } } -const getLibraryAddress = async (library: OmniPointHardhat): Promise => { +export const getLibraryAddress = async (library: OmniPointHardhat): Promise => { const contractFactory = createConnectedContractFactory() const { contract } = await contractFactory(library) return contract.address 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 ac2c20c29..99edfb397 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,29 +1,20 @@ import 'hardhat' -import { configureOApp, OAppEdgeConfig } from '@layerzerolabs/ua-devtools' -import { createOAppFactory } from '@layerzerolabs/ua-devtools-evm' -import { - createConnectedContractFactory, - createSignerFactory, - OmniGraphBuilderHardhat, - OmniPointHardhat, -} from '@layerzerolabs/devtools-evm-hardhat' -import type { OmniGraphHardhat } from '@layerzerolabs/devtools-evm-hardhat' -import { omniContractToPoint } from '@layerzerolabs/devtools-evm' import { EndpointId } from '@layerzerolabs/lz-definitions' -import { - deployOAppFixture, - getDefaultAvaxConfig, - getDefaultEthConfig, - OAppTestConfig, - setUpConfig, - setUpOmniGraphHardhat, -} from '../__utils__/oapp' +import { deployOAppFixture } from '../__utils__/oapp' import { setupDefaultEndpoint } from '../__utils__/endpoint' -import { OmniTransaction } from '@layerzerolabs/devtools' +import { createConnectedContractFactory, createSignerFactory } from '@layerzerolabs/devtools-evm-hardhat' +import { createOAppFactory } from '@layerzerolabs/ua-devtools-evm' +import { configureOApp, OAppOmniGraph } from '@layerzerolabs/ua-devtools' +import { omniContractToPoint } from '@layerzerolabs/devtools-evm' +import { getLibraryAddress } from '../__utils__/oapp' +import { ethSendUln2_Opt2, ethReceiveUln2_Opt2, avaxSendUln2_Opt2, avaxReceiveUln2_Opt2 } from '../__utils__/endpoint' describe('oapp/config', () => { - const ethContract = { eid: EndpointId.ETHEREUM_V2_MAINNET, contractName: 'DefaultOApp' } - const avaxContract = { eid: EndpointId.AVALANCHE_V2_MAINNET, contractName: 'DefaultOApp' } + const ethPointHardhat = { eid: EndpointId.ETHEREUM_V2_MAINNET, contractName: 'DefaultOApp' } + const avaxPointHardhat = { eid: EndpointId.AVALANCHE_V2_MAINNET, contractName: 'DefaultOApp' } + + const contractFactory = createConnectedContractFactory() + const oappSdkFactory = createOAppFactory(contractFactory) // This is the OApp config that we want to use against our contracts beforeEach(async () => { @@ -31,247 +22,405 @@ describe('oapp/config', () => { await setupDefaultEndpoint() }) - it('should return all setPeer transactions', async () => { - const ethTestConfig: OAppTestConfig = await getDefaultEthConfig() - const avaxTestConfig: OAppTestConfig = await getDefaultAvaxConfig() - const ethOAppConfig: OAppEdgeConfig = await setUpConfig(ethTestConfig) - const avaxOAppConfig: OAppEdgeConfig = await setUpConfig(avaxTestConfig) - const config: OmniGraphHardhat = setUpOmniGraphHardhat( - ethContract, - ethOAppConfig, - avaxContract, - avaxOAppConfig - ) - const builder = await OmniGraphBuilderHardhat.fromConfig(config) - - const contractFactory = createConnectedContractFactory() - const sdkFactory = createOAppFactory(contractFactory) - - // And finally the test assertions - const ethPoint = omniContractToPoint(await contractFactory(ethContract)) - const ethOAppSdk = await sdkFactory(ethPoint) - const ethEndpointSdk = await ethOAppSdk.getEndpointSDK() - - const avaxPoint = omniContractToPoint(await contractFactory(avaxContract)) - const avaxOAppSdk = await sdkFactory(avaxPoint) - const avaxEndpointSdk = await avaxOAppSdk.getEndpointSDK() - - const expectedOAppConfigTransactions: OmniTransaction[] = await createExpectedTransactions( - ethContract, - ethTestConfig, - avaxContract, - avaxTestConfig - ) - - { - const signerFactory = createSignerFactory() - // register new Send and Receive ULNs on ETH - const ethSigner = await signerFactory(ethContract.eid) - await ethSigner.signAndSend(await ethEndpointSdk.registerLibrary(ethTestConfig.sendLibrary)) - await ethSigner.signAndSend(await ethEndpointSdk.registerLibrary(ethTestConfig.receiveLibrary)) - - await ethSigner.signAndSend( - await ethEndpointSdk.setSendLibrary(ethPoint.address, avaxPoint.eid, ethTestConfig.sendLibrary) - ) - await ethSigner.signAndSend( - await ethEndpointSdk.setReceiveLibrary(ethPoint.address, avaxPoint.eid, ethTestConfig.receiveLibrary, 0) - ) - - // register new Send and Receive ULNs AVAX - const avaxSigner = await signerFactory(avaxContract.eid) - await avaxSigner.signAndSend(await avaxEndpointSdk.registerLibrary(avaxTestConfig.sendLibrary)) - await avaxSigner.signAndSend(await avaxEndpointSdk.registerLibrary(avaxTestConfig.receiveLibrary)) - - await avaxSigner.signAndSend( - await avaxEndpointSdk.setSendLibrary(avaxPoint.address, ethPoint.eid, avaxTestConfig.sendLibrary) - ) - await avaxSigner.signAndSend( - await avaxEndpointSdk.setReceiveLibrary( - avaxPoint.address, - ethPoint.eid, - avaxTestConfig.receiveLibrary, - 0 - ) - ) - } + describe('configureOApp', () => { + it('should return an empty array with an empty config', async () => { + const graph: OAppOmniGraph = { + contracts: [], + connections: [], + } + const contractFactory = createConnectedContractFactory() + const sdkFactory = createOAppFactory(contractFactory) - // This is where the configuration happens - const transactions = await configureOApp(builder.graph, sdkFactory) - expect(transactions).toEqual(expectedOAppConfigTransactions) - }) + // Now we configure the OApp + const transactions = await configureOApp(graph, sdkFactory) - it('should exclude setPeer transactions for peers that have been set', async () => { - const ethTestConfig: OAppTestConfig = await getDefaultEthConfig() - const avaxTestConfig: OAppTestConfig = await getDefaultAvaxConfig() - const ethOAppConfig: OAppEdgeConfig = await setUpConfig(ethTestConfig) - const avaxOAppConfig: OAppEdgeConfig = await setUpConfig(avaxTestConfig) - const config: OmniGraphHardhat = setUpOmniGraphHardhat( - ethContract, - ethOAppConfig, - avaxContract, - avaxOAppConfig - ) - const builder = await OmniGraphBuilderHardhat.fromConfig(config) - - const contractFactory = createConnectedContractFactory() - const sdkFactory = createOAppFactory(contractFactory) - - // And finally the test assertions - const ethPoint = omniContractToPoint(await contractFactory(ethContract)) - const ethOAppSdk = await sdkFactory(ethPoint) - const ethEndpointSdk = await ethOAppSdk.getEndpointSDK() - - const avaxPoint = omniContractToPoint(await contractFactory(avaxContract)) - const avaxOAppSdk = await sdkFactory(avaxPoint) - const avaxEndpointSdk = await avaxOAppSdk.getEndpointSDK() - - let expectedOAppConfigTransactions: OmniTransaction[] = await createExpectedTransactions( - ethContract, - ethTestConfig, - avaxContract, - avaxTestConfig - ) - - let ethSetPeerTx - { - const signerFactory = createSignerFactory() - // register new Send and Receive ULNs on ETH - const ethSigner = await signerFactory(ethContract.eid) - await ethSigner.signAndSend(await ethEndpointSdk.registerLibrary(ethTestConfig.sendLibrary)) - await ethSigner.signAndSend(await ethEndpointSdk.registerLibrary(ethTestConfig.receiveLibrary)) - - await ethSigner.signAndSend( - await ethEndpointSdk.setSendLibrary(ethPoint.address, avaxPoint.eid, ethTestConfig.sendLibrary) - ) - await ethSigner.signAndSend( - await ethEndpointSdk.setReceiveLibrary(ethPoint.address, avaxPoint.eid, ethTestConfig.receiveLibrary, 0) - ) - - // execute set peer tx before running wire all - ethSetPeerTx = await ethOAppSdk.setPeer(avaxPoint.eid, avaxPoint.address) - await ethSigner.signAndSend(ethSetPeerTx) - - // register new Send and Receive ULNs AVAX - const avaxSigner = await signerFactory(avaxContract.eid) - await avaxSigner.signAndSend(await avaxEndpointSdk.registerLibrary(avaxTestConfig.sendLibrary)) - await avaxSigner.signAndSend(await avaxEndpointSdk.registerLibrary(avaxTestConfig.receiveLibrary)) - - await avaxSigner.signAndSend( - await avaxEndpointSdk.setSendLibrary(avaxPoint.address, ethPoint.eid, avaxTestConfig.sendLibrary) - ) - await avaxSigner.signAndSend( - await avaxEndpointSdk.setReceiveLibrary( - avaxPoint.address, - ethPoint.eid, - avaxTestConfig.receiveLibrary, - 0 - ) - ) - } - - // This is where the configuration happens - const transactions = await configureOApp(builder.graph, sdkFactory) - // remove set peer tx from expectedOAppConfigTransactions - expectedOAppConfigTransactions = expectedOAppConfigTransactions.filter((obj) => obj.data !== ethSetPeerTx.data) - expect(transactions).toEqual(expectedOAppConfigTransactions) + expect(transactions).toEqual([]) + }) }) -}) -const createExpectedTransactions = async ( - ethContract: OmniPointHardhat, - ethTestConfig: OAppTestConfig, - avaxContract: OmniPointHardhat, - avaxTestConfig: OAppTestConfig -): Promise => { - const contractFactory = createConnectedContractFactory() - const sdkFactory = createOAppFactory(contractFactory) - - const ethPoint = omniContractToPoint(await contractFactory(ethContract)) - const ethOAppSdk = await sdkFactory(ethPoint) - const ethEndpointSdk = await ethOAppSdk.getEndpointSDK() - - const avaxPoint = omniContractToPoint(await contractFactory(avaxContract)) - const avaxOAppSdk = await sdkFactory(avaxPoint) - const avaxEndpointSdk = await avaxOAppSdk.getEndpointSDK() - return [ - await ethOAppSdk.setPeer(avaxPoint.eid, avaxPoint.address), - await avaxOAppSdk.setPeer(ethPoint.eid, ethPoint.address), - await ethEndpointSdk.setReceiveLibraryTimeout(ethPoint.address, avaxPoint.eid, ethTestConfig.receiveLibrary, 0), - await avaxEndpointSdk.setReceiveLibraryTimeout( - avaxPoint.address, - ethPoint.eid, - avaxTestConfig.receiveLibrary, - 0 - ), - await ethEndpointSdk.setExecutorConfig(ethPoint.address, ethTestConfig.sendLibrary, [ - { - eid: avaxPoint.eid, - executorConfig: { - maxMessageSize: ethTestConfig.executorMaxMessageSize, - executor: ethTestConfig.executorLibrary, - }, - }, - ]), - await ethEndpointSdk.setUlnConfig(ethPoint.address, ethTestConfig.sendLibrary, [ - { - eid: avaxPoint.eid, - ulnConfig: { - confirmations: ethTestConfig.sendUlnConfirmations, - optionalDVNThreshold: ethTestConfig.sendUlnOptionalDVNThreshold, - requiredDVNs: ethTestConfig.sendUlnRequiredDVNs, - optionalDVNs: ethTestConfig.sendUlnOptionalDVNs, - requiredDVNCount: ethTestConfig.sendUlnRequiredDVNs.length, - optionalDVNCount: ethTestConfig.sendUlnOptionalDVNs.length, - }, - }, - ]), - await avaxEndpointSdk.setExecutorConfig(avaxPoint.address, avaxTestConfig.sendLibrary, [ - { - eid: ethPoint.eid, - executorConfig: { - maxMessageSize: avaxTestConfig.executorMaxMessageSize, - executor: avaxTestConfig.executorLibrary, - }, - }, - ]), - await avaxEndpointSdk.setUlnConfig(avaxPoint.address, avaxTestConfig.sendLibrary, [ - { - eid: ethPoint.eid, - ulnConfig: { - confirmations: avaxTestConfig.sendUlnConfirmations, - optionalDVNThreshold: avaxTestConfig.sendUlnOptionalDVNThreshold, - requiredDVNs: avaxTestConfig.sendUlnRequiredDVNs, - optionalDVNs: avaxTestConfig.sendUlnOptionalDVNs, - requiredDVNCount: avaxTestConfig.sendUlnRequiredDVNs.length, - optionalDVNCount: avaxTestConfig.sendUlnOptionalDVNs.length, - }, - }, - ]), - await ethEndpointSdk.setUlnConfig(ethPoint.address, ethTestConfig.receiveLibrary, [ + describe('configureOAppPeers', () => { + it('should return all setPeer transactions', async () => { + const ethContract = await contractFactory(ethPointHardhat) + const avaxContract = await contractFactory(avaxPointHardhat) + + const ethPoint = omniContractToPoint(ethContract) + const ethOAppSdk = await oappSdkFactory(ethPoint) + + const avaxPoint = omniContractToPoint(avaxContract) + const avaxOAppSdk = await oappSdkFactory(avaxPoint) + + // This is the OApp config that we want to use against our contracts + const graph: OAppOmniGraph = { + contracts: [ + { + point: ethPoint, + }, + { + point: avaxPoint, + }, + ], + connections: [ + { + vector: { from: ethPoint, to: avaxPoint }, + config: undefined, + }, + { + vector: { from: avaxPoint, to: ethPoint }, + config: undefined, + }, + ], + } + + // Now we configure the OApp + const transactions = await configureOApp(graph, oappSdkFactory) + + expect(transactions).toEqual([ + await ethOAppSdk.setPeer(avaxPoint.eid, avaxPoint.address), + await avaxOAppSdk.setPeer(ethPoint.eid, ethPoint.address), + ]) + }) + + it('should exclude setPeer transactions for peers that have been set', async () => { + const ethContract = await contractFactory(ethPointHardhat) + const avaxContract = await contractFactory(avaxPointHardhat) + + const ethPoint = omniContractToPoint(ethContract) + const ethOAppSdk = await oappSdkFactory(ethPoint) + + const avaxPoint = omniContractToPoint(avaxContract) + const avaxOAppSdk = await oappSdkFactory(avaxPoint) + + // This is the OApp config that we want to use against our contracts + const graph: OAppOmniGraph = { + contracts: [ + { + point: ethPoint, + }, + { + point: avaxPoint, + }, + ], + connections: [ + { + vector: { from: ethPoint, to: avaxPoint }, + config: undefined, + }, + { + vector: { from: avaxPoint, to: ethPoint }, + config: undefined, + }, + ], + } + + // Before we configure the OApp, we'll set some peers { - eid: avaxPoint.eid, - ulnConfig: { - confirmations: ethTestConfig.receiveUlnConfirmations, - optionalDVNThreshold: ethTestConfig.receiveUlnOptionalDVNThreshold, - requiredDVNs: ethTestConfig.receiveUlnRequiredDVNs, - optionalDVNs: ethTestConfig.receiveUlnOptionalDVNs, - requiredDVNCount: ethTestConfig.receiveUlnRequiredDVNs.length, - optionalDVNCount: ethTestConfig.receiveUlnOptionalDVNs.length, - }, - }, - ]), - await avaxEndpointSdk.setUlnConfig(avaxPoint.address, avaxTestConfig.receiveLibrary, [ + const signerFactory = createSignerFactory() + const ethSigner = await signerFactory(ethContract.eid) + const ethTransaction = await ethOAppSdk.setPeer(avaxPoint.eid, avaxPoint.address) + const ethResponse = await ethSigner.signAndSend(ethTransaction) + const ethReceipt = await ethResponse.wait() + expect(ethReceipt.from).toBe(await ethSigner.signer.getAddress()) + } + + // Now we configure the OApp + const transactions = await configureOApp(graph, oappSdkFactory) + + // And expect the setPeer on the eth contact not to be there + expect(transactions).toEqual([await avaxOAppSdk.setPeer(ethPoint.eid, ethPoint.address)]) + }) + }) + + describe('configureSendLibraries', () => { + it('should return all setPeer transactions and configureSendLibraries transactions', async () => { + const ethContract = await contractFactory(ethPointHardhat) + const avaxContract = await contractFactory(avaxPointHardhat) + + const ethPoint = omniContractToPoint(ethContract) + const ethOAppSdk = await oappSdkFactory(ethPoint) + const ethEndpointSdk = await ethOAppSdk.getEndpointSDK() + + const avaxPoint = omniContractToPoint(avaxContract) + const avaxOAppSdk = await oappSdkFactory(avaxPoint) + const avaxEndpointSdk = await avaxOAppSdk.getEndpointSDK() + + const ethSendLibrary = await getLibraryAddress(ethSendUln2_Opt2) + const avaxSendLibrary = await getLibraryAddress(avaxSendUln2_Opt2) + + // This is the OApp config that we want to use against our contracts + const graph: OAppOmniGraph = { + contracts: [ + { + point: ethPoint, + }, + { + point: avaxPoint, + }, + ], + connections: [ + { + vector: { from: ethPoint, to: avaxPoint }, + config: { + sendLibrary: ethSendLibrary, + }, + }, + { + vector: { from: avaxPoint, to: ethPoint }, + config: { + sendLibrary: avaxSendLibrary, + }, + }, + ], + } + + // Now we configure the OApp + const transactions = await configureOApp(graph, oappSdkFactory) + + expect(transactions).toEqual([ + await ethOAppSdk.setPeer(avaxPoint.eid, avaxPoint.address), + await avaxOAppSdk.setPeer(ethPoint.eid, ethPoint.address), + await ethEndpointSdk.setSendLibrary(ethPoint.address, avaxPoint.eid, ethSendLibrary), + await avaxEndpointSdk.setSendLibrary(avaxPoint.address, ethPoint.eid, avaxSendLibrary), + ]) + }) + it('should return all setPeer transactions and one configureSendLibraries transaction', async () => { + const ethContract = await contractFactory(ethPointHardhat) + const avaxContract = await contractFactory(avaxPointHardhat) + + const ethPoint = omniContractToPoint(ethContract) + const ethOAppSdk = await oappSdkFactory(ethPoint) + const ethEndpointSdk = await ethOAppSdk.getEndpointSDK() + + const avaxPoint = omniContractToPoint(avaxContract) + const avaxOAppSdk = await oappSdkFactory(avaxPoint) + const avaxEndpointSdk = await avaxOAppSdk.getEndpointSDK() + + const ethSendLibrary = await getLibraryAddress(ethSendUln2_Opt2) + const avaxSendLibrary = await getLibraryAddress(avaxSendUln2_Opt2) + + // This is the OApp config that we want to use against our contracts + const graph: OAppOmniGraph = { + contracts: [ + { + point: ethPoint, + }, + { + point: avaxPoint, + }, + ], + connections: [ + { + vector: { from: ethPoint, to: avaxPoint }, + config: { + sendLibrary: ethSendLibrary, + }, + }, + { + vector: { from: avaxPoint, to: ethPoint }, + config: { + sendLibrary: avaxSendLibrary, + }, + }, + ], + } + { - eid: ethPoint.eid, - ulnConfig: { - confirmations: avaxTestConfig.receiveUlnConfirmations, - optionalDVNThreshold: avaxTestConfig.receiveUlnOptionalDVNThreshold, - requiredDVNs: avaxTestConfig.receiveUlnRequiredDVNs, - optionalDVNs: avaxTestConfig.receiveUlnOptionalDVNs, - requiredDVNCount: avaxTestConfig.receiveUlnRequiredDVNs.length, - optionalDVNCount: avaxTestConfig.receiveUlnOptionalDVNs.length, - }, - }, - ]), - ] -} + const signerFactory = createSignerFactory() + // register new Send ULNs on ETH + const ethSigner = await signerFactory(ethContract.eid) + await ethSigner.signAndSend(await ethEndpointSdk.registerLibrary(ethSendLibrary)) + await ethSigner.signAndSend( + await ethEndpointSdk.setSendLibrary(ethPoint.address, avaxPoint.eid, ethSendLibrary) + ) + } + + // Now we configure the OApp + const transactions = await configureOApp(graph, oappSdkFactory) + const expected = [ + await ethOAppSdk.setPeer(avaxPoint.eid, avaxPoint.address), + await avaxOAppSdk.setPeer(ethPoint.eid, ethPoint.address), + await avaxEndpointSdk.setSendLibrary(avaxPoint.address, ethPoint.eid, avaxSendLibrary), + ] + expect(transactions).toEqual(expected) + }) + }) + + describe('configureReceiveLibraries', () => { + it('should return all setPeer transactions', async () => { + const ethContract = await contractFactory(ethPointHardhat) + const avaxContract = await contractFactory(avaxPointHardhat) + + const ethPoint = omniContractToPoint(ethContract) + const ethOAppSdk = await oappSdkFactory(ethPoint) + const ethEndpointSdk = await ethOAppSdk.getEndpointSDK() + + const avaxPoint = omniContractToPoint(avaxContract) + const avaxOAppSdk = await oappSdkFactory(avaxPoint) + const avaxEndpointSdk = await avaxOAppSdk.getEndpointSDK() + + const ethReceiveLibrary = await getLibraryAddress(ethReceiveUln2_Opt2) + const avaxReceiveLibrary = await getLibraryAddress(avaxReceiveUln2_Opt2) + + // This is the OApp config that we want to use against our contracts + const graph: OAppOmniGraph = { + contracts: [ + { + point: ethPoint, + }, + { + point: avaxPoint, + }, + ], + connections: [ + { + vector: { from: ethPoint, to: avaxPoint }, + config: { + receiveLibraryConfig: { + receiveLibrary: ethReceiveLibrary, + gracePeriod: 0, + }, + }, + }, + { + vector: { from: avaxPoint, to: ethPoint }, + config: { + receiveLibraryConfig: { + receiveLibrary: avaxReceiveLibrary, + gracePeriod: 0, + }, + }, + }, + ], + } + + // Now we configure the OApp + const transactions = await configureOApp(graph, oappSdkFactory) + expect(transactions).toEqual([ + await ethOAppSdk.setPeer(avaxPoint.eid, avaxPoint.address), + await avaxOAppSdk.setPeer(ethPoint.eid, ethPoint.address), + await ethEndpointSdk.setReceiveLibrary(ethPoint.address, avaxPoint.eid, ethReceiveLibrary, 0), + await avaxEndpointSdk.setReceiveLibrary(avaxPoint.address, ethPoint.eid, avaxReceiveLibrary, 0), + ]) + }) + }) + + describe('configureReceiveLibraryTimeouts', () => {}) + + describe('configureSendConfig', () => {}) + + describe('configureReceiveConfig', () => {}) + + describe('configureEnforcedOptions', () => { + it('should return only setPeer transaction when enforcedOptions is empty', async () => { + const ethContract = await contractFactory(ethPointHardhat) + const avaxContract = await contractFactory(avaxPointHardhat) + + const ethPoint = omniContractToPoint(ethContract) + const ethOAppSdk = await oappSdkFactory(ethPoint) + + const avaxPoint = omniContractToPoint(avaxContract) + const avaxOAppSdk = await oappSdkFactory(avaxPoint) + + // This is the OApp config that we want to use against our contracts + const graph: OAppOmniGraph = { + contracts: [ + { + point: ethPoint, + }, + { + point: avaxPoint, + }, + ], + connections: [ + { + vector: { from: ethPoint, to: avaxPoint }, + config: { + enforcedOptions: [], + }, + }, + { + vector: { from: avaxPoint, to: ethPoint }, + config: { + enforcedOptions: [], + }, + }, + ], + } + + // Now we configure the OApp + const transactions = await configureOApp(graph, oappSdkFactory) + + expect(transactions).toEqual([ + await ethOAppSdk.setPeer(avaxPoint.eid, avaxPoint.address), + await avaxOAppSdk.setPeer(ethPoint.eid, ethPoint.address), + ]) + }) + + it('should return setPeer and setEnforcedOptions transaction when enforcedOptions are set', async () => { + const ethContract = await contractFactory(ethPointHardhat) + const avaxContract = await contractFactory(avaxPointHardhat) + + const ethPoint = omniContractToPoint(ethContract) + const ethOAppSdk = await oappSdkFactory(ethPoint) + + const avaxPoint = omniContractToPoint(avaxContract) + const avaxOAppSdk = await oappSdkFactory(avaxPoint) + + // This is the OApp config that we want to use against our contracts + const graph: OAppOmniGraph = { + contracts: [ + { + point: ethPoint, + }, + { + point: avaxPoint, + }, + ], + connections: [ + { + vector: { from: ethPoint, to: avaxPoint }, + config: { + enforcedOptions: [ + { + msgType: 1, + options: '0x00030100110100000000000000000000000000030d40', + }, + ], + }, + }, + { + vector: { from: avaxPoint, to: ethPoint }, + config: { + enforcedOptions: [ + { + msgType: 1, + gas: '200000', + value: '0', + }, + ], + }, + }, + ], + } + // Now we configure the OApp + const transactions = await configureOApp(graph, oappSdkFactory) + expect(transactions).toEqual([ + await ethOAppSdk.setPeer(avaxPoint.eid, avaxPoint.address), + await avaxOAppSdk.setPeer(ethPoint.eid, ethPoint.address), + await ethOAppSdk.setEnforcedOptions([ + { + eid: avaxPoint.eid, + msgType: 1, + options: '0x00030100110100000000000000000000000000030d40', + }, + ]), + await avaxOAppSdk.setEnforcedOptions([ + { + eid: ethPoint.eid, + msgType: 1, + options: '0x00030100110100000000000000000000000000030d40', + }, + ]), + ]) + }) + }) +})