# Transaction Types

Armchain introduces **Type 3** transactions for post-quantum cryptography. If you're using the Armchain Ethers SDK, this is handled automatically. The SDK creates and signs Type 3 transactions for you. This page is for developers who want to understand the format details or build custom tooling.

## Transaction Type Summary

| Type  | Name               | EIP          | Signature    | Supported |
| ----- | ------------------ | ------------ | ------------ | --------- |
| 0     | Legacy             | Pre-EIP-2718 | ECDSA        | **No**    |
| 1     | Access List        | EIP-2930     | ECDSA        | **No**    |
| 2     | EIP-1559           | EIP-1559     | ECDSA        | **No**    |
| **3** | **PQC (Armchain)** | -            | **ML-DSA44** | **Yes**   |

> **Armchain only supports Type 3 transactions.** ECDSA-signed transactions (Types 0, 1, 2) are rejected by the network.

## Type 3 Transaction Format

### Fields

| Field                  | Type      | Description                            |
| ---------------------- | --------- | -------------------------------------- |
| `type`                 | `uint8`   | Always `0x03`                          |
| `chainId`              | `uint64`  | Chain ID (55 devnet)                   |
| `nonce`                | `uint64`  | Sender account nonce                   |
| `maxPriorityFeePerGas` | `uint256` | Tip for the validator (EIP-1559)       |
| `maxFeePerGas`         | `uint256` | Maximum total gas price (EIP-1559)     |
| `gasLimit`             | `uint64`  | Maximum gas for execution              |
| `to`                   | `address` | Recipient (null for contract creation) |
| `value`                | `uint256` | ARM amount in wei                      |
| `data`                 | `bytes`   | Calldata or constructor bytecode       |
| `accessList`           | `[]tuple` | Storage access list (EIP-2930)         |
| `signatureBlob`        | `bytes`   | ML-DSA44 signature + public key        |

### Signature Blob

The `signatureBlob` field contains the **concatenation** of the ML-DSA44 signature and the public key:

```
signatureBlob (3,732 bytes)
```

ML-DSA44 does not support public key recovery, so both the signature and the public key are included in the blob.

### RLP Encoding

Type 3 transactions use **typed transaction envelopes** (EIP-2718):

```
Serialized = 0x03 || RLP([
    chainId,
    nonce,
    maxPriorityFeePerGas,
    maxFeePerGas,
    gasLimit,
    to,
    value,
    data,
    accessList,
    signatureBlob
])
```

The **signing payload** (hash that is signed) is:

```
sigHash = Keccak256(0x03 || RLP([
    chainId,
    nonce,
    maxPriorityFeePerGas,
    maxFeePerGas,
    gasLimit,
    to,
    value,
    data,
    accessList
]))
```

The signature is computed over the `sigHash`, then the `signatureBlob` (signature + public key) is appended to the transaction fields.

## Creating Type 3 Transactions

### Using the Ethers SDK

```javascript
import { ethers } from "@armchain-ethersv6/ethers";

const provider = new ethers.JsonRpcProvider("https://www.armchain.org/devnet");
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);

// Simple transfer
const tx = await wallet.sendTransaction({
  to: "0xRecipientAddress",
  value: ethers.parseEther("1.0"),
  type: 3, // PQC transaction type
});

console.log("TX hash:", tx.hash);
const receipt = await tx.wait();
console.log("Confirmed in block:", receipt.blockNumber);
```

### Manual Transaction Construction

```javascript
import { ethers } from "@armchain-ethersv6/ethers";

const provider = new ethers.JsonRpcProvider("https://www.armchain.org/devnet");
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);

// Build a transaction manually
const txRequest = {
  type: 3,
  chainId: 55,
  to: "0xRecipientAddress",
  value: ethers.parseEther("1.0"),
  nonce: await provider.getTransactionCount(wallet.address),
  maxFeePerGas: ethers.parseUnits("100", "gwei"),
  maxPriorityFeePerGas: ethers.parseUnits("1", "gwei"),
  gasLimit: 21000n,
};

// Sign (produces the signatureBlob internally)
const signedTx = await wallet.signTransaction(txRequest);

// Broadcast
const response = await provider.broadcastTransaction(signedTx);
console.log("TX hash:", response.hash);
```

