diff --git a/package.json b/package.json index fd578fb86f..7c558b88f1 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "@ethersproject/address": "^5.0.8", "@ethersproject/bignumber": "^5.1.1", "@ethersproject/bytes": "^5.0.8", + "async-mutex": "^0.4.0", "ethers": "^5.1.0" }, "devDependencies": { diff --git a/src/index.ts b/src/index.ts index 58e43cf5ca..f34a892d76 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,21 +23,21 @@ export { L2ContractTransaction, } from './lib/message/L2Transaction' export { - ChildToParentChainMessage as L2ToL1Message, - ChildToParentChainMessageWriter as L2ToL1MessageWriter, - ChildToParentChainMessageReader as L2ToL1MessageReader, + ChildToParentMessage as L2ToL1Message, + ChildToParentMessageWriter as L2ToL1MessageWriter, + ChildToParentMessageReader as L2ToL1MessageReader, } from './lib/message/L2ToL1Message' export { L1ContractTransaction, L1TransactionReceipt, } from './lib/message/L1Transaction' export { - ParentChainToChainMessageStatus as L1ToL2MessageStatus, + ParentToChildMessageStatus as L1ToL2MessageStatus, EthDepositStatus, - ParentChainToChainMessage as L1ToL2Message, - ParentChainToChainMessageReader as L1ToL2MessageReader, - ParentChainToChainMessageReaderClassic as L1ToL2MessageReaderClassic, - ParentChainToChainMessageWriter as L1ToL2MessageWriter, + ParentToChildMessage as L1ToL2Message, + ParentToChildMessageReader as L1ToL2MessageReader, + ParentToChildMessageReaderClassic as L1ToL2MessageReaderClassic, + ParentToChildMessageWriter as L1ToL2MessageWriter, } from './lib/message/L1ToL2Message' export { L1ToL2MessageGasEstimator } from './lib/message/L1ToL2MessageGasEstimator' export { argSerializerConstructor } from './lib/utils/byte_serialize_params' diff --git a/src/lib/message/L1ToL2Message.ts b/src/lib/message/L1ToL2Message.ts index 164619b549..406dba83cd 100644 --- a/src/lib/message/L1ToL2Message.ts +++ b/src/lib/message/L1ToL2Message.ts @@ -43,7 +43,7 @@ import { getTransactionReceipt, isDefined } from '../utils/lib' import { EventFetcher } from '../utils/eventFetcher' import { ErrorCode, Logger } from '@ethersproject/logger' -export enum ParentChainToChainMessageStatus { +export enum ParentToChildMessageStatus { /** * The retryable ticket has yet to be created */ @@ -90,17 +90,14 @@ interface RetryableExistsError extends Error { /** * Conditional type for Signer or Provider. If T is of type Provider - * then ParentChainToChainMessageReaderOrWriter will be of type ParentChainToChainMessageReader. - * If T is of type Signer then ParentChainToChainMessageReaderOrWriter will be of - * type ParentChainToChainMessageWriter. + * then ParentToChildMessageReaderOrWriter will be of type ParentToChildMessageReader. + * If T is of type Signer then ParentToChildMessageReaderOrWriter will be of + * type ParentToChildMessageWriter. */ -export type ParentChainToChainMessageReaderOrWriter< - T extends SignerOrProvider -> = T extends Provider - ? ParentChainToChainMessageReader - : ParentChainToChainMessageWriter +export type ParentToChildMessageReaderOrWriter = + T extends Provider ? ParentToChildMessageReader : ParentToChildMessageWriter -export abstract class ParentChainToChainMessage { +export abstract class ParentToChildMessage { /** * When messages are sent from ParentChain to Chain a retryable ticket is created on Chain. * The retryableCreationId can be used to retrieve information about the success or failure of the @@ -182,7 +179,7 @@ export abstract class ParentChainToChainMessage { messageNumber: BigNumber, parentChainBaseFee: BigNumber, messageData: RetryableMessageParams - ): ParentChainToChainMessageReaderOrWriter + ): ParentToChildMessageReaderOrWriter public static fromEventComponents( chainSignerOrProvider: T, chainId: number, @@ -190,9 +187,9 @@ export abstract class ParentChainToChainMessage { messageNumber: BigNumber, parentChainBaseFee: BigNumber, messageData: RetryableMessageParams - ): ParentChainToChainMessageReader | ParentChainToChainMessageWriter { + ): ParentToChildMessageReader | ParentToChildMessageWriter { return SignerProviderUtils.isSigner(chainSignerOrProvider) - ? new ParentChainToChainMessageWriter( + ? new ParentToChildMessageWriter( chainSignerOrProvider, chainId, sender, @@ -200,7 +197,7 @@ export abstract class ParentChainToChainMessage { parentChainBaseFee, messageData ) - : new ParentChainToChainMessageReader( + : new ParentToChildMessageReader( chainSignerOrProvider, chainId, sender, @@ -217,22 +214,21 @@ export abstract class ParentChainToChainMessage { public readonly parentChainBaseFee: BigNumber, public readonly messageData: RetryableMessageParams ) { - this.retryableCreationId = - ParentChainToChainMessage.calculateSubmitRetryableId( - chainId, - sender, - messageNumber, - parentChainBaseFee, - messageData.destAddress, - messageData.l2CallValue, - messageData.l1Value, - messageData.maxSubmissionFee, - messageData.excessFeeRefundAddress, - messageData.callValueRefundAddress, - messageData.gasLimit, - messageData.maxFeePerGas, - messageData.data - ) + this.retryableCreationId = ParentToChildMessage.calculateSubmitRetryableId( + chainId, + sender, + messageNumber, + parentChainBaseFee, + messageData.destAddress, + messageData.l2CallValue, + messageData.l1Value, + messageData.maxSubmissionFee, + messageData.excessFeeRefundAddress, + messageData.callValueRefundAddress, + messageData.gasLimit, + messageData.maxFeePerGas, + messageData.data + ) } } @@ -240,15 +236,15 @@ export abstract class ParentChainToChainMessage { * If the status is redeemed an chainTxReceipt is populated. * For all other statuses chainTxReceipt is not populated */ -export type ParentChainToChainMessageWaitResult = +export type ParentToChildMessageWaitResult = | { - status: ParentChainToChainMessageStatus.REDEEMED + status: ParentToChildMessageStatus.REDEEMED chainTxReceipt: TransactionReceipt } | { status: Exclude< - ParentChainToChainMessageStatus, - ParentChainToChainMessageStatus.REDEEMED + ParentToChildMessageStatus, + ParentToChildMessageStatus.REDEEMED > } @@ -256,7 +252,7 @@ export type EthDepositMessageWaitResult = { chainTxReceipt: TransactionReceipt | null } -export class ParentChainToChainMessageReader extends ParentChainToChainMessage { +export class ParentToChildMessageReader extends ParentToChildMessage { private retryableCreationReceipt: TransactionReceipt | undefined | null public constructor( public readonly chainProvider: Provider, @@ -321,7 +317,7 @@ export class ParentChainToChainMessageReader extends ParentChainToChainMessage { * Receipt for the successful chain transaction created by this message. * @returns TransactionReceipt of the first successful redeem if exists, otherwise the current status of the message. */ - public async getSuccessfulRedeem(): Promise { + public async getSuccessfulRedeem(): Promise { const chainNetwork = await getChainNetwork(this.chainProvider) const eventFetcher = new EventFetcher(this.chainProvider) const creationReceipt = await this.getRetryableCreationReceipt() @@ -329,11 +325,11 @@ export class ParentChainToChainMessageReader extends ParentChainToChainMessage { if (!isDefined(creationReceipt)) { // retryable was never created, or not created yet // therefore it cant have been redeemed or be expired - return { status: ParentChainToChainMessageStatus.NOT_YET_CREATED } + return { status: ParentToChildMessageStatus.NOT_YET_CREATED } } if (creationReceipt.status === 0) { - return { status: ParentChainToChainMessageStatus.CREATION_FAILED } + return { status: ParentToChildMessageStatus.CREATION_FAILED } } // check the auto redeem first to avoid doing costly log queries in the happy case @@ -341,7 +337,7 @@ export class ParentChainToChainMessageReader extends ParentChainToChainMessage { if (autoRedeem && autoRedeem.status === 1) { return { chainTxReceipt: autoRedeem, - status: ParentChainToChainMessageStatus.REDEEMED, + status: ParentToChildMessageStatus.REDEEMED, } } @@ -349,7 +345,7 @@ export class ParentChainToChainMessageReader extends ParentChainToChainMessage { // the retryable was created and still exists // therefore it cant have been redeemed or be expired return { - status: ParentChainToChainMessageStatus.FUNDS_DEPOSITED_ON_CHAIN, + status: ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHAIN, } } @@ -397,7 +393,7 @@ export class ParentChainToChainMessageReader extends ParentChainToChainMessage { if (successfulRedeem.length == 1) return { chainTxReceipt: successfulRedeem[0], - status: ParentChainToChainMessageStatus.REDEEMED, + status: ParentToChildMessageStatus.REDEEMED, } const toBlock = await this.chainProvider.getBlock(toBlockNumber) @@ -440,7 +436,7 @@ export class ParentChainToChainMessageReader extends ParentChainToChainMessage { // we know from earlier that the retryable no longer exists, so if we havent found the redemption // we know that it must have expired - return { status: ParentChainToChainMessageStatus.EXPIRED } + return { status: ParentToChildMessageStatus.EXPIRED } } /** @@ -474,7 +470,7 @@ export class ParentChainToChainMessageReader extends ParentChainToChainMessage { } } - public async status(): Promise { + public async status(): Promise { return (await this.getSuccessfulRedeem()).status } @@ -493,7 +489,7 @@ export class ParentChainToChainMessageReader extends ParentChainToChainMessage { public async waitForStatus( confirmations?: number, timeout?: number - ): Promise { + ): Promise { const chainNetwork = await getChainNetwork(this.chainId) const chosenTimeout = isDefined(timeout) @@ -557,7 +553,7 @@ export class ParentChainToChainMessageReader extends ParentChainToChainMessage { } } -export class ParentChainToChainMessageReaderClassic { +export class ParentToChildMessageReaderClassic { private retryableCreationReceipt: TransactionReceipt | undefined | null public readonly messageNumber: BigNumber public readonly retryableCreationId: string @@ -628,15 +624,15 @@ export class ParentChainToChainMessageReaderClassic { return this.retryableCreationReceipt || null } - public async status(): Promise { + public async status(): Promise { const creationReceipt = await this.getRetryableCreationReceipt() if (!isDefined(creationReceipt)) { - return ParentChainToChainMessageStatus.NOT_YET_CREATED + return ParentToChildMessageStatus.NOT_YET_CREATED } if (creationReceipt.status === 0) { - return ParentChainToChainMessageStatus.CREATION_FAILED + return ParentToChildMessageStatus.CREATION_FAILED } const chainDerivedHash = this.calculateChainDerivedHash( @@ -647,14 +643,14 @@ export class ParentChainToChainMessageReaderClassic { ) if (chainTxReceipt && chainTxReceipt.status === 1) { - return ParentChainToChainMessageStatus.REDEEMED + return ParentToChildMessageStatus.REDEEMED } - return ParentChainToChainMessageStatus.EXPIRED + return ParentToChildMessageStatus.EXPIRED } } -export class ParentChainToChainMessageWriter extends ParentChainToChainMessageReader { +export class ParentToChildMessageWriter extends ParentToChildMessageReader { public constructor( public readonly chainSigner: Signer, chainId: number, @@ -677,11 +673,11 @@ export class ParentChainToChainMessageWriter extends ParentChainToChainMessageRe /** * Manually redeem the retryable ticket. - * Throws if message status is not ParentChainToChainMessageStatus.FUNDS_DEPOSITED_ON_CHAIN + * Throws if message status is not ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHAIN */ public async redeem(overrides?: Overrides): Promise { const status = await this.status() - if (status === ParentChainToChainMessageStatus.FUNDS_DEPOSITED_ON_CHAIN) { + if (status === ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHAIN) { const arbRetryableTx = ArbRetryableTx__factory.connect( ARB_RETRYABLE_TX_ADDRESS, this.chainSigner @@ -698,10 +694,10 @@ export class ParentChainToChainMessageWriter extends ParentChainToChainMessageRe } else { throw new ArbSdkError( `Cannot redeem as retryable does not exist. Message status: ${ - ParentChainToChainMessageStatus[status] + ParentToChildMessageStatus[status] } must be: ${ - ParentChainToChainMessageStatus[ - ParentChainToChainMessageStatus.FUNDS_DEPOSITED_ON_CHAIN + ParentToChildMessageStatus[ + ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHAIN ] }.` ) @@ -710,11 +706,11 @@ export class ParentChainToChainMessageWriter extends ParentChainToChainMessageRe /** * Cancel the retryable ticket. - * Throws if message status is not ParentChainToChainMessageStatus.FUNDS_DEPOSITED_ON_CHAIN + * Throws if message status is not ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHAIN */ public async cancel(overrides?: Overrides): Promise { const status = await this.status() - if (status === ParentChainToChainMessageStatus.FUNDS_DEPOSITED_ON_CHAIN) { + if (status === ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHAIN) { const arbRetryableTx = ArbRetryableTx__factory.connect( ARB_RETRYABLE_TX_ADDRESS, this.chainSigner @@ -723,10 +719,10 @@ export class ParentChainToChainMessageWriter extends ParentChainToChainMessageRe } else { throw new ArbSdkError( `Cannot cancel as retryable does not exist. Message status: ${ - ParentChainToChainMessageStatus[status] + ParentToChildMessageStatus[status] } must be: ${ - ParentChainToChainMessageStatus[ - ParentChainToChainMessageStatus.FUNDS_DEPOSITED_ON_CHAIN + ParentToChildMessageStatus[ + ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHAIN ] }.` ) @@ -735,11 +731,11 @@ export class ParentChainToChainMessageWriter extends ParentChainToChainMessageRe /** * Increase the timeout of a retryable ticket. - * Throws if message status is not ParentChainToChainMessageStatus.FUNDS_DEPOSITED_ON_CHAIN + * Throws if message status is not ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHAIN */ public async keepAlive(overrides?: Overrides): Promise { const status = await this.status() - if (status === ParentChainToChainMessageStatus.FUNDS_DEPOSITED_ON_CHAIN) { + if (status === ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHAIN) { const arbRetryableTx = ArbRetryableTx__factory.connect( ARB_RETRYABLE_TX_ADDRESS, this.chainSigner @@ -748,10 +744,10 @@ export class ParentChainToChainMessageWriter extends ParentChainToChainMessageRe } else { throw new ArbSdkError( `Cannot keep alive as retryable does not exist. Message status: ${ - ParentChainToChainMessageStatus[status] + ParentToChildMessageStatus[status] } must be: ${ - ParentChainToChainMessageStatus[ - ParentChainToChainMessageStatus.FUNDS_DEPOSITED_ON_CHAIN + ParentToChildMessageStatus[ + ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHAIN ] }.` ) diff --git a/src/lib/message/L1Transaction.ts b/src/lib/message/L1Transaction.ts index 2f8d5d4ae0..9351ecb5bf 100644 --- a/src/lib/message/L1Transaction.ts +++ b/src/lib/message/L1Transaction.ts @@ -21,13 +21,13 @@ import { Log, Provider } from '@ethersproject/abstract-provider' import { ContractTransaction } from '@ethersproject/contracts' import { BigNumber } from '@ethersproject/bignumber' import { - ParentChainToChainMessage as L1ToL2Message, - ParentChainToChainMessageReaderOrWriter as L1ToL2MessageReaderOrWriter, - ParentChainToChainMessageReader as L1ToL2MessageReader, - ParentChainToChainMessageReaderClassic as L1ToL2MessageReaderClassic, - ParentChainToChainMessageWriter as L1ToL2MessageWriter, - ParentChainToChainMessageStatus as L1ToL2MessageStatus, - ParentChainToChainMessageWaitResult as L1ToL2MessageWaitResult, + ParentToChildMessage as L1ToL2Message, + ParentToChildMessageReaderOrWriter as L1ToL2MessageReaderOrWriter, + ParentToChildMessageReader as L1ToL2MessageReader, + ParentToChildMessageReaderClassic as L1ToL2MessageReaderClassic, + ParentToChildMessageWriter as L1ToL2MessageWriter, + ParentToChildMessageStatus as L1ToL2MessageStatus, + ParentToChildMessageWaitResult as L1ToL2MessageWaitResult, EthDepositMessage, EthDepositMessageWaitResult, } from './L1ToL2Message' diff --git a/src/lib/message/L2ToL1Message.ts b/src/lib/message/L2ToL1Message.ts index 424d664aec..50c9a336b9 100644 --- a/src/lib/message/L2ToL1Message.ts +++ b/src/lib/message/L2ToL1Message.ts @@ -29,77 +29,74 @@ import { import * as classic from './L2ToL1MessageClassic' import * as nitro from './L2ToL1MessageNitro' import { - L2ToL1TransactionEvent as ClassicChildToParentChainTransactionEvent, - L2ToL1TxEvent as NitroChildToParentChainTransactionEvent, + L2ToL1TransactionEvent as ClassicChildToParentTransactionEvent, + L2ToL1TxEvent as NitroChildToParentTransactionEvent, } from '../abi/ArbSys' import { isDefined } from '../utils/lib' import { EventArgs } from '../dataEntities/event' -import { L2ToL1MessageStatus as ChildToParentChainMessageStatus } from '../dataEntities/message' +import { L2ToL1MessageStatus as ChildToParentMessageStatus } from '../dataEntities/message' import { getChainNetwork } from '../dataEntities/networks' import { ArbSdkError } from '../dataEntities/errors' -export type ChildToParentChainTransactionEvent = - | EventArgs - | EventArgs +export type ChildToParentTransactionEvent = + | EventArgs + | EventArgs /** * Conditional type for Signer or Provider. If T is of type Provider - * then ChildToParentChainMessageReaderOrWriter will be of type ChildToParentChainMessageReader. - * If T is of type Signer then ChildToParentChainMessageReaderOrWriter will be of - * type ChildToParentChainMessageWriter. + * then ChildToParentMessageReaderOrWriter will be of type ChildToParentMessageReader. + * If T is of type Signer then ChildToParentMessageReaderOrWriter will be of + * type ChildToParentMessageWriter. */ -export type ChildToParentChainMessageReaderOrWriter< - T extends SignerOrProvider -> = T extends Provider - ? ChildToParentChainMessageReader - : ChildToParentChainMessageWriter +export type ChildToParentMessageReaderOrWriter = + T extends Provider ? ChildToParentMessageReader : ChildToParentMessageWriter /** * Base functionality for Chain->ParentChain messages */ -export class ChildToParentChainMessage { +export class ChildToParentMessage { protected isClassic( - e: ChildToParentChainTransactionEvent - ): e is EventArgs { + e: ChildToParentTransactionEvent + ): e is EventArgs { return isDefined( - (e as EventArgs).indexInBatch + (e as EventArgs).indexInBatch ) } /** - * Instantiates a new `ChildToParentChainMessageWriter` or `ChildToParentChainMessageReader` object. + * Instantiates a new `ChildToParentMessageWriter` or `ChildToParentMessageReader` object. * * @param {SignerOrProvider} ParentChainSignerOrProvider Signer or provider to be used for executing or reading the Chain-to-ParentChain message. - * @param {ChildToParentChainTransactionEvent} event The event containing the data of the Chain-to-ParentChain message. + * @param {ChildToParentTransactionEvent} event The event containing the data of the Chain-to-ParentChain message. * @param {Provider} [ParentChainProvider] Optional. Used to override the Provider which is attached to `ParentChainSignerOrProvider` in case you need more control. This will be a required parameter in a future major version update. */ public static fromEvent( ParentChainSignerOrProvider: T, - event: ChildToParentChainTransactionEvent, + event: ChildToParentTransactionEvent, ParentChainProvider?: Provider - ): ChildToParentChainMessageReaderOrWriter + ): ChildToParentMessageReaderOrWriter static fromEvent( ParentChainSignerOrProvider: T, - event: ChildToParentChainTransactionEvent, + event: ChildToParentTransactionEvent, ParentChainProvider?: Provider - ): ChildToParentChainMessageReader | ChildToParentChainMessageWriter { + ): ChildToParentMessageReader | ChildToParentMessageWriter { return SignerProviderUtils.isSigner(ParentChainSignerOrProvider) - ? new ChildToParentChainMessageWriter( + ? new ChildToParentMessageWriter( ParentChainSignerOrProvider, event, ParentChainProvider ) - : new ChildToParentChainMessageReader(ParentChainSignerOrProvider, event) + : new ChildToParentMessageReader(ParentChainSignerOrProvider, event) } /** - * Get event logs for ChildToParentChain transactions. + * Get event logs for ChildToParent transactions. * @param ChainProvider * @param filter Block range filter * @param position The batchnumber indexed field was removed in nitro and a position indexed field was added. * For pre-nitro events the value passed in here will be used to find events with the same batchnumber. * For post nitro events it will be used to find events with the same position. - * @param destination The ParentChain destination of the ChildToParentChain message + * @param destination The ParentChain destination of the ChildToParent message * @param hash The uniqueId indexed field was removed in nitro and a hash indexed field was added. * For pre-nitro events the value passed in here will be used to find events with the same uniqueId. * For post nitro events it will be used to find events with the same hash. @@ -113,9 +110,7 @@ export class ChildToParentChainMessage { destination?: string, hash?: BigNumber, indexInBatch?: BigNumber - ): Promise< - (ChildToParentChainTransactionEvent & { transactionHash: string })[] - > { + ): Promise<(ChildToParentTransactionEvent & { transactionHash: string })[]> { const ChainNetwork = await getChainNetwork(ChainProvider) const inClassicRange = (blockTag: BlockTag, nitroGenBlock: number) => { @@ -202,13 +197,13 @@ export class ChildToParentChainMessage { /** * Provides read-only access for Chain-to-ParentChain-messages */ -export class ChildToParentChainMessageReader extends ChildToParentChainMessage { +export class ChildToParentMessageReader extends ChildToParentMessage { private readonly classicReader?: classic.ChildToParentChainMessageReaderClassic private readonly nitroReader?: nitro.ChildToParentChainMessageReaderNitro constructor( protected readonly ParentChainProvider: Provider, - event: ChildToParentChainTransactionEvent + event: ChildToParentTransactionEvent ) { super() if (this.isClassic(event)) { @@ -240,8 +235,8 @@ export class ChildToParentChainMessageReader extends ChildToParentChainMessage { */ public async status( ChainProvider: Provider - ): Promise { - // can we create an ChildToParentChainmessage here, we need to - the constructor is what we need + ): Promise { + // can we create an ChildToParentmessage here, we need to - the constructor is what we need if (this.nitroReader) return await this.nitroReader.status(ChainProvider) else return await this.classicReader!.status(ChainProvider) } @@ -284,20 +279,20 @@ export class ChildToParentChainMessageReader extends ChildToParentChainMessage { /** * Provides read and write access for Chain-to-ParentChain-messages */ -export class ChildToParentChainMessageWriter extends ChildToParentChainMessageReader { +export class ChildToParentMessageWriter extends ChildToParentMessageReader { private readonly classicWriter?: classic.ChildToParentChainMessageWriterClassic private readonly nitroWriter?: nitro.ChildToParentChainMessageWriterNitro /** - * Instantiates a new `ChildToParentChainMessageWriter` object. + * Instantiates a new `ChildToParentMessageWriter` object. * * @param {Signer} ParentChainSigner The signer to be used for executing the Chain-to-ParentChain message. - * @param {ChildToParentChainTransactionEvent} event The event containing the data of the Chain-to-ParentChain message. + * @param {ChildToParentTransactionEvent} event The event containing the data of the Chain-to-ParentChain message. * @param {Provider} [ParentChainProvider] Optional. Used to override the Provider which is attached to `ParentChainSigner` in case you need more control. This will be a required parameter in a future major version update. */ constructor( ParentChainSigner: Signer, - event: ChildToParentChainTransactionEvent, + event: ChildToParentTransactionEvent, ParentChainProvider?: Provider ) { super(ParentChainProvider ?? ParentChainSigner.provider!, event) @@ -319,7 +314,7 @@ export class ChildToParentChainMessageWriter extends ChildToParentChainMessageRe } /** - * Executes the ChildToParentChainMessage on ParentChain. + * Executes the ChildToParentMessage on ParentChain. * Will throw an error if the outbox entry has not been created, which happens when the * corresponding assertion is confirmed. * @returns diff --git a/src/lib/message/L2ToL1MessageNitro.ts b/src/lib/message/L2ToL1MessageNitro.ts index afdf1da785..049e3701f8 100644 --- a/src/lib/message/L2ToL1MessageNitro.ts +++ b/src/lib/message/L2ToL1MessageNitro.ts @@ -32,6 +32,7 @@ import { NodeInterface__factory } from '../abi/factories/NodeInterface__factory' import { L2ToL1TxEvent as ChildToParentChainTxEvent } from '../abi/ArbSys' import { ContractTransaction, Overrides } from 'ethers' +import { Mutex } from 'async-mutex' import { EventFetcher, FetchedEvent } from '../utils/eventFetcher' import { ArbSdkError } from '../dataEntities/errors' import { @@ -64,6 +65,64 @@ const ASSERTION_CREATED_PADDING = 50 // expected number of L1 blocks that it takes for a validator to confirm an L1 block after the node deadline is passed const ASSERTION_CONFIRMED_PADDING = 20 +const l2BlockRangeCache: { [key in string]: (number | undefined)[] } = {} +const mutex = new Mutex() + +function getL2BlockRangeCacheKey({ + l2ChainId, + l1BlockNumber, +}: { + l2ChainId: number + l1BlockNumber: number +}) { + return `${l2ChainId}-${l1BlockNumber}` +} + +function setL2BlockRangeCache(key: string, value: (number | undefined)[]) { + l2BlockRangeCache[key] = value +} + +async function getBlockRangesForL1BlockWithCache({ + l1Provider, + l2Provider, + forL1Block, +}: { + l1Provider: JsonRpcProvider + l2Provider: JsonRpcProvider + forL1Block: number +}) { + const l2ChainId = (await l2Provider.getNetwork()).chainId + const key = getL2BlockRangeCacheKey({ + l2ChainId, + l1BlockNumber: forL1Block, + }) + + if (l2BlockRangeCache[key]) { + return l2BlockRangeCache[key] + } + + // implements a lock that only fetches cache once + const release = await mutex.acquire() + + // if cache has been acquired while awaiting the lock + if (l2BlockRangeCache[key]) { + release() + return l2BlockRangeCache[key] + } + + try { + const l2BlockRange = await getBlockRangesForL1Block({ + forL1Block, + provider: l1Provider, + }) + setL2BlockRangeCache(key, l2BlockRange) + } finally { + release() + } + + return l2BlockRangeCache[key] +} + /** * Base functionality for nitro L2->L1 messages */ @@ -227,9 +286,10 @@ export class ChildToParentChainMessageReaderNitro extends ChildToParentChainMess // If L1 is Arbitrum, then L2 is an Orbit chain. if (await isArbitrumChain(this.l1Provider)) { try { - const l2BlockRange = await getBlockRangesForL1Block({ + const l2BlockRange = await getBlockRangesForL1BlockWithCache({ + l1Provider: this.l1Provider as JsonRpcProvider, + l2Provider: l2Provider as JsonRpcProvider, forL1Block: createdAtBlock.toNumber(), - provider: this.l1Provider as JsonRpcProvider, }) const startBlock = l2BlockRange[0] const endBlock = l2BlockRange[1] diff --git a/src/lib/message/L2Transaction.ts b/src/lib/message/L2Transaction.ts index 470d86b075..30804921bb 100644 --- a/src/lib/message/L2Transaction.ts +++ b/src/lib/message/L2Transaction.ts @@ -25,11 +25,11 @@ import { SignerOrProvider, } from '../dataEntities/signerOrProvider' import { - ChildToParentChainMessageReader as L2ToL1MessageReader, - ChildToParentChainMessageReaderOrWriter as L2ToL1MessageReaderOrWriter, - ChildToParentChainMessage as L2ToL1Message, - ChildToParentChainMessageWriter as L2ToL1MessageWriter, - ChildToParentChainTransactionEvent as L2ToL1TransactionEvent, + ChildToParentMessageReader as L2ToL1MessageReader, + ChildToParentMessageReaderOrWriter as L2ToL1MessageReaderOrWriter, + ChildToParentMessage as L2ToL1Message, + ChildToParentMessageWriter as L2ToL1MessageWriter, + ChildToParentTransactionEvent as L2ToL1TransactionEvent, } from './L2ToL1Message' import { ArbSys__factory } from '../abi/factories/ArbSys__factory' import { ArbRetryableTx__factory } from '../abi/factories/ArbRetryableTx__factory' diff --git a/tests/integration/eth.test.ts b/tests/integration/eth.test.ts index b1c4a4dfa5..5e48ef3bfb 100644 --- a/tests/integration/eth.test.ts +++ b/tests/integration/eth.test.ts @@ -29,10 +29,10 @@ import { prettyLog, skipIfMainnet, } from './testHelpers' -import { ChildToParentChainMessage as L2ToL1Message } from '../../src/lib/message/L2ToL1Message' +import { ChildToParentMessage as L2ToL1Message } from '../../src/lib/message/L2ToL1Message' import { L2ToL1MessageStatus } from '../../src/lib/dataEntities/message' import { L2TransactionReceipt } from '../../src/lib/message/L2Transaction' -import { ParentChainToChainMessageStatus as L1ToL2MessageStatus } from '../../src/lib/message/L1ToL2Message' +import { ParentToChildMessageStatus as L1ToL2MessageStatus } from '../../src/lib/message/L1ToL2Message' import { testSetup } from '../../scripts/testSetup' dotenv.config() @@ -231,7 +231,7 @@ describe('Ether', async () => { 'eth withdraw getWithdrawalsInL2Transaction query came back empty' ).to.exist - const withdrawEvents = await L2ToL1Message.getChildToParentChainEvents( + const withdrawEvents = await L2ToL1Message.getChildToParentEvents( l2Signer.provider!, { fromBlock: withdrawEthRec.blockNumber, toBlock: 'latest' }, undefined, diff --git a/tests/unit/l1toL2MessageEvents.test.ts b/tests/unit/l1toL2MessageEvents.test.ts index 65639a76eb..8e01e4c2b9 100644 --- a/tests/unit/l1toL2MessageEvents.test.ts +++ b/tests/unit/l1toL2MessageEvents.test.ts @@ -3,7 +3,7 @@ import { BigNumber, constants, providers } from 'ethers' import { JsonRpcProvider } from '@ethersproject/providers' import { expect } from 'chai' -describe('L1toL2Message events', () => { +describe('ParentToChildMessage events', () => { it('does call for nitro events', async () => { // Receipt from mainnet tx: 0x00000a61331187be51ab9ae792d74f601a5a21fb112f5b9ac5bccb23d4d5aaba const receipt: providers.TransactionReceipt = { diff --git a/tests/unit/l2toL1MessageEvents.test.ts b/tests/unit/l2toL1MessageEvents.test.ts index 3e0ce17e10..a6c5fc9278 100644 --- a/tests/unit/l2toL1MessageEvents.test.ts +++ b/tests/unit/l2toL1MessageEvents.test.ts @@ -18,16 +18,16 @@ import { Logger, LogLevel } from '@ethersproject/logger' Logger.setLogLevel(LogLevel.ERROR) -import { L2ToL1Message as ChildToParentChainMessage } from '../../src' +import { L2ToL1Message as ChildToParentMessage } from '../../src' import { getChainNetwork as getL2Network } from '../../src/lib/dataEntities/networks' import { providers } from 'ethers' import { anything, deepEqual, instance, mock, verify, when } from 'ts-mockito' -describe('ChildToParentChainMessage events', () => { - // ChildToParentChainTransaction +describe('ChildToParentMessage events', () => { + // ChildToParentTransaction const classicTopic = '0x5baaa87db386365b5c161be377bc3d8e317e8d98d71a3ca7ed7d555340c8f767' - // ChildToParentChainTx + // ChildToParentTx const nitroTopic = '0x3e7aafa77dbf186b7fd488006beff893744caa3c4f6f299e8a709fa2087374fc' @@ -59,7 +59,7 @@ describe('ChildToParentChainMessage events', () => { const fromBlock = 0 const toBlock = 1000 - await ChildToParentChainMessage.getChildToParentChainEvents(l2Provider, { + await ChildToParentMessage.getChildToParentEvents(l2Provider, { fromBlock: fromBlock, toBlock: toBlock, }) @@ -82,7 +82,7 @@ describe('ChildToParentChainMessage events', () => { const fromBlock = l2Network.nitroGenesisBlock const toBlock = l2Network.nitroGenesisBlock + 500 - await ChildToParentChainMessage.getChildToParentChainEvents(l2Provider, { + await ChildToParentMessage.getChildToParentEvents(l2Provider, { fromBlock: fromBlock, toBlock: toBlock, }) @@ -105,7 +105,7 @@ describe('ChildToParentChainMessage events', () => { const fromBlock = 0 const toBlock = l2Network.nitroGenesisBlock + 500 - await ChildToParentChainMessage.getChildToParentChainEvents(l2Provider, { + await ChildToParentMessage.getChildToParentEvents(l2Provider, { fromBlock: fromBlock, toBlock: toBlock, }) @@ -138,7 +138,7 @@ describe('ChildToParentChainMessage events', () => { const fromBlock = 'earliest' const toBlock = 'latest' - await ChildToParentChainMessage.getChildToParentChainEvents(l2Provider, { + await ChildToParentMessage.getChildToParentEvents(l2Provider, { fromBlock: fromBlock, toBlock: toBlock, }) @@ -171,7 +171,7 @@ describe('ChildToParentChainMessage events', () => { const fromBlock = l2Network.nitroGenesisBlock + 2 const toBlock = 'latest' - await ChildToParentChainMessage.getChildToParentChainEvents(l2Provider, { + await ChildToParentMessage.getChildToParentEvents(l2Provider, { fromBlock: fromBlock, toBlock: toBlock, }) @@ -194,7 +194,7 @@ describe('ChildToParentChainMessage events', () => { const fromBlock = 'earliest' const toBlock = 'latest' - await ChildToParentChainMessage.getChildToParentChainEvents(l2Provider, { + await ChildToParentMessage.getChildToParentEvents(l2Provider, { fromBlock: fromBlock, toBlock: toBlock, }) diff --git a/yarn.lock b/yarn.lock index 0e161db0b4..dd40c14fe6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1723,6 +1723,13 @@ async-eventemitter@^0.2.4: dependencies: async "^2.4.0" +async-mutex@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.0.tgz#ae8048cd4d04ace94347507504b3cf15e631c25f" + integrity sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA== + dependencies: + tslib "^2.4.0" + async@^2.4.0: version "2.6.4" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" @@ -1987,17 +1994,17 @@ cbor@^8.0.0: nofilter "^3.1.0" chai@^4.2.0: - version "4.3.6" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.6.tgz#ffe4ba2d9fa9d6680cc0b370adae709ec9011e9c" - integrity sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q== + version "4.3.10" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384" + integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g== dependencies: assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^3.0.1" - get-func-name "^2.0.0" - loupe "^2.3.1" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" pathval "^1.1.1" - type-detect "^4.0.5" + type-detect "^4.0.8" chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.2: version "2.4.2" @@ -2016,10 +2023,12 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= +check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" cheerio-select@^2.1.0: version "2.1.0" @@ -2329,10 +2338,10 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== -deep-eql@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" - integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== +deep-eql@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== dependencies: type-detect "^4.0.0" @@ -3085,10 +3094,10 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= +get-func-name@^2.0.0, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== get-intrinsic@^1.0.2: version "1.1.1" @@ -3854,10 +3863,10 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -loupe@^2.3.1: - version "2.3.4" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.4.tgz#7e0b9bffc76f148f9be769cb1321d3dcf3cb25f3" - integrity sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ== +loupe@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" + integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== dependencies: get-func-name "^2.0.0" @@ -5190,6 +5199,11 @@ tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.4.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + tslint@^6.1.3: version "6.1.3" resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904" @@ -5245,7 +5259,7 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@^4.0.0, type-detect@^4.0.5: +type-detect@^4.0.0, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==