Skip to content

Commit

Permalink
chore: Add utilities for configuring Endpoints & ULNs
Browse files Browse the repository at this point in the history
  • Loading branch information
janjakubnanista committed Dec 5, 2023
1 parent a62b825 commit 9f6a998
Show file tree
Hide file tree
Showing 14 changed files with 483 additions and 156 deletions.
171 changes: 171 additions & 0 deletions packages/ua-utils-evm-hardhat-test/test/__utils__/endpoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import {
createConnectedContractFactory,
createLogger,
createNetworkEnvironmentFactory,
createSignerFactory,
OmniGraphBuilderHardhat,
type OmniGraphHardhat,
} from '@layerzerolabs/utils-evm-hardhat'
import deploy from '../../deploy/001_bootstrap'
import { EndpointId } from '@layerzerolabs/lz-definitions'
import { Endpoint, omniContractToPoint, Uln302 } from '@layerzerolabs/utils-evm'
import {
configureEndpoint,
EndpointEdgeConfig,
EndpointFactory,
Uln302NodeConfig,
formatOmniPoint,
Uln302ExecutorConfig,
configureUln302,
Uln302Factory,
Uln302UlnConfig,
} from '@layerzerolabs/utils'

export const ethEndpoint = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'EndpointV2' }
export const ethReceiveUln = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'ReceiveUln302' }
export const ethSendUln = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'SendUln302' }
export const avaxEndpoint = { eid: EndpointId.AVALANCHE_MAINNET, contractName: 'EndpointV2' }
export const avaxReceiveUln = { eid: EndpointId.AVALANCHE_MAINNET, contractName: 'ReceiveUln302' }
export const avaxSendUln = { eid: EndpointId.AVALANCHE_MAINNET, contractName: 'SendUln302' }

export const defaultExecutorConfig: Uln302ExecutorConfig = {
maxMessageSize: 1024,
executor: '0x0000000000000000000000000000000000000001',
}

export const defaultUlnConfig: Uln302UlnConfig = {
confirmations: 1,
requiredDVNs: ['0x0000000000000000000000000000000000000002', '0x0000000000000000000000000000000000000003'],
optionalDVNs: [],
optionalDVNThreshold: 0,
}

/**
* Helper function that deploys a fresh endpoint infrastructure:
*
* - EndpointV2
* - ReceiveUln302
* - SendUln302
*
* After deploying, it will wire up the elements with minimal configuration
*/
export const setupDefaultEndpoint = async (): Promise<void> => {
// This is the tooling we are going to need
const logger = createLogger()
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))

// First we deploy the endpoint
await deploy(await environmentFactory(EndpointId.ETHEREUM_MAINNET))
await deploy(await environmentFactory(EndpointId.AVALANCHE_MAINNET))

// For the graphs, we'll also need the pointers to the contracts
const ethSendUlnPoint = omniContractToPoint(await contractFactory(ethSendUln))
const avaxSendUlnPoint = omniContractToPoint(await contractFactory(avaxSendUln))
const ethReceiveUlnPoint = omniContractToPoint(await contractFactory(ethReceiveUln))
const avaxReceiveUlnPoint = omniContractToPoint(await contractFactory(avaxReceiveUln))

// This is the graph for SendUln302
const sendUlnConfig: OmniGraphHardhat<Uln302NodeConfig, unknown> = {
contracts: [
{
contract: ethSendUln,
config: {
defaultUlnConfigs: [[EndpointId.AVALANCHE_MAINNET, defaultUlnConfig]],
defaultExecutorConfigs: [[EndpointId.AVALANCHE_MAINNET, defaultExecutorConfig]],
},
},
{
contract: avaxSendUln,
config: {
defaultUlnConfigs: [[EndpointId.ETHEREUM_MAINNET, defaultUlnConfig]],
defaultExecutorConfigs: [[EndpointId.ETHEREUM_MAINNET, defaultExecutorConfig]],
},
},
],
connections: [],
}

// This is the graph for ReceiveUln302
const receiveUlnConfig: OmniGraphHardhat<Uln302NodeConfig, unknown> = {
contracts: [
{
contract: ethReceiveUln,
config: {
defaultUlnConfigs: [[EndpointId.AVALANCHE_MAINNET, defaultUlnConfig]],
defaultExecutorConfigs: [],
},
},
{
contract: avaxReceiveUln,
config: {
defaultUlnConfigs: [[EndpointId.ETHEREUM_MAINNET, defaultUlnConfig]],
defaultExecutorConfigs: [],
},
},
],
connections: [],
}

// This is the graph for EndpointV2
const config: OmniGraphHardhat<unknown, EndpointEdgeConfig> = {
contracts: [
{
contract: ethEndpoint,
config: undefined,
},
{
contract: avaxEndpoint,
config: undefined,
},
],
connections: [
{
from: ethEndpoint,
to: avaxEndpoint,
config: {
defaultReceiveLibrary: ethReceiveUlnPoint.address,
defaultSendLibrary: ethSendUlnPoint.address,
},
},
{
from: avaxEndpoint,
to: ethEndpoint,
config: {
defaultReceiveLibrary: avaxReceiveUlnPoint.address,
defaultSendLibrary: avaxSendUlnPoint.address,
},
},
],
}

