# Transaction Lifecycle

This page walks through what happens to a transaction from the moment you hit "send" to when it's permanently finalized on-chain. Understanding this flow is helpful for debugging, optimizing, and building reliable dApps.

## Overview

```
User signs transaction (ML-DSA44)
        │
        ▼
Transaction submitted via JSON-RPC
        │
        ▼
Node validates and adds to mempool
        │
        ▼
Validator includes in DAG event
        │
        ▼
Event gossiped to network
        │
        ▼
Lachesis determines event order
        │
        ▼
Event transactions assembled into block
        │
        ▼
EVM executes transactions
        │
        ▼
Block finalized (INSTANT - no rollbacks)
        │
        ▼
Transaction receipt available via RPC
```

## Phase 1: Transaction Creation

### Constructing the Transaction

A user constructs a transaction with these fields:

```javascript
{
  type: 3,                        // PQC transaction type (required)
  chainId: 55,                    // Armchain chain ID
  nonce: 42,                      // Sender's transaction count
  to: "0x742d35Cc...",           // Recipient address
  value: "1000000000000000000",   // Amount in wei (1 ARM)
  data: "0x",                     // Call data (empty for transfers)
  gasLimit: 21000,                // Maximum gas units
  maxFeePerGas: "20000000000",    // Max total fee per gas (20 Gwei)
  maxPriorityFeePerGas: "2000000000"  // Priority fee (2 Gwei)
}
```

### Signing with ML-DSA44

The transaction is signed using the sender's ML-DSA44 private key:

1. Serialize the transaction fields using RLP encoding
2. Compute the signing hash: `keccak256(0x03 || RLP(fields))`
3. Sign the hash with the ML-DSA44 private key
4. Construct the signature blob (raw signature + public key)
5. Append the signature blob to the transaction

The signed transaction uses a typed envelope format (EIP-2718):

```
0x03 || RLP([
    chainId,
    nonce,
    maxPriorityFeePerGas,
    maxFeePerGas,
    gasLimit,
    to,
    value,
    data,
    accessList,
    signatureBlob    ← 3,732 bytes
])
```

> **Total transaction size**: \~3,930 bytes for a simple transfer (vs \~200 bytes with ECDSA). The additional size comes from the post-quantum signature.

## Phase 2: Submission and Validation

### RPC Submission

The signed transaction is submitted to a node via JSON-RPC:

```bash
curl -X POST https://www.armchain.org/devnet \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": ["0x03f9...signed_tx_hex"],
    "id": 1
  }'
```

### Node Validation

The receiving node performs these checks:

| Check                    | Description                                               |
| ------------------------ | --------------------------------------------------------- |
| **Type validation**      | Transaction type must be `3` (PQC)                        |
| **Signature validation** | ML-DSA44 signature must be valid for the transaction hash |
| **Signature size**       | Signature blob must be exactly 3,732 bytes                |
| **Address derivation**   | Sender address derived from public key in signature       |
| **Nonce check**          | Nonce must match sender's current nonce                   |
| **Balance check**        | Sender must have sufficient ARM for value + gas           |
| **Gas price check**      | Gas price must meet the minimum (1 Gwei)                  |
| **Gas limit check**      | Gas limit must be within block gas limit                  |
| **Chain ID**             | Must match the network's chain ID                         |

### Mempool (Transaction Pool)

Valid transactions enter the mempool, organized as:

* **Pending**: Ready for inclusion (correct nonce, sufficient balance)
* **Queued**: Waiting for earlier nonces to be processed

You can inspect the mempool:

```bash
# Get pending transaction count
curl -X POST https://www.armchain.org/devnet \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"txpool_status","params":[],"id":1}'
```

## Phase 3: Event Inclusion

### Validator Selection

Any active validator with available gas power can include the transaction:

1. Validator selects transactions from its local mempool (by gas price, highest first)
2. Validator creates a DAG **event** containing these transactions
3. The event includes references to parent events (up to 10 parents)
4. Validator signs the event with its ML-DSA44 validator key

### Event Structure

```
┌───────────────────────────────────┐
│            DAG Event               │
├───────────────────────────────────┤
│  Validator ID:    3                │
│  Sequence:        1057             │
│  Self-Parent:     [Event 1056]     │
│  Other Parents:   [V1:890, V5:442] │
│  Transactions:    [tx1, tx2, tx3]  │
│  Gas Used:        125,000          │
│  Signature:       ML-DSA44 (3,732B)│
└───────────────────────────────────┘
```

## Phase 4: Gossip and Consensus

### P2P Gossip

Events are propagated through the network using the devp2p gossip protocol:

