Skip to content

Commit

Permalink
Clean up jsdoc, add in retry strategy, added in Promise.all to create…
Browse files Browse the repository at this point in the history
…ContractsAndConnections
  • Loading branch information
sirarthurmoney committed Feb 23, 2024
1 parent 0f55f13 commit 0b2a3ff
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 48 deletions.
1 change: 1 addition & 0 deletions packages/ua-devtools-evm-hardhat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"lint:fix": "eslint --fix '**/*.{js,ts,json}'"
},
"dependencies": {
"exponential-backoff": "~3.1.1",
"p-memoize": "~4.0.4"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const EXECUTOR: string = 'executor'
export const EXECUTOR_CONFIG: string = 'executorConfig'
export const FROM: string = 'from'
export const GRACE_PERIOD: string = 'gracePeriod'
export const LAYERZERO_LABS_LZ_DEFINITIONS = '@layerzerolabs/lz-definitions'
export const LAYERZERO_LABS_LZ_DEFINITIONS: string = '@layerzerolabs/lz-definitions'
export const MAX_MESSAGE_SIZE: string = 'maxMessageSize'
export const OPTIONAL_DVN_THRESHOLD: string = 'optionalDVNThreshold'
export const OPTIONAL_DVNS: string = 'optionalDVNs'
Expand Down
156 changes: 112 additions & 44 deletions packages/ua-devtools-evm-hardhat/src/oapp/typescript/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import {
Statement,
VariableStatement,
} from 'typescript'
import { formatEid } from '@layerzerolabs/devtools'
import { formatEid, OmniAddress } from '@layerzerolabs/devtools'
import { getEidForNetworkName } from '@layerzerolabs/devtools-evm-hardhat'
import { getReceiveConfig, getSendConfig } from '@/utils/taskHelpers'
import { Uln302ExecutorConfig, Uln302UlnConfig } from '@layerzerolabs/protocol-devtools'
import { Timeout, Uln302ExecutorConfig, Uln302UlnConfig } from '@layerzerolabs/protocol-devtools'
import {
CONFIG,
CONFIRMATIONS,
Expand Down Expand Up @@ -41,11 +41,15 @@ import {
ULN_CONFIG,
ZERO,
} from '@/oapp/typescript/constants'
import { backOff } from 'exponential-backoff'
import { createLogger } from '@layerzerolabs/io-devtools'

const logger = createLogger()

/**
* Normalizes the identifier name by replacing hyphens with underscores.
*
* @param {string} name - The input string to normalize.
* @param {string} name The input string to normalize.
* @returns {string} The normalized identifier name.
*/
export const normalizeIdentifierName = (name: string): string => name.replaceAll('-', '_')
Expand Down Expand Up @@ -74,8 +78,8 @@ export const createEndpointImportDeclaration = (): ImportDeclaration =>
/**
* Creates contract variables for the selected networks and contract name.
*
* @param {string[]} selectedNetworks - An array of network names.
* @param {string} contractName - The name of the contract.
* @param {string[]} selectedNetworks An array of network names.
* @param {string} contractName The name of the contract.
* @returns {VariableStatement[]} An array of VariableStatement objects representing the created contract variables.
*
* const networkContract = {
Expand Down Expand Up @@ -119,7 +123,7 @@ export const createContractVariables = (selectedNetworks: string[], contractName
/**
* Creates the contracts and connections configurations.
*
* @param {Map<string, Identifier>} contractMap - A map containing network names as keys and corresponding contract identifiers as values.
* @param {Map<string, Identifier>} contractMap A map containing network names as keys and corresponding contract identifiers as values.
* @returns {Promise<ExportAssignment>} A promise that resolves to an ExportAssignment object representing the created contracts and connections.
*
* export default {
Expand Down Expand Up @@ -152,39 +156,51 @@ export const createContractVariables = (selectedNetworks: string[], contractName
export const createContractsAndConnections = async (
contractMap: Map<string, Identifier>
): Promise<ExportAssignment> => {
let contracts = factory.createArrayLiteralExpression([])
let connections = factory.createArrayLiteralExpression([])
let contractsArrayLiteral = factory.createArrayLiteralExpression([])
let connectionsArrayLiteral = factory.createArrayLiteralExpression([])

for (const [fromNetwork, fromContract] of contractMap) {
const contractObject = factory.createObjectLiteralExpression([
factory.createPropertyAssignment(factory.createIdentifier(CONTRACT.toLowerCase()), fromContract),
contractsArrayLiteral = factory.createArrayLiteralExpression([
...contractsArrayLiteral.elements,
factory.createObjectLiteralExpression([
factory.createPropertyAssignment(factory.createIdentifier(CONTRACT.toLowerCase()), fromContract),
]),
])

const connections = await Promise.all(
Array.from(contractMap)
.filter(([toNetwork]) => fromNetwork !== toNetwork)
.map(([toNetwork, toContract]) =>
createDefaultConfig(fromNetwork, toNetwork).then((defaultConfig) =>
factory.createObjectLiteralExpression([
factory.createPropertyAssignment(factory.createIdentifier(FROM), fromContract),
factory.createPropertyAssignment(factory.createIdentifier(TO), toContract),
defaultConfig,
])
)
)
)

connectionsArrayLiteral = factory.createArrayLiteralExpression([
...connectionsArrayLiteral.elements,
...connections,
])
contracts = factory.createArrayLiteralExpression([...contracts.elements, contractObject])
for (const [toNetwork, toContract] of contractMap) {
if (fromNetwork == toNetwork) continue
const contractObject = factory.createObjectLiteralExpression([
factory.createPropertyAssignment(factory.createIdentifier(FROM), fromContract),
factory.createPropertyAssignment(factory.createIdentifier(TO), toContract),
await createDefaultConfig(fromNetwork, toNetwork),
])
connections = factory.createArrayLiteralExpression([...connections.elements, contractObject])
}
}

return factory.createExportAssignment(
/* modifiers */ undefined,
/* isExportEquals */ false,
factory.createObjectLiteralExpression([
factory.createPropertyAssignment(CONTRACTS, contracts),
factory.createPropertyAssignment(CONNECTIONS, connections),
factory.createPropertyAssignment(CONTRACTS, contractsArrayLiteral),
factory.createPropertyAssignment(CONNECTIONS, connectionsArrayLiteral),
])
)
}

