-
Notifications
You must be signed in to change notification settings - Fork 169
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: Add utilities for configuring Endpoints & ULNs
- Loading branch information
1 parent
a62b825
commit 9f6a998
Showing
14 changed files
with
483 additions
and
156 deletions.
There are no files selected for viewing
171 changes: 171 additions & 0 deletions
171
packages/ua-utils-evm-hardhat-test/test/__utils__/endpoint.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
198
packages/ua-utils-evm-hardhat-test/test/endpoint/config.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.