```
Validator A creates event
    │
    ├── sends to Peer B
    │       ├── B sends to Peer D
    │       └── B sends to Peer E
    └── sends to Peer C
            └── C sends to Peer F
```

Default P2P port: `5050` (TCP/UDP).

### Lachesis Consensus

The Lachesis aBFT algorithm processes the growing DAG to determine a **total ordering** of events:

1. **Root detection**: Identify events that are "roots": events forkless-caused by ≥ 2/3 of validator weight from the previous frame
2. **Atropos election**: Root events in frame F+1 vote on candidate events in frame F. Votes are weighted by validator stake. When a candidate reaches quorum (`(TotalWeight × 2/3) + 1`), it is decided.
3. **Atropos selection**: Validators are iterated by weight (descending). The first validator whose candidate has a decided "YES" vote becomes the Atropos for that frame.
4. **Block assignment**: The Atropos event maps 1:1 to a block. All confirmed events from the frame are included.

This process is asynchronous: it does not require rounds, timeouts, or leader election. See [Consensus](/architecture/consensus.md) for a detailed worked example.

## Phase 5: EVM Execution

### Block Execution

Once events are ordered into a block:

1. Transactions are executed **sequentially** through the EVM
2. Each transaction updates the state trie
3. Events (logs) are emitted for contract interactions
4. Gas is consumed and fees are calculated
5. A state root is computed for the block

### Transaction Receipt

After execution, a receipt is generated:

```javascript
{
  transactionHash: "0xabc123...",
  blockNumber: 42000,
  blockHash: "0xdef456...",
  from: "0x742d35...",
  to: "0x5FbDB2...",
  status: 1,              // 1 = success, 0 = revert
  gasUsed: "21000",
  effectiveGasPrice: "5000000000",
  logs: [],
  cumulativeGasUsed: "21000",
  type: "0x3"             // PQC transaction
}
```

## Phase 6: Finality

### Instant and Irreversible

Once the block is sealed, the transaction is **final**:

* **No waiting for confirmations**: 1 confirmation = absolute finality
* **No rollbacks**: The aBFT guarantee means the block will never be reverted
* **No chain reorganizations**: The DAG structure prevents forks
* **Deterministic**: Every honest node will agree on the same final state

### Confirming Finality

```javascript
// Using @armchain-ethersv6/ethers
const tx = await wallet.sendTransaction({
  to: recipient,
  value: ethers.parseEther("1.0"),
  type: 3
});

// Wait for receipt - once received, it's FINAL
const receipt = await tx.wait();
console.log("Final in block:", receipt.blockNumber);
// That's it - no need to wait for more confirmations
```

## Transaction Size Comparison

| Transaction Type  | Typical Size      | Signature Size             | Notes                   |
| ----------------- | ----------------- | -------------------------- | ----------------------- |
| Type 0 (Legacy)   | \~200 bytes       | 65 bytes (ECDSA)           | No access list          |
| Type 2 (EIP-1559) | \~250 bytes       | 65 bytes (ECDSA)           | Dynamic fees            |
| **Type 3 (PQC)**  | **\~3,930 bytes** | **3,732 bytes (ML-DSA44)** | **Post-quantum secure** |

The \~57x increase in signature blob size (65 → 3,732 bytes) drives the overall \~36x increase in transaction size (\~110 → \~3,930 bytes). This is factored into Armchain's block gas limits and network parameters.

## Error Handling

Common transaction errors:

| Error                            | Cause                               | Solution                                        |
| -------------------------------- | ----------------------------------- | ----------------------------------------------- |
| `nonce too low`                  | Transaction nonce already used      | Get latest nonce from `eth_getTransactionCount` |
| `insufficient funds`             | Not enough ARM for value + gas      | Check balance with `eth_getBalance`             |
| `gas too low`                    | Gas limit below minimum             | Use `eth_estimateGas` to get estimate           |
| `invalid signature`              | Malformed ML-DSA44 signature        | Ensure signing with `@armchain-ethersv6/ethers` |
| `invalid sender`                 | Can't derive address from signature | Verify key generation and signing process       |
| `transaction type not supported` | Non-Type 3 transaction              | Set `type: 3` in transaction object             |

## Further Reading

* [PQC Transaction Types](/pqc/transaction-types.md): Detailed Type 3 format specification
* [ML-DSA44 Deep Dive](/pqc/mldsa44.md): How signatures are created and verified
* [JSON-RPC API](/api-reference/json-rpc.md): RPC methods for transaction management
* [Armchain Ethers SDK](/developers/overview-1.md): SDK for building transactions


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.armchain.org/architecture/transaction-lifecycle.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
