diff --git a/.changeset/nine-kangaroos-marry.md b/.changeset/nine-kangaroos-marry.md new file mode 100644 index 0000000000..dbb5e3ba98 --- /dev/null +++ b/.changeset/nine-kangaroos-marry.md @@ -0,0 +1,5 @@ +--- +"hardhat": patch +--- + +Fixed an issue when forking networks like Arbitrum Nitro that use non-standard transaction types (#2995, #3194). diff --git a/packages/hardhat-core/src/internal/hardhat-network/provider/fork/ForkBlockchain.ts b/packages/hardhat-core/src/internal/hardhat-network/provider/fork/ForkBlockchain.ts index d70b63d7de..745d11befe 100644 --- a/packages/hardhat-core/src/internal/hardhat-network/provider/fork/ForkBlockchain.ts +++ b/packages/hardhat-core/src/internal/hardhat-network/provider/fork/ForkBlockchain.ts @@ -24,6 +24,7 @@ import { ReadOnlyValidTransaction } from "../transactions/ReadOnlyValidTransacti import { HardhatBlockchainInterface } from "../types/HardhatBlockchainInterface"; import { ReadOnlyValidEIP1559Transaction } from "../transactions/ReadOnlyValidEIP1559Transaction"; +import { ReadOnlyValidUnknownTypeTransaction } from "../transactions/ReadOnlyValidUnknownTypeTransaction"; import { rpcToBlockData } from "./rpcToBlockData"; import { rpcToTxData } from "./rpcToTxData"; @@ -297,9 +298,20 @@ export class ForkBlockchain rpcToTxData(transaction) as FeeMarketEIP1559TxData ); } else { - throw new InternalError( - `Unknown transaction type ${transaction.type.toString()}` - ); + // we try to interpret unknown txs as legacy transactions, to support + // networks like Arbitrum that have non-standards tx types + try { + tx = new ReadOnlyValidUnknownTypeTransaction( + new Address(transaction.from), + Number(transaction.type), + rpcToTxData(transaction) + ); + } catch (e: any) { + throw new InternalError( + `Could not process transaction with type ${transaction.type.toString()}`, + e + ); + } } block.transactions.push(tx); diff --git a/packages/hardhat-core/src/internal/hardhat-network/provider/transactions/ReadOnlyValidUnknownTypeTransaction.ts b/packages/hardhat-core/src/internal/hardhat-network/provider/transactions/ReadOnlyValidUnknownTypeTransaction.ts new file mode 100644 index 0000000000..38c75c556e --- /dev/null +++ b/packages/hardhat-core/src/internal/hardhat-network/provider/transactions/ReadOnlyValidUnknownTypeTransaction.ts @@ -0,0 +1,162 @@ +import { Common } from "@nomicfoundation/ethereumjs-common"; +import { Transaction, TxData, TxOptions } from "@nomicfoundation/ethereumjs-tx"; +import { Address } from "@nomicfoundation/ethereumjs-util"; + +import { InternalError } from "../../../core/providers/errors"; + +/* eslint-disable @nomiclabs/hardhat-internal-rules/only-hardhat-error */ + +/** + * This class is like `ReadOnlyValidTransaction` but for + * a transaction with an unknown tx type. + */ +export class ReadOnlyValidUnknownTypeTransaction extends Transaction { + public static fromTxData(_txData: TxData, _opts?: TxOptions): never { + throw new InternalError( + "`fromTxData` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + } + + public static fromSerializedTx( + _serialized: Buffer, + _opts?: TxOptions + ): never { + throw new InternalError( + "`fromSerializedTx` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + } + + public static fromRlpSerializedTx( + _serialized: Buffer, + _opts?: TxOptions + ): never { + throw new InternalError( + "`fromRlpSerializedTx` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + } + + public static fromValuesArray(_values: Buffer[], _opts?: TxOptions): never { + throw new InternalError( + "`fromRlpSerializedTx` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + } + + public readonly common: Common; + + private readonly _sender: Address; + private readonly _actualType: number; + + constructor(sender: Address, type: number, data: TxData = {}) { + super(data, { freeze: false }); + + this.common = this._getCommon(); + this._sender = sender; + this._actualType = type; + } + + public get type(): number { + return this._actualType; + } + + public verifySignature(): boolean { + return true; + } + + public getSenderAddress(): Address { + return this._sender; + } + + public sign(): never { + throw new InternalError( + "`sign` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + } + + public getDataFee(): never { + throw new InternalError( + "`getDataFee` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + } + + public getBaseFee(): never { + throw new InternalError( + "`getBaseFee` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + } + + public getUpfrontCost(): never { + throw new InternalError( + "`getUpfrontCost` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + } + + public validate(_stringError?: false): never; + public validate(_stringError: true): never; + public validate(_stringError: boolean = false): never { + throw new InternalError( + "`validate` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + } + + public toCreationAddress(): never { + throw new InternalError( + "`toCreationAddress` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + } + + public getSenderPublicKey(): never { + throw new InternalError( + "`getSenderPublicKey` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + } + + public getMessageToVerifySignature(): never { + throw new InternalError( + "`getMessageToVerifySignature` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + } + + public getMessageToSign(): never { + throw new InternalError( + "`getMessageToSign` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + } +} + +// Override private methods + +const ReadOnlyValidUnknownTypeTransactionPrototype: any = + ReadOnlyValidUnknownTypeTransaction.prototype; + +ReadOnlyValidUnknownTypeTransactionPrototype._validateTxV = function ( + _v: any, + common: any +) { + return this._getCommon(common); +}; + +ReadOnlyValidUnknownTypeTransactionPrototype._signedTxImplementsEIP155 = + function () { + throw new InternalError( + "`_signedTxImplementsEIP155` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + }; + +ReadOnlyValidUnknownTypeTransactionPrototype._unsignedTxImplementsEIP155 = + function () { + throw new InternalError( + "`_unsignedTxImplementsEIP155` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); + }; + +ReadOnlyValidUnknownTypeTransactionPrototype._getMessageToSign = function () { + throw new InternalError( + "`_getMessageToSign` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); +}; + +ReadOnlyValidUnknownTypeTransactionPrototype._processSignature = function () { + throw new InternalError( + "`_processSignature` is not implemented in ReadOnlyValidUnknownTypeTransaction" + ); +};