### Contract Deployment

```javascript
const factory = new ethers.ContractFactory(abi, bytecode, wallet);
const contract = await factory.deploy(/* constructor args */, {
  type: 3,
});
await contract.waitForDeployment();
console.log("Deployed at:", await contract.getAddress());
```

### Contract Interaction

```javascript
const contract = new ethers.Contract(contractAddress, abi, wallet);

// Read (no transaction needed)
const value = await contract.someViewFunction();

// Write (creates a Type 3 transaction)
const tx = await contract.someFunction(arg1, arg2, {
  type: 3,
});
await tx.wait();
```

## JSON-RPC Representation

When queried via JSON-RPC (`eth_getTransactionByHash`), Type 3 transactions include:

```json
{
  "type": "0x3",
  "chainId": "0x37",
  "nonce": "0x0",
  "maxPriorityFeePerGas": "0x3b9aca00",
  "maxFeePerGas": "0x174876e800",
  "gas": "0x5208",
  "to": "0x...",
  "value": "0xde0b6b3a7640000",
  "input": "0x",
  "accessList": [],
  "signatureBlob": "0x...",
  "hash": "0x...",
  "blockHash": "0x...",
  "blockNumber": "0x1",
  "transactionIndex": "0x0"
}
```

> **Note**: Type 3 transactions do NOT have `v`, `r`, `s` fields. They have a single `signatureBlob` field instead.

## Size Comparison

| Component                   | Type 2 (ECDSA)                     | Type 3 (ML-DSA44)                   |
| --------------------------- | ---------------------------------- | ----------------------------------- |
| Transaction fields          | \~45 bytes                         | \~45 bytes                          |
| Signature                   | 65 bytes (v, r, s)                 | 2,420 bytes (raw ML-DSA44)          |
| Public key                  | 0 bytes (recovered from signature) | 1,312 bytes (appended to signature) |
| **Signature blob total**    | **65 bytes**                       | **3,732 bytes**                     |
| **Total (simple transfer)** | **\~110 bytes**                    | **\~3,930 bytes**                   |

The \~36x increase in transaction size is the cost of quantum resistance. Impact:

* **Bandwidth**: More data to propagate between nodes
* **Storage**: Larger blocks on disk
* **Intrinsic gas**: Higher data-related gas costs

However, it does NOT impact:

* **Execution gas**: EVM computation costs are identical
* **Finality time**: Still instant (Lachesis aBFT)
* **Throughput**: Block gas limits accommodate the larger transactions

## Validation Rules

When a Type 3 transaction arrives at a node, the following checks are performed:

1. **Type check**: Transaction type must be `0x03`
2. **Chain ID check**: Must match the node's chain ID
3. **Signature blob size**: Must be exactly 3,732 bytes (2,420 + 1,312)
4. **ML-DSA44 verification**: Verify the signature over the signing hash using the public key from the signature blob
5. **Address derivation**: The sender address (`from`) is derived from the public key: `Keccak256(pubkey)[12:]`
6. **Nonce check**: Must match the sender's current nonce
7. **Balance check**: Sender must have sufficient ARM for `value + gas * maxFeePerGas`
8. **Gas limit check**: Must be at least the intrinsic gas

If any check fails, the transaction is rejected and not propagated.

## Further Reading

* [ML-DSA44 Deep Dive](/pqc/mldsa44.md): Algorithm details
* [PQC Overview](/pqc/overview.md): Why post-quantum cryptography
* [Ethers SDK](/developers/overview-1.md): Working with transactions in code


---

# 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/pqc/transaction-types.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.
