import { Data , JsonRpcErrorCode , Quantity , RPCQUANTITY_GWEI } from "@ganache/utils" ; import type Common from "@ethereumjs/common" ; import { LegacyTransaction } from "./legacy-transaction" ; import { EIP2930AccessListTransaction } from "./eip2930-access-list-transaction" ; import { TypedRpcTransaction } from "./rpc-transaction" ; import { EIP1559FeeMarketDatabasePayload , EIP2930AccessListDatabasePayload , GanacheRawExtraTx , LegacyDatabasePayload , TypedDatabasePayload , TypedDatabaseTransaction } from "./raw" ; import { decode } from "@ganache/rlp" ; import { CodedError } from "@ganache/ethereum-utils" ; import { TypedTransaction } from "./transaction-types" ; import { EIP1559FeeMarketTransaction } from "./eip1559-fee-market-transaction" ; const UNTYPED_TX_START_BYTE = 0xc0 ; export enum TransactionType { Legacy = 0x0 , EIP2930AccessList = 0x1 , EIP1559AccessList = 0x2 } export class TransactionFactory { public tx : TypedTransaction ; constructor ( raw , common ) { const [ txData , extra ] = ( decode ( raw ) as any ) as [ TypedDatabaseTransaction , GanacheRawExtraTx ] ; this . tx = TransactionFactory . fromDatabaseTx ( txData , common , extra ) ; } private static _fromData ( txData , txType , common , extra ? ) { if ( ! common . isActivatedEIP ( 2718 ) ) { return LegacyTransaction . fromTxData ( < LegacyDatabasePayload | TypedRpcTransaction > txData , common , extra ) ; } else if ( ! common . isActivatedEIP ( 1559 ) ) { if ( txType === TransactionType . Legacy ) { return LegacyTransaction . fromTxData ( < TypedRpcTransaction > txData , common , extra ) ; } else if ( txType === TransactionType . EIP2930AccessList ) { if ( common . isActivatedEIP ( 2930 ) ) { return EIP2930AccessListTransaction . fromTxData ( < EIP2930AccessListDatabasePayload | TypedRpcTransaction > txData , common , extra ) ; } else { throw new CodedError ( `EIP 2930 is not activated.` , JsonRpcErrorCode . INVALID_PARAMS ) ; } } else if ( txType === TransactionType . EIP1559AccessList ) { throw new CodedError ( `EIP 1559 is not activated.` , JsonRpcErrorCode . INVALID_PARAMS ) ; } } else { if ( Array . isArray ( txData ) ) { if ( txType === TransactionType . Legacy ) { return LegacyTransaction . fromTxData ( < LegacyDatabasePayload > txData , common , extra ) ; } else if ( txType === TransactionType . EIP2930AccessList ) { return EIP2930AccessListTransaction . fromTxData ( < EIP2930AccessListDatabasePayload > txData , common , extra ) ; } else if ( txType === TransactionType . EIP1559AccessList ) { return EIP1559FeeMarketTransaction . fromTxData ( < EIP1559FeeMarketDatabasePayload > txData , common , extra ) ; } } else { const toEIP1559 = ( txType === TransactionType . Legacy || txType === TransactionType . EIP2930AccessList ) && txData . gasPrice === undefined ; if ( txType === TransactionType . EIP1559AccessList || toEIP1559 ) { const tx = EIP1559FeeMarketTransaction . fromTxData ( txData , common , extra ) ; if ( toEIP1559 ) { if ( ! txData . maxFeePerGas ) { tx . maxFeePerGas = Quantity . from ( null ) ; } if ( ! txData . maxPriorityFeePerGas ) { tx . maxPriorityFeePerGas = RPCQUANTITY_GWEI ; } } return tx ; } else if ( txType === TransactionType . Legacy ) { return LegacyTransaction . fromTxData ( txData , common , extra ) ; } else if ( txType === TransactionType . EIP2930AccessList ) { if ( txData . accessList === undefined ) { return LegacyTransaction . fromTxData ( txData , common , extra ) ; } else { return EIP2930AccessListTransaction . fromTxData ( txData , common , extra ) ; } } } } throw new CodedError ( `Tx instantiation with supplied type not supported` , JsonRpcErrorCode . METHOD_NOT_FOUND ) ; } param txData param common public static fromRpc ( txData , common , extra ? ) { const txType = this . typeOfRPC ( txData ) ; return this . _fromData ( txData , txType , common , extra ) ; } param txData param common public static fromDatabaseTx ( txData , common , extra ? ) { const txType = this . typeOfRaw ( txData ) ; switch ( txType ) { case TransactionType . EIP1559AccessList : return EIP1559FeeMarketTransaction . fromTxData ( txData . slice ( 1 ) as EIP1559FeeMarketDatabasePayload , common , extra ) ; case TransactionType . Legacy : return LegacyTransaction . fromTxData ( txData as LegacyDatabasePayload , common , extra ) ; case TransactionType . EIP2930AccessList : return EIP2930AccessListTransaction . fromTxData ( txData . slice ( 1 ) as EIP2930AccessListDatabasePayload , common , extra ) ; default : throw new CodedError ( `Transactions with supplied type ${ txType } not supported` , JsonRpcErrorCode . METHOD_NOT_FOUND ) ; } } param txData param common public static fromString ( txData , common ) { let data = Data . from ( txData ) . toBuffer ( ) ; const type = data [ 0 ] ; const txType = this . typeOf ( type ) ; if ( common . isActivatedEIP ( 2718 ) ) { let raw ; try { raw = decode < TypedDatabasePayload > ( txType === TransactionType . Legacy ? data : data . slice ( 1 ) ) ; } catch ( e ) { throw new Error ( "Could not decode transaction: " + e . message ) ; } return this . _fromData ( raw , txType , common ) ; } else { let raw ; try { raw = decode < LegacyDatabasePayload > ( data ) ; } catch ( e ) { throw new Error ( "Could not decode transaction: " + e . message ) ; } return this . _fromData ( raw , TransactionType . Legacy , common ) ; } } private static typeOf ( type ) { if ( type === TransactionType . EIP1559AccessList || type === TransactionType . EIP2930AccessList ) { return type ; } else if ( type >= UNTYPED_TX_START_BYTE || type === TransactionType . Legacy || type === undefined ) { return TransactionType . Legacy ; } else { throw new Error ( `Invalid transaction type: ${ type }` ) ; } } public static typeOfRaw ( raw ) { if ( raw . length === 9 ) { return TransactionType . Legacy ; } const type = raw [ 0 ] [ 0 ] ; return this . typeOf ( type ) ; } public static typeOfRPC ( rpc ) { if ( ! ( "type" in rpc ) || rpc . type === undefined ) { return TransactionType . Legacy ; } else { const txType = parseInt ( rpc . type , 16 ) ; return this . typeOf ( txType ) ; } } }