Skip to content

Commit

Permalink
🪚 OmniGraph™ Adding access to Uln302 SDK to Endpoint SDK (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
janjakubnanista authored Dec 11, 2023
1 parent 5fd7196 commit 2f6659a
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 25 deletions.
11 changes: 7 additions & 4 deletions packages/protocol-utils-evm/src/endpoint/factory.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import pMemoize from 'p-memoize'
import { OmniContractFactory } from '@layerzerolabs/utils-evm'
import type { EndpointFactory, Uln302Factory } from '@layerzerolabs/protocol-utils'
import type { OmniContractFactory } from '@layerzerolabs/utils-evm'
import { Endpoint } from './sdk'
import { EndpointFactory } from '@layerzerolabs/protocol-utils'
import { createUln302Factory } from '@/uln302/factory'

/**
* Syntactic sugar that creates an instance of EVM `Endpoint` SDK
Expand All @@ -10,5 +11,7 @@ import { EndpointFactory } from '@layerzerolabs/protocol-utils'
* @param {OmniContractFactory} contractFactory
* @returns {EndpointFactory<Endpoint>}
*/
export const createEndpointFactory = (contractFactory: OmniContractFactory): EndpointFactory<Endpoint> =>
pMemoize(async (point) => new Endpoint(await contractFactory(point)))
export const createEndpointFactory = (
contractFactory: OmniContractFactory,
uln302Factory: Uln302Factory = createUln302Factory(contractFactory)
): EndpointFactory<Endpoint> => pMemoize(async (point) => new Endpoint(await contractFactory(point), uln302Factory))
25 changes: 22 additions & 3 deletions packages/protocol-utils-evm/src/endpoint/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
import type { IEndpoint } from '@layerzerolabs/protocol-utils'
import { formatEid, type Address, type OmniTransaction } from '@layerzerolabs/utils'
import assert from 'assert'
import type { IEndpoint, IUln302, Uln302Factory } from '@layerzerolabs/protocol-utils'
import { formatEid, type Address, type OmniTransaction, formatOmniPoint } from '@layerzerolabs/utils'
import type { EndpointId } from '@layerzerolabs/lz-definitions'
import { ignoreZero, makeZeroAddress, OmniSDK } from '@layerzerolabs/utils-evm'
import { ignoreZero, isZero, makeZeroAddress, type OmniContract, OmniSDK } from '@layerzerolabs/utils-evm'

export class Endpoint extends OmniSDK implements IEndpoint {
constructor(
contract: OmniContract,
private readonly uln302Factory: Uln302Factory
) {
super(contract)
}

async getUln302SDK(address: Address): Promise<IUln302> {
assert(
!isZero(address),
`Uln302 cannot be instantiated: Uln302 address cannot be a zero value for Endpoint ${formatOmniPoint(
this.point
)}`
)

return await this.uln302Factory({ eid: this.point.eid, address })
}

async getDefaultReceiveLibrary(eid: EndpointId): Promise<Address | undefined> {
return ignoreZero(await this.contract.contract.defaultReceiveLibrary(eid))
}
Expand Down
14 changes: 14 additions & 0 deletions packages/protocol-utils-evm/src/uln302/factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import pMemoize from 'p-memoize'
import type { OmniContractFactory } from '@layerzerolabs/utils-evm'
import type { Uln302Factory } from '@layerzerolabs/protocol-utils'
import { Uln302 } from './sdk'

/**
* Syntactic sugar that creates an instance of EVM `Uln302` SDK
* based on an `OmniPoint` with help of an `OmniContractFactory`
*
* @param {OmniContractFactory} contractFactory
* @returns {Uln302Factory<Uln302>}
*/
export const createUln302Factory = (contractFactory: OmniContractFactory): Uln302Factory<Uln302> =>
pMemoize(async (point) => new Uln302(await contractFactory(point)))
1 change: 1 addition & 0 deletions packages/protocol-utils-evm/src/uln302/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './factory'
export * from './sdk'
export * from './types'
65 changes: 65 additions & 0 deletions packages/protocol-utils-evm/test/endpoint/sdk.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import fc from 'fast-check'
import { endpointArbitrary, evmAddressArbitrary } from '@layerzerolabs/test-utils'
import type { Contract } from '@ethersproject/contracts'
import { isZero, makeBytes32, makeZeroAddress, type OmniContract } from '@layerzerolabs/utils-evm'
import { Endpoint } from '@/endpoint'

describe('endpoint/sdk', () => {
describe('getUln302SDK', () => {
const zeroishAddressArbitrary = fc.constantFrom(makeZeroAddress(), makeBytes32())
const jestFunctionArbitrary = fc.anything().map(() => jest.fn())
const oappOmniContractArbitrary = fc.record({
address: evmAddressArbitrary,
peers: jestFunctionArbitrary,
endpoint: jestFunctionArbitrary,
interface: fc.record({
encodeFunctionData: jestFunctionArbitrary,
}),
}) as fc.Arbitrary<unknown> as fc.Arbitrary<Contract>

const omniContractArbitrary: fc.Arbitrary<OmniContract> = fc.record({
eid: endpointArbitrary,
contract: oappOmniContractArbitrary,
})

const uln302Factory = jest.fn().mockRejectedValue('No endpoint')

it('should reject if the address is a zeroish address', async () => {
await fc.assert(
fc.asyncProperty(omniContractArbitrary, zeroishAddressArbitrary, async (omniContract, address) => {
const sdk = new Endpoint(omniContract, uln302Factory)

await expect(sdk.getUln302SDK(address)).rejects.toThrow(
/Uln302 cannot be instantiated: Uln302 address cannot be a zero value for Endpoint/
)
})
)
})

it('should call the uln302Factory if the address is a non-zeroish address', async () => {
await fc.assert(
fc.asyncProperty(
omniContractArbitrary,
evmAddressArbitrary,
fc.anything(),
async (omniContract, address, uln302Sdk) => {
fc.pre(!isZero(address))

uln302Factory.mockReset().mockResolvedValue(uln302Sdk)

const sdk = new Endpoint(omniContract, uln302Factory)
const uln302 = await sdk.getUln302SDK(address)

expect(uln302).toBe(uln302Sdk)

expect(uln302Factory).toHaveBeenCalledTimes(1)
expect(uln302Factory).toHaveBeenCalledWith({
eid: omniContract.eid,
address,
})
}
)
)
})
})
})
3 changes: 3 additions & 0 deletions packages/protocol-utils/src/endpoint/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import type {
Bytes32,
} from '@layerzerolabs/utils'
import type { EndpointId } from '@layerzerolabs/lz-definitions'
import type { IUln302 } from '@/uln302/types'

export interface IEndpoint extends IOmniSDK {
getUln302SDK(address: Address): Promise<IUln302>

getDefaultReceiveLibrary(eid: EndpointId): Promise<Address | undefined>
setDefaultReceiveLibrary(
eid: EndpointId,
Expand Down
8 changes: 3 additions & 5 deletions packages/ua-utils-evm-hardhat-test/test/__utils__/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ import { omniContractToPoint } from '@layerzerolabs/utils-evm'
import {
configureEndpoint,
EndpointEdgeConfig,
EndpointFactory,
Uln302NodeConfig,
Uln302ExecutorConfig,
configureUln302,
Uln302Factory,
Uln302UlnConfig,
} from '@layerzerolabs/protocol-utils'
import { Endpoint, Uln302 } from '@layerzerolabs/protocol-utils-evm'
import { createEndpointFactory, createUln302Factory } from '@layerzerolabs/protocol-utils-evm'
import { formatOmniPoint } from '@layerzerolabs/utils'

export const ethEndpoint = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'EndpointV2' }
Expand Down Expand Up @@ -76,8 +74,8 @@ export const setupDefaultEndpoint = async (): Promise<void> => {
const environmentFactory = createNetworkEnvironmentFactory()
const contractFactory = createConnectedContractFactory()
const signerFactory = createSignerFactory()
const endpointSdkFactory: EndpointFactory = async (point) => new Endpoint(await contractFactory(point))
const ulnSdkFactory: Uln302Factory = async (point) => new Uln302(await contractFactory(point))
const ulnSdkFactory = createUln302Factory(contractFactory)
const endpointSdkFactory = createEndpointFactory(contractFactory, ulnSdkFactory)

// First we deploy the endpoint
await deploy(await environmentFactory(EndpointId.ETHEREUM_MAINNET))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import 'hardhat'
import { createConnectedContractFactory } from '@layerzerolabs/utils-evm-hardhat'
import type { OmniPoint } from '@layerzerolabs/utils'
import { omniContractToPoint } from '@layerzerolabs/utils-evm'
import { EndpointId } from '@layerzerolabs/lz-definitions'
import { getDefaultUlnConfig, setupDefaultEndpoint } from '../__utils__/endpoint'
import { Endpoint, Uln302 } from '@layerzerolabs/protocol-utils-evm'
import { createEndpointFactory, createUln302Factory } from '@layerzerolabs/protocol-utils-evm'

describe('endpoint/config', () => {
const ethEndpoint = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'EndpointV2' }
Expand All @@ -24,7 +23,7 @@ describe('endpoint/config', () => {
it('should have default libraries configured', async () => {
// This is the required tooling we need to set up
const connectedContractFactory = createConnectedContractFactory()
const sdkFactory = async (point: OmniPoint) => new Endpoint(await connectedContractFactory(point))
const sdkFactory = createEndpointFactory(connectedContractFactory)

// Now for the purposes of the test, we need to get coordinates of our contracts
const ethEndpointPoint = omniContractToPoint(await connectedContractFactory(ethEndpoint))
Expand Down Expand Up @@ -59,7 +58,7 @@ describe('endpoint/config', () => {
it('should have default executors configured', async () => {
// This is the required tooling we need to set up
const connectedContractFactory = createConnectedContractFactory()
const sdkFactory = async (point: OmniPoint) => new Uln302(await connectedContractFactory(point))
const sdkFactory = createUln302Factory(connectedContractFactory)

const ethSendUlnPoint = omniContractToPoint(await connectedContractFactory(ethSendUln))
const avaxSendUlnPoint = omniContractToPoint(await connectedContractFactory(avaxSendUln))
Expand Down
14 changes: 11 additions & 3 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 } from '@layerzerolabs/protocol-utils-evm'
import { Endpoint, Uln302, createUln302Factory } from '@layerzerolabs/protocol-utils-evm'

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

// First we get the SDK for the local send library
let sendLibrary: Address
Expand All @@ -38,7 +42,11 @@ export async function getReceiveConfig(
const localEid = getEidForNetworkName(localNetworkName)
const remoteEid = getEidForNetworkName(remoteNetworkName)
const contractFactory = createConnectedContractFactory()
const localEndpointSDK = new Endpoint(await contractFactory({ eid: localEid, contractName: 'EndpointV2' }))
const uln302Factory = createUln302Factory(contractFactory)
const localEndpointSDK = new Endpoint(
await contractFactory({ eid: localEid, contractName: 'EndpointV2' }),
uln302Factory
)

// First we get the SDK for the local send library
let receiveLibrary: Address
Expand Down
2 changes: 1 addition & 1 deletion packages/ua-utils-evm/src/oapp/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class OApp extends OmniSDK implements IOApp {
super(contract)
}

async getEndpoint(): Promise<IEndpoint> {
async getEndpointSDK(): Promise<IEndpoint> {
let address: string

// First we'll need the endpoint address from the contract
Expand Down
8 changes: 4 additions & 4 deletions packages/ua-utils-evm/test/oapp/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,15 +228,15 @@ describe('oapp/sdk', () => {
})
})

describe('getEndpoint', () => {
describe('getEndpointSDK', () => {
it('should reject if the call to endpoint() rejects', async () => {
await fc.assert(
fc.asyncProperty(omniContractArbitrary, async (omniContract) => {
omniContract.contract.endpoint.mockRejectedValue('No you did not')

const sdk = new OApp(omniContract, endpointFactory)

await expect(sdk.getEndpoint()).rejects.toThrow(/Failed to get endpoint address for OApp/)
await expect(sdk.getEndpointSDK()).rejects.toThrow(/Failed to get endpoint address for OApp/)
})
)
})
Expand All @@ -251,7 +251,7 @@ describe('oapp/sdk', () => {

const sdk = new OApp(omniContract, endpointFactory)

await expect(sdk.getEndpoint()).rejects.toThrow(
await expect(sdk.getEndpointSDK()).rejects.toThrow(
/Endpoint cannot be instantiated: Endpoint address has been set to a zero value for OApp/
)
}
Expand All @@ -273,7 +273,7 @@ describe('oapp/sdk', () => {
endpointFactory.mockReset().mockResolvedValue(endpointSdk)

const sdk = new OApp(omniContract, endpointFactory)
const endpoint = await sdk.getEndpoint()
const endpoint = await sdk.getEndpointSDK()

expect(endpoint).toBe(endpointSdk)

Expand Down
2 changes: 1 addition & 1 deletion packages/ua-utils/src/oapp/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { Bytes32 } from '@layerzerolabs/utils'
import type { OmniPointBasedFactory } from '@layerzerolabs/utils'

export interface IOApp extends IOmniSDK {
getEndpoint(): Promise<IEndpoint>
getEndpointSDK(): Promise<IEndpoint>
getPeer(eid: EndpointId): Promise<Bytes32 | undefined>
hasPeer(eid: EndpointId, address: Bytes32 | Address | null | undefined): Promise<boolean>
setPeer(eid: EndpointId, peer: Bytes32 | Address | null | undefined): Promise<OmniTransaction>
Expand Down

0 comments on commit 2f6659a

Please sign in to comment.