Skip to content

Commit

Permalink
 🪲Fixing configureEnforcedOptions (#338)
Browse files Browse the repository at this point in the history
  • Loading branch information
sirarthurmoney authored Feb 5, 2024
1 parent a42feab commit ce0c2f4
Show file tree
Hide file tree
Showing 6 changed files with 764 additions and 119 deletions.
7 changes: 7 additions & 0 deletions .changeset/long-dolphins-press.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@layerzerolabs/ua-devtools-evm-hardhat-test": patch
"@layerzerolabs/ua-devtools-evm": patch
"@layerzerolabs/ua-devtools": patch
---

Fixing configureEnforcedOptions
51 changes: 25 additions & 26 deletions packages/ua-devtools-evm/src/oapp/sdk.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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(
Expand Down Expand Up @@ -71,42 +71,41 @@ export class OApp extends OmniSDK implements IOApp {
}
}

async getEnforcedOptions(eid: EndpointId, msgType: number): Promise<string> {
async getEnforcedOptions(eid: EndpointId, msgType: number): Promise<Bytes> {
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<OmniTransaction> {
async setEnforcedOptions(enforcedOptions: OAppEnforcedOptionParam[]): Promise<OmniTransaction> {
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
}
118 changes: 92 additions & 26 deletions packages/ua-devtools/src/oapp/config.ts
Original file line number Diff line number Diff line change
@@ -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<OmniTransaction[]>

export const configureOApp: OAppConfigurator = async (graph: OAppOmniGraph, createSdk: OAppFactory) =>
Expand Down Expand Up @@ -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<OmniTransaction[]> => {
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<OAppEnforcedOptionParam[]> = 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<ExecutorOptionType, Options>()
)
)

// 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<Map<OmniAddress, SetConfigParam[]>>,
Expand All @@ -260,3 +283,46 @@ const buildOmniTransactions = async (
}
return omniTransaction
}

const buildEnforcedOptionsOmniTransactions = async (
setEnforcedOptionsByEndpoint: OmniPointMap<OAppEnforcedOptionParam[]>,
createSdk: OAppFactory
): Promise<OmniTransaction[]> => {
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<ExecutorOptionType, Options>,
optionConfig: OAppEnforcedOption
): Map<ExecutorOptionType, Options> => {
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())
}
}
15 changes: 3 additions & 12 deletions packages/ua-devtools/src/oapp/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -29,13 +28,6 @@ export const OAppReceiveConfigSchema = z.object({
ulnConfig: Uln302UlnConfigSchema,
}) satisfies z.ZodSchema<OAppReceiveConfig, z.ZodTypeDef, unknown>

const ExecutorOptionTypeSchema = z.nativeEnum(ExecutorOptionType)

export const EncodedOptionSchema = z.object({
msgType: ExecutorOptionTypeSchema,
options: z.string(),
}) satisfies z.ZodSchema<EndcodedOption, z.ZodTypeDef, unknown>

export const ExecutorLzReceiveOptionSchema = z.object({
msgType: z.literal(ExecutorOptionType.LZ_RECEIVE),
gas: UIntNumberSchema,
Expand All @@ -60,15 +52,14 @@ export const ExecutorOrderedExecutionOptionSchema = z.object({
}) satisfies z.ZodSchema<ExecutorOrderedExecutionOption, z.ZodTypeDef, unknown>

export const OAppEnforcedOptionConfigSchema = z.union([
EncodedOptionSchema,
ExecutorLzReceiveOptionSchema,
ExecutorNativeDropOptionSchema,
ExecutorComposeOptionSchema,
ExecutorOrderedExecutionOptionSchema,
]) satisfies z.ZodSchema<OAppEnforcedOptionConfig, z.ZodTypeDef, unknown>
]) satisfies z.ZodSchema<OAppEnforcedOption, z.ZodTypeDef, unknown>

export const OAppEnforcedOptionsSchema = z.array(OAppEnforcedOptionConfigSchema) satisfies z.ZodSchema<
OAppEnforcedOptionConfig[],
OAppEnforcedOption[],
z.ZodTypeDef,
unknown
>
Expand Down
42 changes: 23 additions & 19 deletions packages/ua-devtools/src/oapp/types.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,25 @@
import type { EndpointId } from '@layerzerolabs/lz-definitions'
import type { IEndpoint, Timeout, Uln302ExecutorConfig, Uln302UlnConfig } from '@layerzerolabs/protocol-devtools'
import type {
Bytes,
Factory,
IOmniSDK,
OmniAddress,
OmniGraph,
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<IEndpoint>
getPeer(eid: EndpointId): Promise<OmniAddress | undefined>
hasPeer(eid: EndpointId, address: OmniAddress | null | undefined): Promise<boolean>
setPeer(eid: EndpointId, peer: OmniAddress | null | undefined): Promise<OmniTransaction>
getEnforcedOptions(eid: EndpointId, msgType: number): Promise<string>
setEnforcedOptions(enforcedOptions: EnforcedOptions[]): Promise<OmniTransaction>
encodeEnforcedOptions(enforcedOptionConfig: OAppEnforcedOptionConfig): Options
}

export type EnforcedOptions = {
eid: EndpointId
msgType: number
options: string
getEnforcedOptions(eid: EndpointId, msgType: number): Promise<Bytes>
setEnforcedOptions(enforcedOptions: OAppEnforcedOptionParam[]): Promise<OmniTransaction>
}

export interface OAppReceiveLibraryConfig {
Expand All @@ -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<unknown, OAppEdgeConfig | undefined>

export type OAppFactory<TOApp extends IOApp = IOApp, TOmniPoint = OmniPoint> = Factory<[TOmniPoint], TOApp>
Loading

0 comments on commit ce0c2f4

Please sign in to comment.