/**
* Creates a send library configuration.
*
* @param {string} sendDefaultLibrary - The default send library.
* @param {string} sendDefaultLibrary The default send library.
* @returns {PropertyAssignment} A PropertyAssignment object representing the send library configuration.
*
* sendLibrary: "0x0000000000000000000000000000000000000000"
Expand All @@ -199,7 +215,7 @@ export const createSendLibraryConfig = (sendDefaultLibrary: string): PropertyAss
/**
* Creates a receive library configuration object.
*
* @param {string} receiveDefaultLibrary - The default receive library.
* @param {string} receiveDefaultLibrary The default receive library.
* @returns {PropertyAssignment} A PropertyAssignment object representing the receive library configuration.
*
* receiveLibraryConfig: {
Expand All @@ -223,9 +239,9 @@ export const createReceiveLibraryConfig = (receiveDefaultLibrary: string): Prope
/**
* Creates an executor configuration object.
*
* @param {object} executorConfig - The executor configuration parameters.
* @param {number} executorConfig.maxMessageSize - The maximum message size.
* @param {string} executorConfig.executor - The executor string.
* @param {object} executorConfig The executor configuration parameters.
* @param {number} executorConfig.maxMessageSize The maximum message size.
* @param {string} executorConfig.executor The executor string.
* @returns {ObjectLiteralExpression} An ObjectLiteralExpression representing the executor configuration.
*
* executorConfig: {
Expand All @@ -246,11 +262,11 @@ export const creatExecutorConfig = ({ maxMessageSize, executor }: Uln302Executor
/**
* Creates a ULN configuration object.
*
* @param {object} ulnConfig - The ULN configuration parameters.
* @param {bigint} ulnConfig.confirmations - The number of confirmations.
* @param {string[]} ulnConfig.requiredDVNs - An array of required DVNs.
* @param {string[]} ulnConfig.optionalDVNs - An array of optional DVNs.
* @param {number} ulnConfig.optionalDVNThreshold - The threshold for optional DVNs.
* @param {object} ulnConfig The ULN configuration parameters.
* @param {bigint} ulnConfig.confirmations The number of confirmations.
* @param {string[]} ulnConfig.requiredDVNs An array of required DVNs.
* @param {string[]} ulnConfig.optionalDVNs An array of optional DVNs.
* @param {number} ulnConfig.optionalDVNThreshold The threshold for optional DVNs.
* @returns {ObjectLiteralExpression} An ObjectLiteralExpression representing the ULN configuration.
*
* ulnConfig: {
Expand Down Expand Up @@ -296,16 +312,16 @@ export const creatUlnConfig = ({
/**
* Creates a send configuration object.
*
* @param {Uln302ExecutorConfig} sendDefaultExecutorConfig - The default executor configuration.
* @param {Uln302UlnConfig} sendDefaultUlnConfig - The default ULN configuration.
* @param {Uln302ExecutorConfig} sendDefaultExecutorConfig The default executor configuration.
* @param {Uln302UlnConfig} sendDefaultUlnConfig The default ULN configuration.
* @returns {PropertyAssignment} A PropertyAssignment object representing the send configuration.
*
* sendConfig: {
* executorConfig: {},
* ulnConfig: {},
* }
*/
export const creatSendConfig = (
export const createSendConfig = (
sendDefaultExecutorConfig: Uln302ExecutorConfig,
sendDefaultUlnConfig: Uln302UlnConfig
): PropertyAssignment => {
Expand All @@ -327,7 +343,7 @@ export const creatSendConfig = (
/**
* Creates a receive configuration object.
*
* @param {Uln302UlnConfig} receiveDefaultUlnConfig - The default ULN configuration.
* @param {Uln302UlnConfig} receiveDefaultUlnConfig The default ULN configuration.
* @returns {PropertyAssignment} A PropertyAssignment object representing the receive configuration.
*
* receiveConfig: {
Expand All @@ -349,8 +365,8 @@ export const creatReceiveConfig = (receiveDefaultUlnConfig: Uln302UlnConfig): Pr
/**
* Creates a default LayerZero configuration from the passed in networks by reading current defaults off-chain.
*
* @param {string} fromNetwork - The source network.
* @param {string} toNetwork - The destination network.
* @param {string} fromNetwork The source network.
* @param {string} toNetwork The destination network.
* @returns {Promise<PropertyAssignment>} A promise that resolves to a PropertyAssignment object representing the default configuration.
* @throws {Error} Throws an error if any required default configuration is missing.
*
Expand All @@ -363,11 +379,19 @@ export const creatReceiveConfig = (receiveDefaultUlnConfig: Uln302UlnConfig): Pr
* }
*/
export const createDefaultConfig = async (fromNetwork: string, toNetwork: string): Promise<PropertyAssignment> => {
const receiveDefaultConfig = await getReceiveConfig(fromNetwork, toNetwork)
const [receiveDefaultLibrary, receiveDefaultUlnConfig] = receiveDefaultConfig ?? []
let sendDefaultConfig: [OmniAddress, Uln302UlnConfig, Uln302ExecutorConfig] | undefined
let receiveDefaultConfig: [OmniAddress, Uln302UlnConfig, Timeout] | undefined
try {
;[sendDefaultConfig, receiveDefaultConfig] = await Promise.all([
basicRetryPolicy(() => getSendConfig(fromNetwork, toNetwork)),
basicRetryPolicy(() => getReceiveConfig(fromNetwork, toNetwork)),
])
} catch (error) {
console.error('Failed to get send and receive default configs:', error)
}

const sendDefaultConfig = await getSendConfig(fromNetwork, toNetwork)
const [sendDefaultLibrary, sendDefaultUlnConfig, sendDefaultExecutorConfig] = sendDefaultConfig ?? []
const [receiveDefaultLibrary, receiveDefaultUlnConfig] = receiveDefaultConfig ?? []

if (sendDefaultLibrary == null) {
throw new Error(
Expand Down Expand Up @@ -396,7 +420,7 @@ export const createDefaultConfig = async (fromNetwork: string, toNetwork: string
factory.createObjectLiteralExpression([
createSendLibraryConfig(sendDefaultLibrary),
createReceiveLibraryConfig(receiveDefaultLibrary),
creatSendConfig(sendDefaultExecutorConfig, sendDefaultUlnConfig),
createSendConfig(sendDefaultExecutorConfig, sendDefaultUlnConfig),
creatReceiveConfig(receiveDefaultUlnConfig),
])
)
Expand All @@ -405,8 +429,8 @@ export const createDefaultConfig = async (fromNetwork: string, toNetwork: string
/**
* Generates a default LayerZero configuration for the selected networks and contract.
*
* @param {string[]} selectedNetworks - An array of network names.
* @param {string} contractName - The name of the contract.
* @param {string[]} selectedNetworks An array of network names.
* @param {string} contractName The name of the contract.
* @returns {Promise<NodeArray<Statement>>} A promise that resolves to a NodeArray containing generated LayerZero configuration statements.
*/
export const generateLzConfig = async (
Expand All @@ -425,3 +449,47 @@ export const generateLzConfig = async (
)
),
])

/**
* Retry policy function that retries a given asynchronous operation with exponential backoff strategy.
* @template T - The type of the result returned by the asynchronous operation.
* @param {() => Promise<T>} fn - The asynchronous operation to be retried.
* @param {number} [maxAttempts=3] - The maximum number of attempts.
* @param {number} [baseDelay=1000] - The base delay in milliseconds before the first retry.
* @param {number} [maxDelay=15000] - The maximum delay in milliseconds between retries.
* @returns {Promise<T>} - A promise resolving to the result of the operation if successful.
* @throws {Error} - Throws an error if all attempts fail.
*/
async function basicRetryPolicy<T>(
fn: () => Promise<T>,
maxAttempts: number = 3,
baseDelay: number = 1000,
maxDelay: number = 15000
): Promise<T> {
const operation = async () => {
return await fn()
}

const backoffOptions = {
numOfAttempts: maxAttempts,
startingDelay: baseDelay,
delayFirstAttempt: true,
maxDelay: maxDelay,
factor: 2,
randomisationFactor: 0.5,
}

for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
logger.verbose(`Attempt ${attempt + 1}/${maxAttempts}`)
return await backOff(operation, backoffOptions)
} catch (error) {
logger.info(`Attempt ${attempt + 1} failed with error: ${error}`)
if (attempt < maxAttempts - 1) {
logger.info('Retrying...')
}
}
}

throw new Error(`All ${maxAttempts} attempts failed`)
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ interface TaskArgs {
*
* The return value is a file path to the newly generated LayerZero Config file
*
* @returns {Promise<oappConfig>}
* @returns {Promise<string>}
*/
const action: ActionType<TaskArgs> = async ({ contractName, oappConfig, logLevel = 'info' }, hre): Promise<string> => {
printLogo()
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,8 @@ function buildDefaultConfig(defaultConfig: Record<string, Record<string, unknown
`sendLibrary: "${defaultConfig.defaultSendLibrary}", ` +
`receiveLibraryConfig: { receiveLibrary: "${defaultConfig.defaultReceiveLibrary}", gracePeriod: 0 }, ` +
`sendConfig: { executorConfig: { maxMessageSize: ${defaultConfig.sendExecutorConfig?.maxMessageSize}, executor: "${defaultConfig.sendExecutorConfig?.executor}" }, ` +
`ulnConfig: { confirmations: ${defaultConfig.sendUlnConfig.confirmations}, requiredDVNs: ${handleDvns(defaultConfig.sendUlnConfig.requiredDVNs)}, optionalDVNs: ${handleDvns(defaultConfig.sendUlnConfig.optionalDVNs)}, optionalDVNThreshold: ${defaultConfig.sendUlnConfig.optionalDVNThreshold ?? 0} } }, ` +
`receiveConfig: { ulnConfig: { confirmations: ${defaultConfig.receiveUlnConfig.confirmations}, requiredDVNs: ${handleDvns(defaultConfig.receiveUlnConfig.requiredDVNs)}, optionalDVNs: ${handleDvns(defaultConfig.receiveUlnConfig.optionalDVNs)}, optionalDVNThreshold: ${defaultConfig.receiveUlnConfig.optionalDVNThreshold ?? 0} } }` +
`ulnConfig: { confirmations: ${defaultConfig.sendUlnConfig?.confirmations}, requiredDVNs: ${handleDvns(defaultConfig.sendUlnConfig?.requiredDVNs as string[])}, optionalDVNs: ${handleDvns(defaultConfig.sendUlnConfig?.optionalDVNs as string[])}, optionalDVNThreshold: ${defaultConfig.sendUlnConfig?.optionalDVNThreshold ?? 0} } }, ` +
`receiveConfig: { ulnConfig: { confirmations: ${defaultConfig.receiveUlnConfig?.confirmations}, requiredDVNs: ${handleDvns(defaultConfig.receiveUlnConfig?.requiredDVNs as string[])}, optionalDVNs: ${handleDvns(defaultConfig.receiveUlnConfig?.optionalDVNs as string[])}, optionalDVNThreshold: ${defaultConfig.receiveUlnConfig?.optionalDVNThreshold ?? 0} } }` +
` }`
)
}
Expand Down

0 comments on commit 0b2a3ff

Please sign in to comment.