> ## Documentation Index
> Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Transaction Utilities

> Helper methods for gas, access lists, formatting, and more

<Card title="Try it Live" icon="play" href="https://playground.tevm.sh?example=primitives/transaction.ts">
  Run Transaction examples in the interactive playground
</Card>

# Transaction Utilities

Utility methods for working with transactions across all types.

## format

Format transaction to human-readable string.

```typescript theme={null}
function format(tx: Any): string
```

### Usage

```typescript theme={null}
import { format } from 'tevm/Transaction'

const tx: Transaction.EIP1559 = {
  type: Transaction.Type.EIP1559,
  nonce: 5n,
  to: recipientAddress,
  value: 1500000000000000000n,
  // ...
}

const formatted = format(tx)
// "EIP-1559 tx to 0x742d35Cc..., value: 1.5 ETH, nonce: 5"

// Contract creation
const deploy: Transaction.Legacy = {
  type: Transaction.Type.Legacy,
  nonce: 0n,
  to: null,
  value: 0n,
  // ...
}

format(deploy)
// "Legacy tx contract creation, value: 0 ETH, nonce: 0"
```

Source: [format.ts:6-20](https://github.com/evmts/voltaire/blob/main/src/primitives/Transaction/format.ts#L6-L20)

## getGasPrice

Get transaction gas price (handles all types).

```typescript theme={null}
function getGasPrice(tx: Any, baseFee?: bigint): bigint
```

### Parameters

* `tx: Any` - Any transaction type
* `baseFee?: bigint` - Required for EIP-1559+ transactions

### Returns

`bigint` - Gas price in wei

### Throws

* `Error("baseFee required for EIP-1559+ transactions")` - If baseFee missing for dynamic fee transactions

### Usage

```typescript theme={null}
import { getGasPrice } from 'tevm/Transaction'

// Legacy/EIP-2930: Returns fixed gasPrice
const legacyTx: Transaction.Legacy = {
  type: Transaction.Type.Legacy,
  gasPrice: 20000000000n,
  // ...
}
getGasPrice(legacyTx)  // 20000000000n

// EIP-1559+: Calculates effective gas price
const eip1559Tx: Transaction.EIP1559 = {
  type: Transaction.Type.EIP1559,
  maxPriorityFeePerGas: 2000000000n,
  maxFeePerGas: 30000000000n,
  // ...
}
const baseFee = 15000000000n
getGasPrice(eip1559Tx, baseFee)  // 17000000000n (15 + 2)

// Missing baseFee throws
try {
  getGasPrice(eip1559Tx)  // Error!
} catch (e) {
  console.error(e.message)  // "baseFee required for EIP-1559+ transactions"
}
```

Source: [getGasPrice.ts:16-38](https://github.com/evmts/voltaire/blob/main/src/primitives/Transaction/getGasPrice.ts#L16-L38)

## hasAccessList

Check if transaction supports access lists.

```typescript theme={null}
function hasAccessList(tx: Any): boolean
```

### Returns

* `true` - For EIP-2930, EIP-1559, EIP-4844, EIP-7702
* `false` - For Legacy

### Usage

```typescript theme={null}
import { hasAccessList, getAccessList } from 'tevm/Transaction'

const tx: Transaction.Any = /* ... */

if (hasAccessList(tx)) {
  const accessList = getAccessList(tx)
  console.log('Access list:', accessList)
} else {
  console.log('No access list support')
}
```

Source: [hasAccessList.ts:7-9](https://github.com/evmts/voltaire/blob/main/src/primitives/Transaction/hasAccessList.ts#L7-L9)

## getAccessList

Get transaction access list.

```typescript theme={null}
function getAccessList(tx: Any): AccessList
```

### Returns

* `AccessList` - Transaction access list
* `[]` - Empty array for Legacy transactions

### Usage

```typescript theme={null}
import { getAccessList } from 'tevm/Transaction'

// EIP-1559 with access list
const eip1559: Transaction.EIP1559 = {
  type: Transaction.Type.EIP1559,
  accessList: [
    { address: addr1, storageKeys: [key1, key2] }
  ],
  // ...
}
getAccessList(eip1559)
// [{ address: addr1, storageKeys: [key1, key2] }]

// Legacy returns empty
const legacy: Transaction.Legacy = { /* ... */ }
getAccessList(legacy)  // []
```

Source: [getAccessList.ts:7-12](https://github.com/evmts/voltaire/blob/main/src/primitives/Transaction/getAccessList.ts#L7-L12)

## getChainId

Get transaction chain ID.

```typescript theme={null}
function getChainId(tx: Any): bigint | null
```

### Returns

* `bigint` - Chain ID
* `null` - For pre-EIP-155 Legacy transactions

### Usage

```typescript theme={null}
import { getChainId } from 'tevm/Transaction'

// EIP-1559 (explicit chainId field)
const eip1559: Transaction.EIP1559 = {
  type: Transaction.Type.EIP1559,
  chainId: 1n,
  // ...
}
getChainId(eip1559)  // 1n

// Legacy with EIP-155
const legacy: Transaction.Legacy = {
  type: Transaction.Type.Legacy,
  v: 37n,  // chainId = (37 - 35) / 2 = 1
  // ...
}
getChainId(legacy)  // 1n

// Pre-EIP-155 Legacy
const oldLegacy: Transaction.Legacy = {
  type: Transaction.Type.Legacy,
  v: 27n,
  // ...
}
getChainId(oldLegacy)  // null
```

Source: [getChainId.ts:8-13](https://github.com/evmts/voltaire/blob/main/src/primitives/Transaction/getChainId.ts#L8-L13)

## isSigned

Check if transaction has a signature.

```typescript theme={null}
function isSigned(tx: Any): boolean
```

### Returns

* `true` - If transaction has non-zero r and s
* `false` - If r or s is all zeros

### Usage

```typescript theme={null}
import { isSigned, getSender } from 'tevm/Transaction'

const signedTx: Transaction.EIP1559 = {
  type: Transaction.Type.EIP1559,
  r: Bytes32().fill(1),
  s: Bytes32().fill(2),
  // ...
}
isSigned(signedTx)  // true

const unsignedTx: Transaction.EIP1559 = {
  type: Transaction.Type.EIP1559,
  r: Bytes32(),  // All zeros
  s: Bytes32(),  // All zeros
  // ...
}
isSigned(unsignedTx)  // false

// Use for conditional logic
if (isSigned(tx)) {
  const sender = getSender(tx)
  console.log('Signed by:', sender)
}
```

Source: [isSigned.ts:7-14](https://github.com/evmts/voltaire/blob/main/src/primitives/Transaction/isSigned.ts#L7-L14)

## assertSigned

Assert transaction is signed (throws if not).

```typescript theme={null}
function assertSigned(tx: Any): void
```

### Throws

* `Error("Transaction is not signed")` - If r or s is all zeros

### Usage

```typescript theme={null}
import { assertSigned, getSender } from 'tevm/Transaction'

try {
  assertSigned(tx)
  // Transaction is signed, safe to get sender
  const sender = getSender(tx)
  processTransaction(tx, sender)
} catch (e) {
  console.error('Cannot process unsigned transaction')
  return
}
```

Source: [assertSigned.ts:6-13](https://github.com/evmts/voltaire/blob/main/src/primitives/Transaction/assertSigned.ts#L6-L13)

## Usage Patterns

### Transaction Cost Calculation

```typescript theme={null}
import { getGasPrice } from 'tevm/Transaction'

function calculateMaxCost(
  tx: Transaction.Any,
  baseFee?: bigint
): bigint {
  const gasPrice = getGasPrice(tx, baseFee)
  return tx.gasLimit * gasPrice + tx.value
}

// Usage
const tx: Transaction.EIP1559 = {
  type: Transaction.Type.EIP1559,
  maxPriorityFeePerGas: 2000000000n,
  maxFeePerGas: 30000000000n,
  gasLimit: 21000n,
  value: 1000000000000000000n,
  // ...
}

const maxCost = calculateMaxCost(tx, 15000000000n)
// gasLimit * effectiveGasPrice + value
// 21000 * 17000000000 + 1000000000000000000
```

### Replay Protection Check

```typescript theme={null}
import { getChainId } from 'tevm/Transaction'
import { TransactionError } from 'tevm/errors'

function validateChainId(
  tx: Transaction.Any,
  expectedChainId: bigint
): void {
  const txChainId = getChainId(tx)

  if (txChainId === null) {
    throw new TransactionError('Transaction has no chain ID (pre-EIP-155)', {
      code: 'MISSING_CHAIN_ID',
      context: { txType: tx.type }
    })
  }

  if (txChainId !== expectedChainId) {
    throw new TransactionError(
      `Wrong chain: expected ${expectedChainId}, got ${txChainId}`,
      {
        code: 'WRONG_CHAIN_ID',
        context: { expected: expectedChainId, actual: txChainId }
      }
    )
  }
}
```

### Access List Extraction

```typescript theme={null}
import { hasAccessList, getAccessList } from 'tevm/Transaction'

function extractAddresses(tx: Transaction.Any): Set<AddressType> {
  const addresses = new Set<AddressType>()

  // Add sender
  if (isSigned(tx)) {
    addresses.add(getSender(tx))
  }

  // Add recipient
  if (tx.to) {
    addresses.add(tx.to)
  }

  // Add access list addresses
  if (hasAccessList(tx)) {
    const accessList = getAccessList(tx)
    for (const item of accessList) {
      addresses.add(item.address)
    }
  }

  return addresses
}
```

### Transaction Display

```typescript theme={null}
import { format, getGasPrice, getChainId } from 'tevm/Transaction'

function displayTransaction(
  tx: Transaction.Any,
  baseFee?: bigint
): void {
  console.log('Transaction Details')
  console.log('-------------------')
  console.log(format(tx))
  console.log('Chain ID:', getChainId(tx) || 'N/A')

  try {
    const gasPrice = getGasPrice(tx, baseFee)
    console.log('Gas Price:', gasPrice, 'wei')
  } catch {
    console.log('Gas Price: Requires base fee')
  }

  console.log('Signed:', isSigned(tx) ? 'Yes' : 'No')

  if (hasAccessList(tx)) {
    const accessList = getAccessList(tx)
    console.log('Access List Items:', accessList.length)
  }
}
```

### Signature Validation

```typescript theme={null}
import { isSigned, assertSigned, verifySignature, getSender } from 'tevm/Transaction'

function validateTransaction(
  tx: Transaction.Any,
  expectedSender?: AddressType
): { valid: boolean; sender?: AddressType; error?: string } {
  // Check if signed
  if (!isSigned(tx)) {
    return { valid: false, error: 'Transaction not signed' }
  }

  // Verify signature
  if (!verifySignature(tx)) {
    return { valid: false, error: 'Invalid signature' }
  }

  // Get sender
  const sender = getSender(tx)

  // Check expected sender if provided
  if (expectedSender && !Address.equals(sender, expectedSender)) {
    return {
      valid: false,
      error: `Wrong sender: expected ${Address.toHex(expectedSender)}, got ${Address.toHex(sender)}`
    }
  }

  return { valid: true, sender }
}
```

### Transaction Pool Entry

```typescript theme={null}
import {
  isSigned,
  getSender,
  getChainId,
  getGasPrice,
  hash
} from 'tevm/Transaction'

interface PoolEntry {
  tx: Transaction.Any
  hash: HashType
  sender: AddressType
  gasPrice: bigint
  timestamp: number
}

function createPoolEntry(
  tx: Transaction.Any,
  baseFee?: bigint
): PoolEntry {
  assertSigned(tx)

  return {
    tx,
    hash: hash(tx),
    sender: getSender(tx),
    gasPrice: getGasPrice(tx, baseFee),
    timestamp: Date.now()
  }
}
```

## Performance Tips

1. **Cache getSender results** - ECDSA recovery is expensive
2. **Batch getGasPrice calls** - Reuse baseFee for multiple transactions
3. **Check isSigned before getSender** - Avoid unnecessary recovery attempts
4. **Use hasAccessList before getAccessList** - Skip unnecessary checks

```typescript theme={null}
// Bad: Redundant checks
if (tx.type !== Transaction.Type.Legacy) {
  const accessList = getAccessList(tx)
}

// Good: Single check
if (hasAccessList(tx)) {
  const accessList = getAccessList(tx)
}
```