// Now we compile a list of all the transactions that need to be executed for the ULNs and Endpoints
const builderEndpoint = await OmniGraphBuilderHardhat.fromConfig(config, contractFactory)
const endpointTransactions = await configureEndpoint(builderEndpoint.graph, endpointSdkFactory)
const builderSendUln = await OmniGraphBuilderHardhat.fromConfig(sendUlnConfig, contractFactory)
const sendUlnTransactions = await configureUln302(builderSendUln.graph, ulnSdkFactory)
const builderReceiveUln = await OmniGraphBuilderHardhat.fromConfig(receiveUlnConfig, contractFactory)
const receiveUlnTransactions = await configureUln302(builderReceiveUln.graph, ulnSdkFactory)

const transactions = [...sendUlnTransactions, ...receiveUlnTransactions, ...endpointTransactions]

logger.info(`Executing ${transactions.length} transactions`)

for (const transaction of transactions) {
const signer = await signerFactory(transaction.point.eid)
const description = transaction.description ?? '[no description]'

logger.info(`${formatOmniPoint(transaction.point)}: ${description}`)

const response = await signer.signAndSend(transaction)
logger.info(`${formatOmniPoint(transaction.point)}: ${description}: ${response.transactionHash}`)

const receipt = await response.wait()
logger.info(`${formatOmniPoint(transaction.point)}: ${description}: ${receipt.transactionHash}`)
}

logger.info(`Done configuring endpoint`)
}
198 changes: 63 additions & 135 deletions packages/ua-utils-evm-hardhat-test/test/endpoint/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,148 +1,76 @@
import { EndpointEdgeConfig, configureEndpoint } from '@layerzerolabs/utils'
import { Endpoint } from '@layerzerolabs/utils-evm'
import {
createContractFactory,
OmniGraphHardhat,
OmniGraphBuilderHardhat,
createConnectedContractFactory,
} from '@layerzerolabs/utils-evm-hardhat'
import { Endpoint, Uln302 } from '@layerzerolabs/utils-evm'
import { createConnectedContractFactory } from '@layerzerolabs/utils-evm-hardhat'
import type { OmniPoint } from '@layerzerolabs/utils'
import { omniContractToPoint } from '@layerzerolabs/utils-evm'
import { createSignerFactory } from '@layerzerolabs/utils-evm-hardhat'
import { expect } from 'chai'
import { describe } from 'mocha'
import { EndpointId } from '@layerzerolabs/lz-definitions'
import { defaultUlnConfig, setupDefaultEndpoint } from '../__utils__/endpoint'

describe('endpoint/config', () => {
const ethEndpoint = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'EndpointV2' }
const ethUln = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'SendUln302' }
const ethReceiveUln = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'ReceiveUln302' }
const ethSendUln = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'SendUln302' }
const avaxEndpoint = { eid: EndpointId.AVALANCHE_MAINNET, contractName: 'EndpointV2' }
const avaxUln = { eid: EndpointId.AVALANCHE_MAINNET, contractName: 'SendUln302' }

