Skip to content

Commit

Permalink
chore: Retry wiring
Browse files Browse the repository at this point in the history
  • Loading branch information
janjakubnanista committed Jan 9, 2024
1 parent 9ecfd78 commit eddb67a
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 31 deletions.
85 changes: 58 additions & 27 deletions packages/ua-devtools-evm-hardhat/src/tasks/oapp/wire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,39 +88,70 @@ const action: ActionType<TaskArgs> = async ({ oappConfig: oappConfigPath, logLev
// For now we are only allowing sign & send using the accounts confgiured in hardhat config
const signAndSend = createSignAndSend(createSignerFactory())

// Now we render a progressbar to monitor the task progress
const progressBar = render(createProgressBar({ before: 'Signing... ', after: ` 0/${transactions.length}` }))

logger.verbose(`Sending the transactions`)
const results = await signAndSend(transactions, (result, results) => {
// We'll keep updating the progressbar as we sign the transactions
progressBar.rerender(
createProgressBar({
progress: results.length / transactions.length,
before: 'Signing... ',
after: ` ${results.length}/${transactions.length}`,
})
// eslint-disable-next-line no-constant-condition
while (true) {
// Now we render a progressbar to monitor the task progress
const progressBar = render(createProgressBar({ before: 'Signing... ', after: ` 0/${transactions.length}` }))

logger.verbose(`Sending the transactions`)
const [successful, errors] = await signAndSend(transactions, (result, results) => {
// We'll keep updating the progressbar as we sign the transactions
progressBar.rerender(
createProgressBar({
progress: results.length / transactions.length,
before: 'Signing... ',
after: ` ${results.length}/${transactions.length}`,
})
)
})

// And finally we drop the progressbar and continue
progressBar.clear()

logger.verbose(`Sent the transactions`)
logger.debug(`Successfully sent the following transactions:\n\n${printJson(successful)}`)
logger.debug(`Failed to send the following transactions:\n\n${printJson(errors)}`)

logger.info(
pluralizeNoun(
transactions.length,
`Successfully sent 1 transaction`,
`Successfully sent ${successful.length} transactions`
)
)
})

// And finally we drop the progressbar and continue
progressBar.clear()
// If there are no errors, we break out of the loop immediatelly
if (errors.length === 0) {
logger.info(`${printBoolean(true)} Your OApp is now configured`)

logger.verbose(`Sent the transactions`)
logger.debug(`Received the following output:\n\n${printJson(results)}`)
return [successful, errors]
}

// FIXME We need to check whether we got any errors and display those to the user
logger.info(
pluralizeNoun(
transactions.length,
`Successfully sent 1 transaction`,
`Successfully sent ${transactions.length} transactions`
// Now we bring the bad news to the user
logger.error(
pluralizeNoun(
transactions.length,
`Failed to send 1 transaction`,
`Failed to send ${errors.length} transactions`
)
)
)
logger.info(`${printBoolean(true)} Your OApp is now configured`)

// FIXME We need to return the results
return []
// FIXME Show errors along with the transactions
const previewErrors = isInteractive
? await promptToContinue(`Would you like to preview the failed transactions?`)
: true
if (previewErrors) printRecords(transactions.map(formatOmniTransaction))

// We'll ask the user if they want to retry if we're in interactive mode
//
// If they decide not to, we exit, if they want to retry we start the loop again
const retry = isInteractive ? await promptToContinue(`Would you like to retry?`, true) : false
if (!retry) {
logger.error(`${printBoolean(false)} Failed to configure the OApp`)

return [successful, errors]
}
}
}
task(TASK_LZ_WIRE_OAPP, 'Wire LayerZero OApp')
.addParam('oappConfig', 'Path to your LayerZero OApp config', './layerzero.config.js', types.string)
Expand Down
107 changes: 103 additions & 4 deletions tests/ua-devtools-evm-hardhat-test/test/task/oapp/wire.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { relative, resolve } from 'path'
import { TASK_LZ_WIRE_OAPP } from '@layerzerolabs/ua-devtools-evm-hardhat'
import { deployOAppFixture } from '../../__utils__/oapp'
import { cwd } from 'process'
import { JsonRpcSigner } from '@ethersproject/providers'

jest.mock('@layerzerolabs/io-devtools', () => {
const original = jest.requireActual('@layerzerolabs/io-devtools')
Expand Down Expand Up @@ -121,9 +122,8 @@ describe('task/oapp/wire', () => {

promptToContinueMock.mockResolvedValue(false)

const result = await hre.run(TASK_LZ_WIRE_OAPP, { oappConfig, ci: true })
await hre.run(TASK_LZ_WIRE_OAPP, { oappConfig, ci: true })

expect(result).toEqual([])
expect(promptToContinueMock).not.toHaveBeenCalled()
})

Expand Down Expand Up @@ -155,9 +155,108 @@ describe('task/oapp/wire', () => {
.mockResolvedValueOnce(false) // We don't want to see the list
.mockResolvedValueOnce(true) // We want to continue

const result = await hre.run(TASK_LZ_WIRE_OAPP, { oappConfig })
const [successful, errors] = await hre.run(TASK_LZ_WIRE_OAPP, { oappConfig })

const expectTransactionWithReceipt = { receipt: expect.any(Object), transaction: expect.any(Object) }

expect(successful).toEqual([expectTransactionWithReceipt, expectTransactionWithReceipt])
expect(errors).toEqual([])
})

expect(result).toEqual([])
describe('if a transaction fails', () => {
let sendTransactionMock: jest.SpyInstance

beforeEach(() => {
sendTransactionMock = jest.spyOn(JsonRpcSigner.prototype, 'sendTransaction')
})

afterEach(() => {
sendTransactionMock.mockRestore()
})

it('should return a list of failed transactions in the CI mode', async () => {
const error = new Error('Oh god dammit')

// We want to make the fail
sendTransactionMock.mockRejectedValue(error)

const oappConfig = configPathFixture('valid.config.connected.js')
const [successful, errors] = await hre.run(TASK_LZ_WIRE_OAPP, { oappConfig, ci: true })

expect(successful).toEqual([])
expect(errors).toEqual([
{
error,
point: {
address: expect.any(String),
eid: expect.any(Number),
},
},
])
})

it('should ask the user to retry if not in the CI mode', async () => {
const error = new Error('Oh god dammit')

// Mock the first sendTransaction call to reject, the rest should use the original implementation
//
// This way we simulate a situation in which the first call would fail but then the user retries, it would succeed
sendTransactionMock.mockRejectedValueOnce(error)

// In the non-CI mode we need to answer the prompts
promptToContinueMock
.mockResolvedValueOnce(false) // We don't want to see the list of transactions
.mockResolvedValueOnce(true) // We want to continue
.mockResolvedValueOnce(false) // We don't want to see the list of failed transactions
.mockResolvedValueOnce(true) // We want to retry

const oappConfig = configPathFixture('valid.config.connected.js')
const [successful, errors] = await hre.run(TASK_LZ_WIRE_OAPP, { oappConfig })

// Check that the user has been asked to retry
expect(promptToContinueMock).toHaveBeenCalledWith(`Would you like to preview the failed transactions?`)
expect(promptToContinueMock).toHaveBeenCalledWith(`Would you like to retry?`, true)

// After retrying, the signer should not fail anymore
const expectTransactionWithReceipt = { receipt: expect.any(Object), transaction: expect.any(Object) }
expect(successful).toEqual([expectTransactionWithReceipt, expectTransactionWithReceipt])
expect(errors).toEqual([])
})

it('should not retry if the user decides not to if not in the CI mode', async () => {
const error = new Error('Oh god dammit')

// Mock the first sendTransaction call to reject, the rest should use the original implementation
//
// This way we simulate a situation in which the first call would fail but then the user retries, it would succeed
sendTransactionMock.mockRejectedValueOnce(error)

// In the non-CI mode we need to answer the prompts
promptToContinueMock
.mockResolvedValueOnce(false) // We don't want to see the list of transactions
.mockResolvedValueOnce(true) // We want to continue
.mockResolvedValueOnce(false) // We don't want to see the list of failed transactions
.mockResolvedValueOnce(false) // We don't want to retry

const oappConfig = configPathFixture('valid.config.connected.js')
const [successful, errors] = await hre.run(TASK_LZ_WIRE_OAPP, { oappConfig })

// Check that the user has been asked to retry
expect(promptToContinueMock).toHaveBeenCalledWith(`Would you like to preview the failed transactions?`)
expect(promptToContinueMock).toHaveBeenCalledWith(`Would you like to retry?`, true)

// Check that we got the failures back
expect(successful).toEqual([])
expect(errors).toEqual([
{
error,
point: {
address: expect.any(String),
eid: expect.any(Number),
},
},
])
})
})
})
})

0 comments on commit eddb67a

Please sign in to comment.