Skip to content

Commit

Permalink
🪚 OmniGraph™ Sign & Send flow (#108)
Browse files Browse the repository at this point in the history
  • Loading branch information
janjakubnanista authored Dec 13, 2023
1 parent 66d923a commit 878f278
Show file tree
Hide file tree
Showing 11 changed files with 396 additions and 22 deletions.
1 change: 1 addition & 0 deletions packages/io-utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './filesystem'
export * from './language'
export * from './stdio'
1 change: 1 addition & 0 deletions packages/io-utils/src/language/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './plurals'
51 changes: 51 additions & 0 deletions packages/io-utils/src/language/plurals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const cardinalRules = new Intl.PluralRules('en-US')

const ordinalRules = new Intl.PluralRules('en-US', { type: 'ordinal' })
const ordinals: Record<Intl.LDMLPluralRule, string> = {
one: 'st',
two: 'nd',
few: 'rd',
other: 'th',
zero: 'th',
many: 'th',
}

/**
* Turn a number into an ordinal.
*
* ```typescript
* pluralizeOrdinal(7) // 7th
* pluralizeOrdinal(1) // 1st
* pluralizeOrdinal(19) // 19th
* ```
*
* @param {number} n
* @returns {string}
*/
export const pluralizeOrdinal = (n: number): string => {
const rule = ordinalRules.select(n)
const suffix = ordinals[rule]

return `${n}${suffix}`
}

/**
* Choose a correct form of a noun based on cardinality.
*
* ```typescript
* pluralizeNoun(7, 'cat') // cats
* pluralizeNoun(1, 'cat') // cat
* pluralizeNoun(19, 'cactus', 'cacti') // cacti
* ```
*
* @param {number} n
* @param {string} singular The signular form of the english noun
* @param {string} [plural] Plural version of the noun for irregular cases
* @returns {string}
*/
export const pluralizeNoun = (n: number, singular: string, plural: string = `${singular}s`): string => {
const rule = cardinalRules.select(n)
if (rule === 'one') return singular

return plural
}
67 changes: 67 additions & 0 deletions packages/io-utils/test/language/__snapshots__/plurals.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`language/plurals pluralizeNoun with custom plural should work for 0 1`] = `"cacti"`;

exports[`language/plurals pluralizeNoun with custom plural should work for 1 1`] = `"cactus"`;

exports[`language/plurals pluralizeNoun with custom plural should work for 2 1`] = `"cacti"`;

exports[`language/plurals pluralizeNoun with custom plural should work for 3 1`] = `"cacti"`;

exports[`language/plurals pluralizeNoun with custom plural should work for 4 1`] = `"cacti"`;

exports[`language/plurals pluralizeNoun with custom plural should work for 5 1`] = `"cacti"`;

exports[`language/plurals pluralizeNoun with custom plural should work for 11 1`] = `"cacti"`;

exports[`language/plurals pluralizeNoun with custom plural should work for 12 1`] = `"cacti"`;

exports[`language/plurals pluralizeNoun with custom plural should work for 21 1`] = `"cacti"`;

exports[`language/plurals pluralizeNoun with custom plural should work for 100 1`] = `"cacti"`;

exports[`language/plurals pluralizeNoun with custom plural should work for 1234 1`] = `"cacti"`;

exports[`language/plurals pluralizeNoun without custom plural should work for 0 1`] = `"cats"`;

exports[`language/plurals pluralizeNoun without custom plural should work for 1 1`] = `"cat"`;

exports[`language/plurals pluralizeNoun without custom plural should work for 2 1`] = `"cats"`;

exports[`language/plurals pluralizeNoun without custom plural should work for 3 1`] = `"cats"`;

exports[`language/plurals pluralizeNoun without custom plural should work for 4 1`] = `"cats"`;

exports[`language/plurals pluralizeNoun without custom plural should work for 5 1`] = `"cats"`;

exports[`language/plurals pluralizeNoun without custom plural should work for 11 1`] = `"cats"`;

exports[`language/plurals pluralizeNoun without custom plural should work for 12 1`] = `"cats"`;

exports[`language/plurals pluralizeNoun without custom plural should work for 21 1`] = `"cats"`;

exports[`language/plurals pluralizeNoun without custom plural should work for 100 1`] = `"cats"`;

