Skip to content

Commit

Permalink
🪚 OmniGraph™ More flexible (a.k.a. flexibler) factory types (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
janjakubnanista authored Dec 12, 2023
1 parent 76e4c9a commit c9c1bdb
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 46 deletions.
8 changes: 5 additions & 3 deletions packages/protocol-utils-evm/src/endpoint/factory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pMemoize from 'p-memoize'
import type { EndpointFactory, Uln302Factory } from '@layerzerolabs/protocol-utils'
import type { OmniPoint } from '@layerzerolabs/utils'
import type { OmniContractFactory } from '@layerzerolabs/utils-evm'
import { Endpoint } from './sdk'
import { createUln302Factory } from '@/uln302/factory'
Expand All @@ -11,7 +12,8 @@ import { createUln302Factory } from '@/uln302/factory'
* @param {OmniContractFactory} contractFactory
* @returns {EndpointFactory<Endpoint>}
*/
export const createEndpointFactory = (
contractFactory: OmniContractFactory,
export const createEndpointFactory = <TOmniPoint = never>(
contractFactory: OmniContractFactory<TOmniPoint | OmniPoint>,
uln302Factory: Uln302Factory = createUln302Factory(contractFactory)
): EndpointFactory<Endpoint> => pMemoize(async (point) => new Endpoint(await contractFactory(point), uln302Factory))
): EndpointFactory<Endpoint, TOmniPoint | OmniPoint> =>
pMemoize(async (point) => new Endpoint(await contractFactory(point), uln302Factory))
5 changes: 3 additions & 2 deletions packages/protocol-utils-evm/src/uln302/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ import { Uln302 } from './sdk'
* @param {OmniContractFactory} contractFactory
* @returns {Uln302Factory<Uln302>}
*/
export const createUln302Factory = (contractFactory: OmniContractFactory): Uln302Factory<Uln302> =>
pMemoize(async (point) => new Uln302(await contractFactory(point)))
export const createUln302Factory = <TOmniPoint = never>(
contractFactory: OmniContractFactory<TOmniPoint>
): Uln302Factory<Uln302, TOmniPoint> => pMemoize(async (point) => new Uln302(await contractFactory(point)))
14 changes: 5 additions & 9 deletions packages/protocol-utils/src/endpoint/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import type {
Address,
OmniGraph,
OmniPointBasedFactory,
OmniTransaction,
IOmniSDK,
Bytes32,
} from '@layerzerolabs/utils'
import type { Address, Factory, OmniGraph, OmniPoint, OmniTransaction, IOmniSDK, Bytes32 } from '@layerzerolabs/utils'
import type { EndpointId } from '@layerzerolabs/lz-definitions'
import type { IUln302 } from '@/uln302/types'

Expand Down Expand Up @@ -40,4 +33,7 @@ export interface EndpointEdgeConfig {

export type EndpointOmniGraph = OmniGraph<unknown, EndpointEdgeConfig>

export type EndpointFactory<TEndpoint extends IEndpoint = IEndpoint> = OmniPointBasedFactory<TEndpoint>
export type EndpointFactory<TEndpoint extends IEndpoint = IEndpoint, TOmniPoint = OmniPoint> = Factory<
[TOmniPoint],
TEndpoint
>
4 changes: 2 additions & 2 deletions packages/protocol-utils/src/uln302/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Address, OmniGraph, OmniPointBasedFactory, OmniTransaction, IOmniSDK } from '@layerzerolabs/utils'
import type { Address, OmniGraph, Factory, OmniTransaction, IOmniSDK, OmniPoint } from '@layerzerolabs/utils'
import type { EndpointId } from '@layerzerolabs/lz-definitions'

export interface IUln302 extends IOmniSDK {
Expand Down Expand Up @@ -27,4 +27,4 @@ export interface Uln302NodeConfig {

export type Uln302OmniGraph = OmniGraph<Uln302NodeConfig, unknown>

export type Uln302Factory<TUln302 extends IUln302 = IUln302> = OmniPointBasedFactory<TUln302>
export type Uln302Factory<TUln302 extends IUln302 = IUln302, TOmniPoint = OmniPoint> = Factory<[TOmniPoint], TUln302>
28 changes: 10 additions & 18 deletions packages/ua-utils-evm-hardhat/src/utils/taskHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Address } from '@layerzerolabs/utils'
import { Uln302ExecutorConfig, Uln302UlnConfig } from '@layerzerolabs/protocol-utils'
import { createConnectedContractFactory, getEidForNetworkName } from '@layerzerolabs/utils-evm-hardhat'
import { Endpoint, Uln302, createUln302Factory } from '@layerzerolabs/protocol-utils-evm'
import { createEndpointFactory } from '@layerzerolabs/protocol-utils-evm'

export async function getSendConfig(
localNetworkName: string,
Expand All @@ -11,11 +11,9 @@ export async function getSendConfig(
const localEid = getEidForNetworkName(localNetworkName)
const remoteEid = getEidForNetworkName(remoteNetworkName)
const contractFactory = createConnectedContractFactory()
const uln302Factory = createUln302Factory(contractFactory)
const localEndpointSDK = new Endpoint(
await contractFactory({ eid: localEid, contractName: 'EndpointV2' }),
uln302Factory
)
const endpointFactory = createEndpointFactory(contractFactory)

const localEndpointSDK = await endpointFactory({ eid: localEid, contractName: 'EndpointV2' })

// First we get the SDK for the local send library
let sendLibrary: Address
Expand All @@ -25,12 +23,10 @@ export async function getSendConfig(
sendLibrary = await localEndpointSDK.getDefaultSendLibrary(remoteEid)
}

const localSendUlnSDK = new Uln302(
await contractFactory({ eid: localEid, contractName: 'SendUln302', address: sendLibrary })
)

const localSendUlnSDK = await localEndpointSDK.getUln302SDK(sendLibrary)
const sendUlnConfig = await localSendUlnSDK.getUlnConfig(remoteEid)
const sendExecutorConfig = await localSendUlnSDK.getExecutorConfig(remoteEid)

return [sendLibrary, sendUlnConfig, sendExecutorConfig]
}

Expand All @@ -42,11 +38,9 @@ export async function getReceiveConfig(
const localEid = getEidForNetworkName(localNetworkName)
const remoteEid = getEidForNetworkName(remoteNetworkName)
const contractFactory = createConnectedContractFactory()
const uln302Factory = createUln302Factory(contractFactory)
const localEndpointSDK = new Endpoint(
await contractFactory({ eid: localEid, contractName: 'EndpointV2' }),
uln302Factory
)
const endpointFactory = createEndpointFactory(contractFactory)

const localEndpointSDK = await endpointFactory({ eid: localEid, contractName: 'EndpointV2' })

// First we get the SDK for the local send library
let receiveLibrary: Address
Expand All @@ -56,9 +50,7 @@ export async function getReceiveConfig(
receiveLibrary = await localEndpointSDK.getDefaultReceiveLibrary(remoteEid)
}

const localReceiveUlnSDK = new Uln302(
await contractFactory({ eid: localEid, contractName: 'ReceiveUln302', address: receiveLibrary })
)
const localReceiveUlnSDK = await localEndpointSDK.getUln302SDK(receiveLibrary)

const receiveUlnConfig = await localReceiveUlnSDK.getUlnConfig(remoteEid)
return [receiveLibrary, receiveUlnConfig]
Expand Down
6 changes: 2 additions & 4 deletions packages/ua-utils/src/oapp/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type { EndpointId } from '@layerzerolabs/lz-definitions'
import type { IEndpoint } from '@layerzerolabs/protocol-utils'
import type { Address, OmniGraph, OmniTransaction, IOmniSDK } from '@layerzerolabs/utils'
import type { Bytes32 } from '@layerzerolabs/utils'
import type { OmniPointBasedFactory } from '@layerzerolabs/utils'
import type { Address, Bytes32, Factory, OmniGraph, OmniTransaction, IOmniSDK, OmniPoint } from '@layerzerolabs/utils'

export interface IOApp extends IOmniSDK {
getEndpointSDK(): Promise<IEndpoint>
Expand All @@ -13,4 +11,4 @@ export interface IOApp extends IOmniSDK {

export type OAppOmniGraph = OmniGraph<unknown, unknown>

export type OAppFactory<TOApp extends IOApp = IOApp> = OmniPointBasedFactory<TOApp>
export type OAppFactory<TOApp extends IOApp = IOApp, TOmniPoint = OmniPoint> = Factory<[TOmniPoint], TOApp>
4 changes: 2 additions & 2 deletions packages/utils-evm/src/omnigraph/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { Contract } from '@ethersproject/contracts'
import type { IOmniSDK as IOmniSDKAbstract, OmniPoint, WithEid } from '@layerzerolabs/utils'
import type { Factory, IOmniSDK as IOmniSDKAbstract, OmniPoint, WithEid } from '@layerzerolabs/utils'

export type OmniContract<TContract extends Contract = Contract> = WithEid<{
contract: TContract
}>

export type OmniContractFactory = (point: OmniPoint) => OmniContract | Promise<OmniContract>
export type OmniContractFactory<TOmniPoint = OmniPoint> = Factory<[TOmniPoint], OmniContract>

/**
* Base interface for all EVM SDKs, adding the EVM specific attributes
Expand Down
6 changes: 0 additions & 6 deletions packages/utils/src/omnigraph/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,6 @@ export interface OmniGraph<TNodeConfig = unknown, TEdgeConfig = unknown> {
connections: OmniEdge<TEdgeConfig>[]
}

/**
* OmniPointBasedFactory is a basis for all factories that can create objects
* based on OmniPoints - by their character these are typically contract or deployment factories
*/
export type OmniPointBasedFactory<TValue> = (point: OmniPoint) => TValue | Promise<TValue>

/**
* Helper type that adds eid property to an underlying type
*/
Expand Down
17 changes: 17 additions & 0 deletions packages/utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@ export type Address = string

export type Bytes32 = string

/**
* Generic type for a hybrid (sync / async) factory
* that generates an instance of `TOutput` based on arguments of type `TInput`
*
* `TInput` represents the list of all function arguments that need to be passed to the factory:
*
* ```typescript
* const mySyncFactory: Factory<[number, boolean], string> = (num: number, bool: boolean): string => "hello"
*
* const mySyncFactory: Factory<[], string> = async () => "hello"
* ```
*
* The hybrid aspect just makes it easier for implementers - if the logic is synchronous,
* this type will not force any extra `async`.
*/
export type Factory<TInput extends unknown[], TOutput> = (...input: TInput) => TOutput | Promise<TOutput>

export type EndpointBasedFactory<TValue> = (eid: EndpointId) => TValue | Promise<TValue>

/**
Expand Down
31 changes: 31 additions & 0 deletions packages/utils/test/omnigraph/types.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { OmniEdge, OmniNode, OmniPoint, OmniVector } from '@/omnigraph'
import { Factory } from '@/types'
import { EndpointId } from '@layerzerolabs/lz-definitions'

describe('schema/types', () => {
Expand Down Expand Up @@ -120,4 +121,34 @@ describe('schema/types', () => {
const anotherNode: OmniEdge<number> = { vector: edge.vector, config: edge.config }
})
})

describe('Factory', () => {
it('should not allow a factory that changes input types', () => {
// @ts-expect-error The input of the factory is a string, not a boolean
const factory: Factory<[boolean], boolean> = (a: string): boolean => !!a
})

it('should not allow a factory that changes output types', () => {
// @ts-expect-error The output of the factory is a string, not a boolean
const factory: Factory<[boolean], boolean> = (a: string): string => a
})

it('should not allow a factory that changes input types', () => {
// @ts-expect-error The input of the factory is a string, not a boolean
const factory: Factory<[boolean], boolean> = async (a: string): boolean => !!a
})

it('should not allow a factory that changes output types', () => {
// @ts-expect-error The output of the factory is a string, not a boolean
const factory: Factory<[boolean], boolean> = async (a: string): string => a
})

it('should allow a sync factory', () => {
const factory: Factory<[boolean], boolean> = (a: boolean) => a
})

it('should allow an sync factory', () => {
const factory: Factory<[boolean], boolean> = async (a: boolean) => a
})
})
})

0 comments on commit c9c1bdb

Please sign in to comment.