Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/tx/src/capabilities/eip2718.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,31 @@ import { errorMsg } from './legacy.ts'
import type { Input } from '@ethereumjs/rlp'
import type { EIP2718CompatibleTx } from '../types.ts'

/**
* Gets the hashed message to sign for EIP-2718 transactions
* @param tx - The EIP-2718 compatible transaction
* @returns Hashed message to sign
*/
export function getHashedMessageToSign(tx: EIP2718CompatibleTx): Uint8Array {
const keccakFunction = tx.common.customCrypto.keccak256 ?? keccak256
return keccakFunction(tx.getMessageToSign())
}

/**
* Serializes an EIP-2718 transaction
* @param tx - The EIP-2718 compatible transaction
* @param base - Optional base input for RLP encoding
* @returns Serialized transaction bytes
*/
export function serialize(tx: EIP2718CompatibleTx, base?: Input): Uint8Array {
return concatBytes(txTypeBytes(tx.type), RLP.encode(base ?? tx.raw()))
}

/**
* Validates the y-parity value of an EIP-2718 transaction
* @param tx - The EIP-2718 compatible transaction
* @throws EthereumJSErrorWithoutCode if y-parity is invalid
*/
export function validateYParity(tx: EIP2718CompatibleTx) {
const { v } = tx
if (v !== undefined && v !== BIGINT_0 && v !== BIGINT_1) {
Expand Down
11 changes: 11 additions & 0 deletions packages/tx/src/capabilities/legacy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,21 @@ import { secp256k1 } from 'ethereum-cryptography/secp256k1'
import type { LegacyTx } from '../legacy/tx.ts'
import type { LegacyTxInterface, Transaction } from '../types.ts'

/**
* Creates an error message with transaction context
* @param tx - The transaction interface
* @param msg - The error message
* @returns Formatted error message with transaction context
*/
export function errorMsg(tx: LegacyTxInterface, msg: string) {
return `${msg} (${tx.errorStr()})`
}

/**
* Checks if a transaction is signed
* @param tx - The transaction interface
* @returns true if the transaction is signed
*/
export function isSigned(tx: LegacyTxInterface): boolean {
const { v, r, s } = tx
if (v === undefined || r === undefined || s === undefined) {
Expand Down
60 changes: 60 additions & 0 deletions packages/tx/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ export interface TxOptions {
allowUnlimitedInitCodeSize?: boolean
}

/**
* Type guard to check if input is AccessListBytes format
* @param input - The input to check
* @returns true if input is AccessListBytes format
*/
export function isAccessListBytes(input: AccessListBytes | AccessList): input is AccessListBytes {
if (input.length === 0) {
return true
Expand All @@ -116,6 +121,11 @@ export function isAccessListBytes(input: AccessListBytes | AccessList): input is
return false
}

/**
* Type guard to check if input is AccessList format
* @param input - The input to check
* @returns true if input is AccessList format
*/
export function isAccessList(input: AccessListBytes | AccessList): input is AccessList {
return !isAccessListBytes(input) // This is exactly the same method, except the output is negated.
}
Expand Down Expand Up @@ -153,22 +163,47 @@ export interface Transaction {

export type TypedTransaction = Transaction[TransactionType]

/**
* Type guard to check if transaction is a Legacy transaction
* @param tx - The transaction to check
* @returns true if transaction is Legacy type
*/
export function isLegacyTx(tx: TypedTransaction): tx is LegacyTx {
return tx.type === TransactionType.Legacy
}

/**
* Type guard to check if transaction is an AccessList EIP-2930 transaction
* @param tx - The transaction to check
* @returns true if transaction is AccessList EIP-2930 type
*/
export function isAccessList2930Tx(tx: TypedTransaction): tx is AccessList2930Tx {
return tx.type === TransactionType.AccessListEIP2930
}

/**
* Type guard to check if transaction is a Fee Market EIP-1559 transaction
* @param tx - The transaction to check
* @returns true if transaction is Fee Market EIP-1559 type
*/
export function isFeeMarket1559Tx(tx: TypedTransaction): tx is FeeMarket1559Tx {
return tx.type === TransactionType.FeeMarketEIP1559
}

/**
* Type guard to check if transaction is a Blob EIP-4844 transaction
* @param tx - The transaction to check
* @returns true if transaction is Blob EIP-4844 type
*/
export function isBlob4844Tx(tx: TypedTransaction): tx is Blob4844Tx {
return tx.type === TransactionType.BlobEIP4844
}

/**
* Type guard to check if transaction is an EOA Code EIP-7702 transaction
* @param tx - The transaction to check
* @returns true if transaction is EOA Code EIP-7702 type
*/
export function isEOACode7702Tx(tx: TypedTransaction): tx is EOACode7702Tx {
return tx.type === TransactionType.EOACodeEIP7702
}
Expand Down Expand Up @@ -262,26 +297,51 @@ export interface TxData {

export type TypedTxData = TxData[TransactionType]

/**
* Type guard to check if transaction data is Legacy transaction data
* @param txData - The transaction data to check
* @returns true if transaction data is Legacy type
*/
export function isLegacyTxData(txData: TypedTxData): txData is LegacyTxData {
const txType = Number(bytesToBigInt(toBytes(txData.type)))
return txType === TransactionType.Legacy
}

/**
* Type guard to check if transaction data is AccessList EIP-2930 transaction data
* @param txData - The transaction data to check
* @returns true if transaction data is AccessList EIP-2930 type
*/
export function isAccessList2930TxData(txData: TypedTxData): txData is AccessList2930TxData {
const txType = Number(bytesToBigInt(toBytes(txData.type)))
return txType === TransactionType.AccessListEIP2930
}

/**
* Type guard to check if transaction data is Fee Market EIP-1559 transaction data
* @param txData - The transaction data to check
* @returns true if transaction data is Fee Market EIP-1559 type
*/
export function isFeeMarket1559TxData(txData: TypedTxData): txData is FeeMarketEIP1559TxData {
const txType = Number(bytesToBigInt(toBytes(txData.type)))
return txType === TransactionType.FeeMarketEIP1559
}

/**
* Type guard to check if transaction data is Blob EIP-4844 transaction data
* @param txData - The transaction data to check
* @returns true if transaction data is Blob EIP-4844 type
*/
export function isBlob4844TxData(txData: TypedTxData): txData is BlobEIP4844TxData {
const txType = Number(bytesToBigInt(toBytes(txData.type)))
return txType === TransactionType.BlobEIP4844
}

/**
* Type guard to check if transaction data is EOA Code EIP-7702 transaction data
* @param txData - The transaction data to check
* @returns true if transaction data is EOA Code EIP-7702 type
*/
export function isEOACode7702TxData(txData: TypedTxData): txData is EOACode7702TxData {
const txType = Number(bytesToBigInt(toBytes(txData.type)))
return txType === TransactionType.EOACodeEIP7702
Expand Down
30 changes: 27 additions & 3 deletions packages/tx/src/util/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,29 @@ import { paramsTx } from '../params.ts'

import type { TransactionInterface, TransactionType, TxData, TxOptions } from '../types.ts'

/**
* Gets a Common instance, creating a new one if none provided
* @param common - Optional Common instance
* @returns Common instance (copied if provided, new Mainnet instance if not)
*/
export function getCommon(common?: Common): Common {
return common?.copy() ?? new Common({ chain: Mainnet })
}

/**
* Converts a transaction type to its byte representation
* @param txType - The transaction type
* @returns Uint8Array representation of the transaction type
*/
export function txTypeBytes(txType: TransactionType): Uint8Array {
return hexToBytes(`0x${txType.toString(16).padStart(2, '0')}`)
}

/**
* Validates that transaction data fields are not arrays
* @param values - Object containing transaction data fields
* @throws EthereumJSErrorWithoutCode if any transaction field is an array
*/
export function validateNotArray(values: { [key: string]: any }) {
const txDataKeys = [
'nonce',
Expand Down Expand Up @@ -114,9 +129,13 @@ type Mutable<T> = {
-readonly [P in keyof T]: T[P]
}

// This is (temp) a shared method which reflects `super` logic which were called from all txs and thus
// represents the constructor of baseTransaction
// Note: have to use `Mutable` to write to readonly props. Only call this in constructor of txs.
/**
* Shared constructor logic for all transaction types
* Note: Uses Mutable type to write to readonly properties. Only call this in transaction constructors.
* @param tx - Mutable transaction interface to initialize
* @param txData - Transaction data
* @param opts - Transaction options
*/
export function sharedConstructor(
tx: Mutable<TransactionInterface>,
txData: TxData[TransactionType],
Expand Down Expand Up @@ -180,6 +199,11 @@ export function sharedConstructor(
}
}

/**
* Converts a transaction to its base JSON representation
* @param tx - The transaction interface
* @returns JSON object with base transaction fields
*/
export function getBaseJSON(tx: TransactionInterface) {
return {
type: bigIntToHex(BigInt(tx.type)),
Expand Down
Loading