exports[`language/plurals pluralizeNoun without custom plural should work for 1234 1`] = `"cats"`;

exports[`language/plurals pluralizeOrdinal should work for 0 1`] = `"0th"`;

exports[`language/plurals pluralizeOrdinal should work for 1 1`] = `"1st"`;

exports[`language/plurals pluralizeOrdinal should work for 2 1`] = `"2nd"`;

exports[`language/plurals pluralizeOrdinal should work for 3 1`] = `"3rd"`;

exports[`language/plurals pluralizeOrdinal should work for 4 1`] = `"4th"`;

exports[`language/plurals pluralizeOrdinal should work for 5 1`] = `"5th"`;

exports[`language/plurals pluralizeOrdinal should work for 11 1`] = `"11th"`;

exports[`language/plurals pluralizeOrdinal should work for 12 1`] = `"12th"`;

exports[`language/plurals pluralizeOrdinal should work for 21 1`] = `"21st"`;

exports[`language/plurals pluralizeOrdinal should work for 100 1`] = `"100th"`;

exports[`language/plurals pluralizeOrdinal should work for 1234 1`] = `"1234th"`;
23 changes: 23 additions & 0 deletions packages/io-utils/test/language/plurals.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { pluralizeNoun, pluralizeOrdinal } from '@/language'

