Skip to content

Commit

Permalink
🧹 Cleanup schemas & add generic loosening type for bigints (#305)
Browse files Browse the repository at this point in the history
  • Loading branch information
janjakubnanista authored Jan 30, 2024
1 parent 7882a45 commit 9b4256a
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 26 deletions.
7 changes: 7 additions & 0 deletions .changeset/tidy-forks-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@layerzerolabs/protocol-devtools-evm": patch
"@layerzerolabs/ua-devtools": patch
"@layerzerolabs/devtools": patch
---

Clean up schemas; Add WithLooseBigInts generic mapped type
31 changes: 31 additions & 0 deletions packages/devtools/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,34 @@ type GetMandatoryKeys<T> = {
* into optional properties
*/
export type WithOptionals<T> = Partial<T> & Pick<T, GetMandatoryKeys<T>>

/**
* 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<Config>
*
* const userConfig: UserConfig = {
* values: ["124", 124, BigInt(124)]
* }
* ```
*/
export type WithLooseBigInts<T> = T extends bigint
? PossiblyBigInt
: T extends Set<bigint>
? Set<PossiblyBigInt>
: T extends Map<infer K, infer V>
? Map<WithLooseBigInts<K>, WithLooseBigInts<V>>
: T extends { [K in keyof T]: T[K] }
? { [K in keyof T]: WithLooseBigInts<T[K]> }
: T extends Promise<infer V>
? Promise<WithLooseBigInts<V>>
: T
76 changes: 76 additions & 0 deletions packages/devtools/test/types.test.ts
Original file line number Diff line number Diff line change
@@ -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<bigint>

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<bigint[]>

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: [] }
})
})
})
9 changes: 0 additions & 9 deletions packages/protocol-devtools-evm/src/uln302/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,3 @@ export const Uln302UlnConfigSchema = Uln302UlnConfigSchemaBase.extend({
export const Uln302ExecutorConfigSchema = Uln302ExecutorConfigSchemaBase.extend({
maxMessageSize: BigNumberishNumberSchema,
}) satisfies z.ZodSchema<Uln302ExecutorConfig, z.ZodTypeDef, Uln302ExecutorConfigInput>

/**
* 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,
}))
23 changes: 20 additions & 3 deletions packages/protocol-devtools-evm/src/uln302/sdk.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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<OmniTransaction> {
const serializedConfig = Uln302UlnConfigInputSchema.parse(config)
const serializedConfig = this.serializeUlnConfig(config)
const data = this.contract.contract.interface.encodeFunctionData('setDefaultUlnConfigs', [
[
{
Expand All @@ -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,
}
}
}
34 changes: 20 additions & 14 deletions packages/ua-devtools/src/oapp/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,24 @@ export const OAppReceiveConfigSchema = z.object({
ulnConfig: Uln302UlnConfigSchema,
}) satisfies z.ZodSchema<OAppReceiveConfig, z.ZodTypeDef, unknown>

export const OAppEnforcedOptionsSchema = z.array(
z.object({
msgType: z.number(),
options: z.string(),
})
) satisfies z.ZodSchema<OAppEnforcedOptionConfig[], z.ZodTypeDef, unknown>
export const OAppEnforcedOptionSchema = z.object({
msgType: z.number(),
options: z.string(),
}) satisfies z.ZodSchema<OAppEnforcedOptionConfig, z.ZodTypeDef, unknown>

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<OAppEdgeConfig, z.ZodTypeDef, unknown>
export const OAppEdgeConfigSchema = z
.object({
sendLibrary: AddressSchema,
receiveLibraryConfig: OAppReceiveLibraryConfigSchema,
receiveLibraryTimeoutConfig: TimeoutSchema,
sendConfig: OAppSendConfigSchema,
receiveConfig: OAppReceiveConfigSchema,
enforcedOptions: OAppEnforcedOptionsSchema,
})
.partial() satisfies z.ZodSchema<OAppEdgeConfig, z.ZodTypeDef, unknown>

0 comments on commit 9b4256a

Please sign in to comment.