/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see .
*/
import { Numbers } from 'web3-types';
import { isUint8Array } from 'web3-utils';
import { toUint8Array, uint8ArrayToBigInt } from '../common/utils.js';
import { FeeMarketEIP1559Transaction } from './eip1559Transaction.js';
import { AccessListEIP2930Transaction } from './eip2930Transaction.js';
import { Transaction } from './legacyTransaction.js';
import type { TypedTransaction } from '../types.js';
import type {
AccessListEIP2930TxData,
FeeMarketEIP1559TxData,
TxData,
TxOptions,
} from './types.js';
import { BaseTransaction } from './baseTransaction.js';
const extraTxTypes: Map> = new Map();
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class TransactionFactory {
// It is not possible to instantiate a TransactionFactory object.
// eslint-disable-next-line @typescript-eslint/no-empty-function, no-useless-constructor
private constructor() {}
public static typeToInt(txType: Numbers) {
return Number(uint8ArrayToBigInt(toUint8Array(txType)));
}
public static registerTransactionType>(
type: Numbers,
txClass: NewTxTypeClass,
) {
const txType = TransactionFactory.typeToInt(type);
extraTxTypes.set(txType, txClass);
}
/**
* Create a transaction from a `txData` object
*
* @param txData - The transaction data. The `type` field will determine which transaction type is returned (if undefined, creates a legacy transaction)
* @param txOptions - Options to pass on to the constructor of the transaction
*/
public static fromTxData(
txData: TxData | TypedTransaction,
txOptions: TxOptions = {},
): TypedTransaction {
if (!('type' in txData) || txData.type === undefined) {
// Assume legacy transaction
return Transaction.fromTxData(txData as TxData, txOptions);
}
const txType = TransactionFactory.typeToInt(txData.type);
if (txType === 0) {
return Transaction.fromTxData(txData as TxData, txOptions);
}
if (txType === 1) {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return AccessListEIP2930Transaction.fromTxData(
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
txData,
txOptions,
);
}
if (txType === 2) {
return FeeMarketEIP1559Transaction.fromTxData(
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
txData,
txOptions,
);
}
const ExtraTransaction = extraTxTypes.get(txType);
if (ExtraTransaction?.fromTxData) {
return ExtraTransaction.fromTxData(txData, txOptions) as TypedTransaction;
}
throw new Error(`Tx instantiation with type ${txType} not supported`);
}
/**
* This method tries to decode serialized data.
*
* @param data - The data Uint8Array
* @param txOptions - The transaction options
*/
public static fromSerializedData(
data: Uint8Array,
txOptions: TxOptions = {},
): TypedTransaction {
if (data[0] <= 0x7f) {
// Determine the type.
switch (data[0]) {
case 1:
return AccessListEIP2930Transaction.fromSerializedTx(data, txOptions);
case 2:
return FeeMarketEIP1559Transaction.fromSerializedTx(data, txOptions);
default: {
const ExtraTransaction = extraTxTypes.get(Number(data[0]));
if (ExtraTransaction?.fromSerializedTx) {
return ExtraTransaction.fromSerializedTx(
data,
txOptions,
) as TypedTransaction;
}
throw new Error(`TypedTransaction with ID ${data[0]} unknown`);
}
}
} else {
return Transaction.fromSerializedTx(data, txOptions);
}
}
/**
* When decoding a BlockBody, in the transactions field, a field is either:
* A Uint8Array (a TypedTransaction - encoded as TransactionType || rlp(TransactionPayload))
* A Uint8Array[] (Legacy Transaction)
* This method returns the right transaction.
*
* @param data - A Uint8Array or Uint8Array[]
* @param txOptions - The transaction options
*/
public static fromBlockBodyData(data: Uint8Array | Uint8Array[], txOptions: TxOptions = {}) {
if (isUint8Array(data)) {
return this.fromSerializedData(data , txOptions);
}
if (Array.isArray(data)) {
// It is a legacy transaction
return Transaction.fromValuesArray(data, txOptions);
}
throw new Error('Cannot decode transaction: unknown type input');
}
}