describe('language/plurals', () => {
describe('pluralizeOrdinal', () => {
it.each([0, 1, 2, 3, 4, 5, 11, 12, 21, 100, 1234])(`should work for %d`, (n) =>
expect(pluralizeOrdinal(n)).toMatchSnapshot()
)
})

describe('pluralizeNoun', () => {
describe('without custom plural', () => {
it.each([0, 1, 2, 3, 4, 5, 11, 12, 21, 100, 1234])(`should work for %d`, (n) =>
expect(pluralizeNoun(n, 'cat')).toMatchSnapshot()
)
})

describe('with custom plural', () => {
it.each([0, 1, 2, 3, 4, 5, 11, 12, 21, 100, 1234])(`should work for %d`, (n) =>
expect(pluralizeNoun(n, 'cactus', 'cacti')).toMatchSnapshot()
)
})
})
})
25 changes: 6 additions & 19 deletions packages/ua-utils-evm-hardhat-test/test/__utils__/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
OmniGraphBuilderHardhat,
type OmniGraphHardhat,
} from '@layerzerolabs/utils-evm-hardhat'
import { createLogger } from '@layerzerolabs/io-utils'
import { EndpointId } from '@layerzerolabs/lz-definitions'
import { omniContractToPoint } from '@layerzerolabs/utils-evm'
import {
Expand All @@ -17,7 +16,7 @@ import {
Uln302UlnConfig,
} from '@layerzerolabs/protocol-utils'
import { createEndpointFactory, createUln302Factory } from '@layerzerolabs/protocol-utils-evm'
import { formatOmniPoint } from '@layerzerolabs/utils'
import { createSignAndSend } from '@layerzerolabs/utils'

export const ethEndpoint = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'EndpointV2' }
export const ethReceiveUln = { eid: EndpointId.ETHEREUM_MAINNET, contractName: 'ReceiveUln302' }
Expand Down Expand Up @@ -94,9 +93,8 @@ export const deployEndpoint = async () => {
*/
export const setupDefaultEndpoint = async (): Promise<void> => {
// This is the tooling we are going to need
const logger = createLogger()
const contractFactory = createConnectedContractFactory()
const signerFactory = createSignerFactory()
const signAndSend = createSignAndSend(createSignerFactory())
const ulnSdkFactory = createUln302Factory(contractFactory)
const endpointSdkFactory = createEndpointFactory(contractFactory, ulnSdkFactory)

Expand Down Expand Up @@ -258,20 +256,9 @@ export const setupDefaultEndpoint = async (): Promise<void> => {
...receiveUlnTransactions_Opt2,
]

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

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

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

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

const receipt = await response.wait()
logger.debug(`${formatOmniPoint(transaction.point)}: ${description}: ${receipt.transactionHash}`)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [successful, errors] = await signAndSend(transactions)
if (errors.length !== 0) {
throw new Error(`Failed to deploy endpoint: ${errors}`)
}

logger.debug(`Done configuring endpoint`)
}
9 changes: 6 additions & 3 deletions packages/ua-utils-evm-hardhat/src/tasks/oapp/wire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
setDefaultLogLevel,
promptToContinue,
printJson,
pluralizeNoun,
} from '@layerzerolabs/io-utils'
import { OAppOmniGraphHardhat, OAppOmniGraphHardhatSchema } from '@/oapp'
import { OAppOmniGraph, configureOApp } from '@layerzerolabs/ua-utils'
Expand Down Expand Up @@ -126,9 +127,11 @@ const action: ActionType<TaskArgs> = async ({ oappConfig: oappConfigPath, logLev

// Tell the user about the transactions
logger.info(
transactions.length === 1
? `There is 1 transaction required to configure the OApp`
: `There are ${transactions.length} transactions required to configure the OApp`
pluralizeNoun(
transactions.length,
`There is 1 transaction required to configure the OApp`,
`There are ${transactions.length} transactions required to configure the OApp`
)
)

// Ask them whether they want to see them
Expand Down
1 change: 1 addition & 0 deletions packages/utils/src/transactions/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './format'
export * from './signer'
export * from './types'
export * from './utils'
61 changes: 61 additions & 0 deletions packages/utils/src/transactions/signer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { createModuleLogger, pluralizeNoun, pluralizeOrdinal } from '@layerzerolabs/io-utils'
import type { OmniSignerFactory, OmniTransaction, OmniTransactionWithReceipt } from './types'
import { formatOmniPoint } from '@/omnigraph/format'
import type { OmniError } from '@/omnigraph/types'

/**
* Creates a sign & send utility for a list of transaction
* with a help of `OmniSignerFactory`
*
* @param {OmniSignerFactory} createSigner
*/
export const createSignAndSend =
(createSigner: OmniSignerFactory) =>
async (
transactions: OmniTransaction[]
): Promise<[successful: OmniTransactionWithReceipt[], errors: OmniError[]]> => {
const logger = createModuleLogger('sign & send')

// Put it here so that we don't need to type like seven toilet rolls of variable names
const n = transactions.length

// Just exit when there is nothing to sign
if (n === 0) return logger.debug(`No transactions to sign, exiting`), [[], []]

// Tell the user how many we are signing
logger.debug(`Signing ${n} ${pluralizeNoun(n, 'transaction')}`)

// We'll gather the successful transactions here
const successful: OmniTransactionWithReceipt[] = []

for (const [index, transaction] of transactions.entries()) {
// We want to refer to this transaction by index so we create an ordinal for it (1st, 2nd etc)
const ordinal = pluralizeOrdinal(index + 1)

try {
logger.debug(`Signing ${ordinal} transaction to ${formatOmniPoint(transaction.point)}`)

logger.debug(`Creating signer for ${ordinal} transaction`)
const signer = await createSigner(transaction.point.eid)

logger.debug(`Signing ${ordinal} transaction`)
const response = await signer.signAndSend(transaction)

logger.debug(`Signed ${ordinal} transaction, got hash ${response.transactionHash}`)

const receipt = await response.wait()
logger.debug(`Finished ${ordinal} transaction`)

successful.push({ transaction, receipt })
} catch (error) {
logger.debug(`Failed to process ${ordinal} transaction: ${error}`)

return [successful, [{ point: transaction.point, error }]]
}
}

// Tell the inquisitive user what a good job we did
logger.debug(`Successfully signed ${n} ${pluralizeNoun(n, 'transaction')}`)

return [successful, []]
}
10 changes: 10 additions & 0 deletions packages/utils/src/transactions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ export interface OmniTransaction {
value?: string | bigint | number
}

export interface OmniTransactionWithResponse<TReceipt extends OmniTransactionReceipt = OmniTransactionReceipt> {
transaction: OmniTransaction
response: OmniTransactionResponse<TReceipt>
}

export interface OmniTransactionWithReceipt<TReceipt extends OmniTransactionReceipt = OmniTransactionReceipt> {
transaction: OmniTransaction
receipt: TReceipt
}

export interface OmniTransactionResponse<TReceipt extends OmniTransactionReceipt = OmniTransactionReceipt> {
transactionHash: string
wait: (confirmations?: number) => Promise<TReceipt>
Expand Down
Loading

0 comments on commit 878f278

Please sign in to comment.