it('should return all setDefaultSendLibrary transactions', async () => {
// This is the required tooling we need to set up
const contractFactory = createContractFactory()
const connectedContractFactory = createConnectedContractFactory(contractFactory)
const sdkFactory = async (point: OmniPoint) => new Endpoint(await connectedContractFactory(point))

// At this point we need a config
//
// Since the config values now depend on contracts deployed in the bootstrap,
// the config creation is a bit more involved
const ethUlnPoint = omniContractToPoint(await contractFactory(ethUln))
const avaxUlnPoint = omniContractToPoint(await contractFactory(avaxUln))
const config: OmniGraphHardhat<unknown, EndpointEdgeConfig> = {
contracts: [
{
contract: ethEndpoint,
config: undefined,
},
{
contract: avaxEndpoint,
config: undefined,
},
],
connections: [
{
from: ethEndpoint,
to: avaxEndpoint,
config: {
defaultSendLibrary: ethUlnPoint.address,
},
},
{
from: avaxEndpoint,
to: ethEndpoint,
config: {
defaultSendLibrary: avaxUlnPoint.address,
},
},
],
}

const builder = await OmniGraphBuilderHardhat.fromConfig(config, contractFactory)

// This is where the configuration happens
const transactions = await configureEndpoint(builder.graph, sdkFactory)

// And finally the test assertions
const ethEndpointPoint = omniContractToPoint(await contractFactory(ethEndpoint))
const ethEndpointSdk = await sdkFactory(ethEndpointPoint)

const avaxEndpointPoint = omniContractToPoint(await contractFactory(avaxEndpoint))
const avaxEndpointSdk = await sdkFactory(avaxEndpointPoint)

expect(transactions).to.eql([
await ethEndpointSdk.setDefaultSendLibrary(avaxUlnPoint.eid, avaxUlnPoint.address),
await avaxEndpointSdk.setDefaultSendLibrary(ethUlnPoint.eid, ethUlnPoint.address),
])
const avaxReceiveUln = { eid: EndpointId.AVALANCHE_MAINNET, contractName: 'ReceiveUln302' }
const avaxSendUln = { eid: EndpointId.AVALANCHE_MAINNET, contractName: 'SendUln302' }

beforeEach(async () => {
await setupDefaultEndpoint()
})

it('should exclude setDefaultSendLibrary transactions for libraries that have been set', async () => {
// This is the required tooling we need to set up
// This is the required tooling we need to set up
const contractFactory = createContractFactory()
const connectedContractFactory = createConnectedContractFactory(contractFactory)
const sdkFactory = async (point: OmniPoint) => new Endpoint(await connectedContractFactory(point))

// At this point we need a config
//
// Since the config values now depend on contracts deployed in the bootstrap,
// the config creation is a bit more involved
const ethUlnPoint = omniContractToPoint(await contractFactory(ethUln))
const avaxUlnPoint = omniContractToPoint(await contractFactory(avaxUln))
const config: OmniGraphHardhat<unknown, EndpointEdgeConfig> = {
contracts: [
{
contract: ethEndpoint,
config: undefined,
},
{
contract: avaxEndpoint,
config: undefined,
},
],
connections: [
{
from: ethEndpoint,
to: avaxEndpoint,
config: {
defaultSendLibrary: ethUlnPoint.address,
},
},
{
from: avaxEndpoint,
to: ethEndpoint,
config: {
defaultSendLibrary: avaxUlnPoint.address,
},
},
],
}

const builder = await OmniGraphBuilderHardhat.fromConfig(config, contractFactory)

const ethEndpointPoint = omniContractToPoint(await contractFactory(ethEndpoint))
const ethEndpointSdk = await sdkFactory(ethEndpointPoint)

const avaxEndpointPoint = omniContractToPoint(await contractFactory(avaxEndpoint))
const avaxEndpointSdk = await sdkFactory(avaxEndpointPoint)

// Before we configure the Endpoint, we'll set some defaultSendLibraries
{
const signerFactory = createSignerFactory()
const ethSigner = await signerFactory(ethEndpoint.eid)
const ethTransaction = await ethEndpointSdk.setDefaultSendLibrary(avaxUlnPoint.eid, avaxUlnPoint.address)
const ethResponse = await ethSigner.signAndSend(ethTransaction)
const ethReceipt = await ethResponse.wait()

expect(ethReceipt.from).to.equal(await ethSigner.signer.getAddress())
}

// Now we configure the Endpoint
const transactions = await configureEndpoint(builder.graph, sdkFactory)

// And we check that the configuration transaction we already submitted is not included
expect(transactions).to.eql([await avaxEndpointSdk.setDefaultSendLibrary(ethUlnPoint.eid, ethUlnPoint.address)])
describe('endpoint', () => {
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))

// Now for the purposes of the test, we need to get coordinates of our contracts
const ethEndpointPoint = omniContractToPoint(await connectedContractFactory(ethEndpoint))
const avaxEndpointPoint = omniContractToPoint(await connectedContractFactory(avaxEndpoint))

const ethEndpointSdk = await sdkFactory(ethEndpointPoint)
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 ethSendUlnPoint = omniContractToPoint(await connectedContractFactory(ethSendUln))
const avaxSendUlnPoint = omniContractToPoint(await connectedContractFactory(avaxSendUln))

expect(ethDefaultSendLib).to.equal(ethSendUlnPoint.address)
expect(avaxDefaultSendLib).to.equal(avaxSendUlnPoint.address)

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

const ethReceiveUlnPoint = omniContractToPoint(await connectedContractFactory(ethReceiveUln))
const avaxReceiveUlnPoint = omniContractToPoint(await connectedContractFactory(avaxReceiveUln))

expect(ethDefaultReceiveLib).to.equal(ethReceiveUlnPoint.address)
expect(avaxDefaultReceiveLib).to.equal(avaxReceiveUlnPoint.address)
})
})

describe('sendUln302', () => {
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 ethSendUlnPoint = omniContractToPoint(await connectedContractFactory(ethSendUln))
const avaxSendUlnPoint = omniContractToPoint(await connectedContractFactory(avaxSendUln))

const ethSendUlnSdk = await sdkFactory(ethSendUlnPoint)
const avaxSendUlnSdk = await sdkFactory(avaxSendUlnPoint)

const ethConfig = await ethSendUlnSdk.getUlnConfig(avaxSendUlnPoint.eid, avaxSendUlnPoint.address)
const avaxConfig = await avaxSendUlnSdk.getUlnConfig(ethSendUlnPoint.eid, ethSendUlnPoint.address)

expect(ethConfig).to.eql(defaultUlnConfig)
expect(avaxConfig).to.eql(defaultUlnConfig)
})
})
})
Loading

0 comments on commit 9f6a998

Please sign in to comment.