Skip to content

Commit

Permalink
🪚 OmniGraph™ SDK factories; OApp ↔︎ Endpoint SDK link (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
janjakubnanista authored Dec 10, 2023
1 parent 086b820 commit 9e4cb71
Show file tree
Hide file tree
Showing 19 changed files with 194 additions and 47 deletions.
14 changes: 14 additions & 0 deletions packages/protocol-utils-evm/src/endpoint/factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import pMemoize from 'p-memoize'
import { OmniContractFactory } from '@layerzerolabs/utils-evm'
import { Endpoint } from './sdk'
import { EndpointFactory } from '@layerzerolabs/protocol-utils'

/**
* Syntactic sugar that creates an instance of EVM `Endpoint` SDK
* based on an `OmniPoint` with help of an `OmniContractFactory`
*
* @param {OmniContractFactory} contractFactory
* @returns {EndpointFactory<Endpoint>}
*/
export const createEndpointFactory = (contractFactory: OmniContractFactory): EndpointFactory<Endpoint> =>
pMemoize(async (point) => new Endpoint(await contractFactory(point)))
1 change: 1 addition & 0 deletions packages/protocol-utils-evm/src/endpoint/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './factory'
export * from './sdk'
4 changes: 2 additions & 2 deletions packages/protocol-utils-evm/src/endpoint/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ignoreZero, makeZeroAddress, omniContractToPoint, type OmniContract } f
export class Endpoint implements IEndpoint {
constructor(public readonly contract: OmniContract) {}

async defaultReceiveLibrary(eid: EndpointId): Promise<string | undefined> {
async getDefaultReceiveLibrary(eid: EndpointId): Promise<string | undefined> {
return ignoreZero(await this.contract.contract.defaultReceiveLibrary(eid))
}

Expand Down Expand Up @@ -35,7 +35,7 @@ export class Endpoint implements IEndpoint {
}
}

async defaultSendLibrary(eid: EndpointId): Promise<string | undefined> {
async getDefaultSendLibrary(eid: EndpointId): Promise<string | undefined> {
return ignoreZero(await this.contract.contract.defaultSendLibrary(eid))
}

Expand Down
4 changes: 2 additions & 2 deletions packages/protocol-utils/src/endpoint/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const configureEndpointDefaultReceiveLibraries: EndpointConfigurator = as
await Promise.all(
graph.connections.map(async ({ vector: { from, to }, config }): Promise<OmniTransaction[]> => {
const sdk = await createSdk(from)
const address = await sdk.defaultReceiveLibrary(to.eid)
const address = await sdk.getDefaultReceiveLibrary(to.eid)

// If the library is already set as default, do nothing
if (config.defaultReceiveLibrary === address) return []
Expand All @@ -40,7 +40,7 @@ export const configureEndpointDefaultSendLibraries: EndpointConfigurator = async
await Promise.all(
graph.connections.map(async ({ vector: { from, to }, config }): Promise<OmniTransaction[]> => {
const sdk = await createSdk(from)
const address = await sdk.defaultSendLibrary(to.eid)
const address = await sdk.getDefaultSendLibrary(to.eid)

// If the library is already set as default, do nothing
if (config.defaultSendLibrary === address) return []
Expand Down
6 changes: 3 additions & 3 deletions packages/protocol-utils/src/endpoint/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import type { Address, OmniGraph, OmniPointBasedFactory, OmniTransaction } from
import type { EndpointId } from '@layerzerolabs/lz-definitions'

export interface IEndpoint {
defaultReceiveLibrary(eid: EndpointId): Promise<Address | undefined>
getDefaultReceiveLibrary(eid: EndpointId): Promise<Address | undefined>
setDefaultReceiveLibrary(
eid: EndpointId,
lib: Address | null | undefined,
gracePeriod?: number
): Promise<OmniTransaction>

defaultSendLibrary(eid: EndpointId): Promise<Address | undefined>
getDefaultSendLibrary(eid: EndpointId): Promise<Address | undefined>
setDefaultSendLibrary(eid: EndpointId, lib: Address | null | undefined): Promise<OmniTransaction>

isRegisteredLibrary(lib: Address): Promise<boolean>
Expand All @@ -27,4 +27,4 @@ export interface EndpointEdgeConfig {

export type EndpointOmniGraph = OmniGraph<unknown, EndpointEdgeConfig>

export type EndpointFactory = OmniPointBasedFactory<IEndpoint>
export type EndpointFactory<TEndpoint extends IEndpoint = IEndpoint> = OmniPointBasedFactory<TEndpoint>
2 changes: 1 addition & 1 deletion packages/protocol-utils/src/uln302/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ export interface Uln302NodeConfig {

export type Uln302OmniGraph = OmniGraph<Uln302NodeConfig, unknown>

export type Uln302Factory = OmniPointBasedFactory<IUln302>
export type Uln302Factory<TUln302 extends IUln302 = IUln302> = OmniPointBasedFactory<TUln302>
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ describe('endpoint/config', () => {
const avaxEndpointSdk = await sdkFactory(avaxEndpointPoint)

// First let's check the send libraries
const ethDefaultSendLib = await ethEndpointSdk.defaultSendLibrary(avaxEndpointPoint.eid)
const avaxDefaultSendLib = await avaxEndpointSdk.defaultSendLibrary(ethEndpointPoint.eid)
const ethDefaultSendLib = await ethEndpointSdk.getDefaultSendLibrary(avaxEndpointPoint.eid)
const avaxDefaultSendLib = await avaxEndpointSdk.getDefaultSendLibrary(ethEndpointPoint.eid)

const ethSendUlnPoint = omniContractToPoint(await connectedContractFactory(ethSendUln))
const avaxSendUlnPoint = omniContractToPoint(await connectedContractFactory(avaxSendUln))
Expand All @@ -44,8 +44,8 @@ describe('endpoint/config', () => {
expect(avaxDefaultSendLib).toBe(avaxSendUlnPoint.address)

// Then let's check the receive libraries
const ethDefaultReceiveLib = await ethEndpointSdk.defaultReceiveLibrary(avaxEndpointPoint.eid)
const avaxDefaultReceiveLib = await avaxEndpointSdk.defaultReceiveLibrary(ethEndpointPoint.eid)
const ethDefaultReceiveLib = await ethEndpointSdk.getDefaultReceiveLibrary(avaxEndpointPoint.eid)
const avaxDefaultReceiveLib = await avaxEndpointSdk.getDefaultReceiveLibrary(ethEndpointPoint.eid)

const ethReceiveUlnPoint = omniContractToPoint(await connectedContractFactory(ethReceiveUln))
const avaxReceiveUlnPoint = omniContractToPoint(await connectedContractFactory(avaxReceiveUln))
Expand Down
15 changes: 4 additions & 11 deletions packages/ua-utils-evm-hardhat-test/test/oapp/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import 'hardhat'
import { configureOApp } from '@layerzerolabs/ua-utils'
import { OApp } from '@layerzerolabs/ua-utils-evm'
import { createOAppFactory } from '@layerzerolabs/ua-utils-evm'
import {
createConnectedContractFactory,
createContractFactory,
createSignerFactory,
OmniGraphBuilderHardhat,
} from '@layerzerolabs/utils-evm-hardhat'
import type { OmniGraphHardhat } from '@layerzerolabs/utils-evm-hardhat'
import type { OmniPoint } from '@layerzerolabs/utils'
import { omniContractToPoint } from '@layerzerolabs/utils-evm'
import { EndpointId } from '@layerzerolabs/lz-definitions'
import { setupDefaultEndpoint } from '../__utils__/endpoint'
Expand Down Expand Up @@ -49,9 +47,7 @@ describe('oapp/config', () => {
// This is the required tooling we need to set up
const contractFactory = createConnectedContractFactory()
const builder = await OmniGraphBuilderHardhat.fromConfig(config)

// This so far the only non-oneliner, a function that returns an SDK for a contract on a network
const sdkFactory = async (point: OmniPoint) => new OApp(await contractFactory(point))
const sdkFactory = createOAppFactory(contractFactory)

// This is where the configuration happens
const transactions = await configureOApp(builder.graph, sdkFactory)
Expand All @@ -71,12 +67,9 @@ describe('oapp/config', () => {

it('should exclude setPeer transactions for peers that have been set', async () => {
// This is the required tooling we need to set up
const contractFactory = createContractFactory()
const connectedContractFactory = createConnectedContractFactory(contractFactory)
const contractFactory = createConnectedContractFactory()
const builder = await OmniGraphBuilderHardhat.fromConfig(config)

// This so far the only non-oneliner, a function that returns an SDK for a contract on a network
const sdkFactory = async (point: OmniPoint) => new OApp(await connectedContractFactory(point))
const sdkFactory = createOAppFactory(contractFactory)

const ethPoint = omniContractToPoint(await contractFactory(ethContract))
const ethSdk = await sdkFactory(ethPoint)
Expand Down
5 changes: 5 additions & 0 deletions packages/ua-utils-evm-hardhat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
"lint": "npx eslint '**/*.{js,ts,json}'",
"test": "jest --passWithNoTests"
},
"dependencies": {
"p-memoize": "~4.0.1"
},
"devDependencies": {
"@ethersproject/contracts": "^5.7.0",
"@gnosis.pm/safe-core-sdk": "^2.0.0",
Expand All @@ -44,6 +47,7 @@
"@layerzerolabs/io-utils": "~0.0.1",
"@layerzerolabs/lz-definitions": "~1.5.70",
"@layerzerolabs/ua-utils": "~0.1.0",
"@layerzerolabs/ua-utils-evm": "~0.0.1",
"@layerzerolabs/utils": "~0.0.1",
"@layerzerolabs/utils-evm-hardhat": "~0.0.2",
"@types/jest": "^29.5.10",
Expand All @@ -69,6 +73,7 @@
"@layerzerolabs/io-utils": "~0.0.1",
"@layerzerolabs/lz-definitions": "~1.5.70",
"@layerzerolabs/ua-utils": "~0.1.0",
"@layerzerolabs/ua-utils-evm": "~0.0.1",
"@layerzerolabs/utils": "~0.0.1",
"@layerzerolabs/utils-evm-hardhat": "~0.0.2",
"ethers": "^5.5.2",
Expand Down
4 changes: 2 additions & 2 deletions packages/ua-utils-evm-hardhat/src/utils/taskHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export async function getSendConfig(
if (address) {
sendLibrary = await localEndpointSDK.getSendLibrary(address, remoteEid)
} else {
sendLibrary = await localEndpointSDK.defaultSendLibrary(remoteEid)
sendLibrary = await localEndpointSDK.getDefaultSendLibrary(remoteEid)
}

const localSendUlnSDK = new Uln302(
Expand All @@ -45,7 +45,7 @@ export async function getReceiveConfig(
if (address) {
receiveLibrary = (await localEndpointSDK.getReceiveLibrary(address, remoteEid))[0]
} else {
receiveLibrary = await localEndpointSDK.defaultReceiveLibrary(remoteEid)
receiveLibrary = await localEndpointSDK.getDefaultReceiveLibrary(remoteEid)
}

const localReceiveUlnSDK = new Uln302(
Expand Down
4 changes: 4 additions & 0 deletions packages/ua-utils-evm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"@ethersproject/constants": "^5.7.0",
"@ethersproject/contracts": "^5.7.0",
"@layerzerolabs/lz-definitions": "~1.5.70",
"@layerzerolabs/protocol-utils": "~0.0.1",
"@layerzerolabs/protocol-utils-evm": "~0.0.1",
"@layerzerolabs/test-utils": "~0.0.1",
"@layerzerolabs/ua-utils": "~0.1.0",
"@layerzerolabs/utils-evm": "~0.0.1",
Expand All @@ -48,6 +50,8 @@
},
"peerDependencies": {
"@layerzerolabs/lz-definitions": "~1.5.70",
"@layerzerolabs/protocol-utils": "~0.0.1",
"@layerzerolabs/protocol-utils-evm": "~0.0.1",
"@layerzerolabs/ua-utils": "~0.0.1",
"@layerzerolabs/utils-evm": "~0.0.1",
"zod": "^3.22.4"
Expand Down
20 changes: 20 additions & 0 deletions packages/ua-utils-evm/src/oapp/factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import pMemoize from 'p-memoize'
import type { OAppFactory } from '@layerzerolabs/ua-utils'
import type { OmniContractFactory } from '@layerzerolabs/utils-evm'
import type { EndpointFactory } from '@layerzerolabs/protocol-utils'
import { createEndpointFactory } from '@layerzerolabs/protocol-utils-evm'
import { OApp } from './sdk'

/**
* Syntactic sugar that creates an instance of EVM `OApp` SDK
* based on an `OmniPoint` with help of an `OmniContractFactory`
* and an (optional) `EndpointFactory`
*
* @param {OmniContractFactory} contractFactory
* @param {EndpointFactory} [endpointFactory]
* @returns {EndpointFactory<Endpoint>}
*/
export const createOAppFactory = (
contractFactory: OmniContractFactory,
endpointFactory: EndpointFactory = createEndpointFactory(contractFactory)
): OAppFactory<OApp> => pMemoize(async (point) => new OApp(await contractFactory(point), endpointFactory))
1 change: 1 addition & 0 deletions packages/ua-utils-evm/src/oapp/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './factory'
export * from './sdk'
43 changes: 39 additions & 4 deletions packages/ua-utils-evm/src/oapp/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,52 @@
import type { IOApp } from '@layerzerolabs/ua-utils'
import type { Bytes32, Address, OmniTransaction } from '@layerzerolabs/utils'
import { omniContractToPoint, OmniContract, ignoreZero, makeBytes32, areBytes32Equal } from '@layerzerolabs/utils-evm'
import {
omniContractToPoint,
OmniContract,
ignoreZero,
makeBytes32,
areBytes32Equal,
isZero,
formatOmniContract,
} from '@layerzerolabs/utils-evm'
import type { EndpointId } from '@layerzerolabs/lz-definitions'
import type { EndpointFactory, IEndpoint } from '@layerzerolabs/protocol-utils'

export class OApp implements IOApp {
constructor(public readonly contract: OmniContract) {}
constructor(
public readonly contract: OmniContract,
private readonly endpointFactory: EndpointFactory
) {}

async peers(eid: EndpointId): Promise<Bytes32 | undefined> {
async getEndpoint(): Promise<IEndpoint> {
let address: string

// First we'll need the endpoint address from the contract
try {
address = await this.contract.contract.endpoint()
} catch (error) {
// We'll just wrap the error in some nice words
throw new Error(`Failed to get endpoint address for OApp ${formatOmniContract(this.contract)}: ${error}`)
}

// We'll also do an additional check to see whether the endpoint has been set to a non-zero address
if (isZero(address)) {
throw new Error(
`Endpoint cannot be instantiated: Endpoint address has been set to a zero value for OApp ${formatOmniContract(
this.contract
)}`
)
}

return await this.endpointFactory({ address, eid: this.contract.eid })
}

async getPeer(eid: EndpointId): Promise<Bytes32 | undefined> {
return ignoreZero(await this.contract.contract.peers(eid))
}

async hasPeer(eid: EndpointId, address: Bytes32 | Address | null | undefined): Promise<boolean> {
return areBytes32Equal(await this.peers(eid), address)
return areBytes32Equal(await this.getPeer(eid), address)
}

async setPeer(eid: EndpointId, address: Bytes32 | Address | null | undefined): Promise<OmniTransaction> {
Expand Down
Loading

0 comments on commit 9e4cb71

Please sign in to comment.