diff --git a/.changeset/tidy-forks-pull.md b/.changeset/tidy-forks-pull.md new file mode 100644 index 000000000..5afc3bd44 --- /dev/null +++ b/.changeset/tidy-forks-pull.md @@ -0,0 +1,7 @@ +--- +"@layerzerolabs/protocol-devtools-evm": patch +"@layerzerolabs/ua-devtools": patch +"@layerzerolabs/devtools": patch +--- + +Clean up schemas; Add WithLooseBigInts generic mapped type diff --git a/packages/devtools/src/types.ts b/packages/devtools/src/types.ts index 54c8f69e9..07eabe92a 100644 --- a/packages/devtools/src/types.ts +++ b/packages/devtools/src/types.ts @@ -56,3 +56,34 @@ type GetMandatoryKeys = { * into optional properties */ export type WithOptionals = Partial & Pick> + +/** + * Helper type for loosening the user configuration types + * when it comes to `bigint`s. + * + * It will recursively replace all the `bigint` types, + * preserving the types structure, with `PossiblyBigInt` type. + * + * ``` + * interface Config { + * values: bigint[] + * } + * + * type UserConfig = WithLooseBigInts + * + * const userConfig: UserConfig = { + * values: ["124", 124, BigInt(124)] + * } + * ``` + */ +export type WithLooseBigInts = T extends bigint + ? PossiblyBigInt + : T extends Set + ? Set + : T extends Map + ? Map, WithLooseBigInts> + : T extends { [K in keyof T]: T[K] } + ? { [K in keyof T]: WithLooseBigInts } + : T extends Promise + ? Promise> + : T diff --git a/packages/devtools/test/types.test.ts b/packages/devtools/test/types.test.ts new file mode 100644 index 000000000..f1b4b2805 --- /dev/null +++ b/packages/devtools/test/types.test.ts @@ -0,0 +1,76 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable jest/expect-expect */ +import { OmniPoint } from '@/omnigraph' +import { WithLooseBigInts } from '@/types' +import { EndpointId } from '@layerzerolabs/lz-definitions' + +describe('types', () => { + describe('WithLooseBigInts', () => { + const point: OmniPoint = { eid: EndpointId.ZORA_TESTNET, address: '' } + + it('should transform bigint into PossiblyBigint', () => { + type Test = WithLooseBigInts + + const str: Test = '' + const num: Test = 6 + const big: Test = BigInt(69) + + // @ts-expect-error objects should not pass + const obj: Test = {} + // @ts-expect-error arrays should not pass + const arr: Test = [] + // @ts-expect-error booleans should not pass + const bool: Test = false + // @ts-expect-error symbols should not pass + const sym: Test = Symbol() + }) + + it('should transform array', () => { + type Test = WithLooseBigInts + + const str: Test = ['', '1'] + const num: Test = [7, 15] + const big: Test = [BigInt(8), BigInt(87)] + const mixed: Test = ['', 6, BigInt(112)] + + // @ts-expect-error objects should not pass + const obj: Test = [{}] + // @ts-expect-error arrays should not pass + const arr: Test = [[]] + // @ts-expect-error booleans should not pass + const bool: Test = [false] + // @ts-expect-error symbols should not pass + const sym: Test = [Symbol()] + }) + + it('should transform tuple', () => { + type Test = WithLooseBigInts<[bigint, string, boolean]> + + const str: Test = ['', 'string', false] + const num: Test = [17, 'string', false] + const big: Test = [BigInt(50), 'string', true] + + // @ts-expect-error objects should not pass + const obj: Test = [{}, 'string', false] + // @ts-expect-error wrong tuple length should not pass + const short: Test = [87] + // @ts-expect-error wrong tuple types should not pass + const mistyped: Test = [87, 65, true] + }) + + it('should transform interface', () => { + type Test = WithLooseBigInts<{ big: bigint; str: string; arr: unknown[] }> + + const str: Test = { big: 'string', str: '', arr: [] } + const num: Test = { big: 6, str: '', arr: [] } + const big: Test = { big: BigInt(654), str: '', arr: [] } + + // @ts-expect-error objects should not pass + const obj: Test = { big: {}, str: '', arr: [] } + // @ts-expect-error arrays should not pass + const arr: Test = { big: [], str: '', arr: [] } + // @ts-expect-error wrong interface types should not pass + const mistyped: Test = { big: [], str: 6, arr: [] } + }) + }) +}) diff --git a/packages/protocol-devtools-evm/src/uln302/schema.ts b/packages/protocol-devtools-evm/src/uln302/schema.ts index ffb7bcae5..d3fef9b66 100644 --- a/packages/protocol-devtools-evm/src/uln302/schema.ts +++ b/packages/protocol-devtools-evm/src/uln302/schema.ts @@ -20,12 +20,3 @@ export const Uln302UlnConfigSchema = Uln302UlnConfigSchemaBase.extend({ export const Uln302ExecutorConfigSchema = Uln302ExecutorConfigSchemaBase.extend({ maxMessageSize: BigNumberishNumberSchema, }) satisfies z.ZodSchema - -/** - * Schema for parsing a common UlnConfig into a ethers-specific format - */ -export const Uln302UlnConfigInputSchema = Uln302UlnConfigSchema.transform((config) => ({ - ...config, - requiredDVNCount: config.requiredDVNs.length, - optionalDVNCount: config.optionalDVNs.length, -})) diff --git a/packages/protocol-devtools-evm/src/uln302/sdk.ts b/packages/protocol-devtools-evm/src/uln302/sdk.ts index afde12194..3a1c1a2d2 100644 --- a/packages/protocol-devtools-evm/src/uln302/sdk.ts +++ b/packages/protocol-devtools-evm/src/uln302/sdk.ts @@ -1,7 +1,7 @@ import type { EndpointId } from '@layerzerolabs/lz-definitions' import type { IUln302, Uln302ExecutorConfig, Uln302UlnConfig } from '@layerzerolabs/protocol-devtools' import { OmniAddress, formatEid, type OmniTransaction } from '@layerzerolabs/devtools' -import { Uln302ExecutorConfigSchema, Uln302UlnConfigInputSchema, Uln302UlnConfigSchema } from './schema' +import { Uln302ExecutorConfigSchema, Uln302UlnConfigSchema } from './schema' import assert from 'assert' import { printRecord } from '@layerzerolabs/io-devtools' import { isZero } from '@layerzerolabs/devtools' @@ -92,14 +92,14 @@ export class Uln302 extends OmniSDK implements IUln302 { } encodeUlnConfig(config: Uln302UlnConfig): string { - const serializedConfig = Uln302UlnConfigInputSchema.parse(config) + const serializedConfig = this.serializeUlnConfig(config) const encoded = this.contract.contract.interface.encodeFunctionResult('getUlnConfig', [serializedConfig]) return assert(typeof encoded === 'string', 'Must be a string'), encoded } async setDefaultUlnConfig(eid: EndpointId, config: Uln302UlnConfig): Promise { - const serializedConfig = Uln302UlnConfigInputSchema.parse(config) + const serializedConfig = this.serializeUlnConfig(config) const data = this.contract.contract.interface.encodeFunctionData('setDefaultUlnConfigs', [ [ { @@ -114,4 +114,21 @@ export class Uln302 extends OmniSDK implements IUln302 { description: `Setting default ULN config for ${formatEid(eid)}: ${printRecord(serializedConfig)}`, } } + + /** + * Prepares the ULN config to be sent to the contract + * + * This involves adding two properties that are required by the EVM + * contracts (for optimization purposes) but don't need to be present + * in our configuration. + * + * @param {Uln302UlnConfig} config + */ + protected serializeUlnConfig(config: Uln302UlnConfig) { + return { + ...config, + requiredDVNCount: config.requiredDVNs.length, + optionalDVNCount: config.optionalDVNs.length, + } + } } diff --git a/packages/ua-devtools/src/oapp/schema.ts b/packages/ua-devtools/src/oapp/schema.ts index 8203466f5..ff5b256a1 100644 --- a/packages/ua-devtools/src/oapp/schema.ts +++ b/packages/ua-devtools/src/oapp/schema.ts @@ -23,18 +23,24 @@ 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 OAppEnforcedOptionSchema = z.object({ + msgType: z.number(), + options: z.string(), +}) satisfies z.ZodSchema + +export const OAppEnforcedOptionsSchema = z.array(OAppEnforcedOptionSchema) satisfies z.ZodSchema< + OAppEnforcedOptionConfig[], + z.ZodTypeDef, + unknown +> -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 +export const OAppEdgeConfigSchema = z + .object({ + sendLibrary: AddressSchema, + receiveLibraryConfig: OAppReceiveLibraryConfigSchema, + receiveLibraryTimeoutConfig: TimeoutSchema, + sendConfig: OAppSendConfigSchema, + receiveConfig: OAppReceiveConfigSchema, + enforcedOptions: OAppEnforcedOptionsSchema, + }) + .partial() satisfies z.ZodSchema