---
eip: 8197
title: Cryptographically Agile Transactions
description: Cryptographically Agile Transaction format separating signature from transaction body
author: Danno Ferrin (@shemnon) <danno@tectonic.xyz>, Ron Kahat <ron@tectonic.xyz>
discussions-to: https://ethereum-magicians.org/t/eip-8197-cryptographically-agile-transactions-catx/28003
status: Draft
type: Standards Track
category: Core
created: 2025-03-04
requires: 1559, 2718, 2930, 4844, 7702
---

## Abstract

CATX (Cryptographically Agile Transactions) is an [EIP-2718](./eip-2718.md)
transaction format that separates the transaction body from its signatures using
a flat structure: `[payload_type, payload_body, (sig_type, sig_body)+]`. This
enables future migration to post-quantum cryptography without modifying
transaction semantics, supports multi-signature payloads resistant to key
substitution attacks using chained signature hashes, and facilitates future
support of zk signature aggregation.

## Motivation

Current Ethereum transactions tightly couple body and signature by embedding
algorithm-specific fields in the transaction, requiring new transaction types
for each signature algorithm. CATX addresses three key concerns:

1. **Post-quantum cryptography adoption**: Signature types are independent of
   payload types, enabling migration to PQC algorithms without modifying
   transaction semantics
2. **Key substitution resistance**: Chained signature hashes prevent key
   substitution attacks (Fujita et al., 2024) in multi-signature payloads
3. **ZK signature aggregation**: Trailing signatures can be easily stripped and
   replaced for aggregation schemes, with the payload body remaining intact for
   proof generation

## Specification

### Parameters

| Name                 | Description                                          | Value  |
|----------------------|------------------------------------------------------|--------|
| `CA_TX_TYPE`         | [EIP-2718](./eip-2718.md) transaction type           | TBD    |
| `ECDSA_SIG_TYPE`     | ECDSA signature identifier                           | `0x00` |
| `SET_CODE_AUTH_TYPE` | [EIP-7702](./eip-7702.md) authorization type (MAGIC) | `0x05` |

This EIP defines the transaction format and encoding rules. The assignment of
`sig_type` values to specific cryptographic algorithms will be governed by other
EIPs. The ECDSA signature type (`0x00`) is specified here as the existing
signature algorithm—in the event of conflict, the signature type registry EIP
takes precedence.

### Transaction Encoding

```
CA_TX_TYPE || rlp([payload_type, payload_body, (sig_type, sig_body)+])
```

Signatures are appended as flat alternating `sig_type, sig_body` pairs. The
number of signature pairs required is controlled by the payload (either fixed
per `payload_type` or determined by fields within `payload_body`). If a
transaction does not contain exactly the required number of signatures, the
transaction is invalid.

### Payload Types and Data

| Type   | Name                      | Sig Count | payload_body                                                                                                                                         |
|--------|---------------------------|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| `0x01` | [EIP-2930](./eip-2930.md) | 1         | `[chain_id, nonce, gas_price, gas_limit, to, value, data, access_list]`                                                                              |
| `0x02` | [EIP-1559](./eip-1559.md) | 1         | `[chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, access_list]`                                              |
| `0x03` | [EIP-4844](./eip-4844.md) | 1         | `[chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, access_list, max_fee_per_blob_gas, blob_versioned_hashes]` |
| `0x04` | [EIP-7702](./eip-7702.md) | 1         | `[chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, access_list, authorization_list]`                          |

Legacy (type 0) transactions are not supported.

`payload_body` corresponds to the signing form of the related transaction types.
The referenced EIPs govern the meaning and validation of the fields in their
respective payload bodies, except as noted below.

#### [EIP-7702](./eip-7702.md) Authorization List

For type `0x04` payloads, the `authorization_list` contains CATX-formatted
authorization tuples:

```
authorization_list = [[SET_CODE_AUTH_TYPE, [chain_id, address, nonce], sig_type, sig_body], ...]
```

Each authorization follows the same flat structure as the outer transaction: a
type byte, a body, and a signature pair. The authorization signature hash is:

```
auth_hash = keccak256(rlp(SET_CODE_AUTH_TYPE) || rlp([chain_id, address, nonce]))
```

The authority address is recovered from the authorization signature using the
same address derivation rules as the outer transaction signatures. Authorization
processing follows EIP-7702 semantics.

### Signatures

Each signature consists of a `sig_type, sig_body` pair. `sig_type` is encoded as
an RLP number. `sig_body` is encoded as an RLP byte string (using either short
or long string encoding as appropriate for the signature length).

When a signature is transmitted with both public key or verification key and
signature data, the public key or verification key should be first in the
`sig_body`. This does not apply to algorithms that support key extraction such
as ECDSA.

Some proposed transaction types, like Frame
transactions ([EIP-8141](./eip-8141.md)), will require multiple signatures
committing to the whole transaction. The payload body controls how many
signatures are required.

### Signature Hash

Each signature commits to the payload and all preceding signatures. The hash is
computed as:

```
payload_commitment = CA_TX_TYPE || rlp(payload_type) || rlp(payload_body)
signature_hash(0) = keccak256(payload_commitment)
signature_hash(i) = keccak256(payload_commitment
    || rlp(sig_type_0) || rlp(sig_body_0)
    || ...
    || rlp(sig_type_{i-1}) || rlp(sig_body_{i-1}))
```

Where `payload_type` is the inner type byte. The `CA_TX_TYPE` prefix in
`payload_commitment` binds all signatures to the CATX transaction format. Each
signature at index `i > 0` additionally includes all preceding `(sig_type,
sig_body)` pairs, creating an ordered commitment chain.

### Signature Type 0: ECDSA

The ECDSA signature type (`sig_type = 0x00`, RLP encoded as `0x80`) is specified
here for bootstrapping purposes. In the event of conflict for the ECDSA type
definition other specifications prevail.

The signature body for ECDSA(secp256k1) is defined as:

```
sig_body = y_parity[1] || r[32] || s[32]
```

Where `y_parity` uses 0/1 parity (not legacy `v` values of 27/28); any other
value is invalid. The length of the sig_body is 65 bytes; if it is not 65 bytes,
the signature is invalid. Existing rules regarding ECDSA signatures, such as `s`
being in the lower half of the curve order, are observed as well.

### Address Derivation

Address derivation depends on the signature type.

**ECDSA (0x00)**: Uses the legacy Ethereum address derivation for backwards
compatibility:

```
address = keccak256(uncompressed_pubkey)[12:32]
```

Where `uncompressed_pubkey` is the 64-byte public key (without the `0x04`
prefix).

**Non-ECDSA signature types**: Include the signature type in the hash.

```python
def derive_address(sig_type: uint8, public_key: bytes) -> ExecutionAddress:
    if sig_type == 0x00:
        # ECDSA: legacy derivation
        return keccak256(public_key)[12:32]
    elif len(public_key) == 63:
        # Pad 63-byte keys with 0x00 between sig_type and key
        return keccak256(sig_type || 0x00 || public_key)[12:32]
    else:
        return keccak256(sig_type || public_key)[12:32]
```

### Receipts

The common receipt formatting is used

```
CA_TX_TYPE || rlp([status, cumulative_gas_used, logs_bloom, logs])
```

### Transaction Hash

The transaction hash is computed as:

```
tx_hash = keccak256(CA_TX_TYPE || rlp([payload_type, payload_body, sig_type, sig_body, ...]))
```

This is the keccak256 hash over the entire encoded transaction.

### Gas Costs

Gas costing rules for each payload type are governed by their respective EIPs.
ECDSA signatures impose no additional cost beyond what the referenced EIPs
already specify.

EIPs defining new signature types should specify a gas penalty or discount
relative to ECDSA to account for the algorithm's verification cost, as well as
adjustments for signature size differences comparable to calldata costs.

## Rationale

### Separation of Format and Algorithm Registry

This EIP intentionally limits its scope to the transaction format—encoding,
signature hash chaining, and address derivation rules—without establishing a
comprehensive signature type registry. Algorithm assignments, `sig_body`
encoding rules, and gas cost adjustments for new signature types are concerns
that evolve independently of the transaction format and are better managed by a
dedicated registry EIP such as [EIP-7932](./eip-7932.md). The ECDSA type is
included here only to make the format immediately usable; deferring to the
registry in case of conflict avoids locking algorithm-specific decisions into
the transaction format specification.

### Flat Structure

Enables independent extension of payload types and signature algorithms without
unneeded RLP nesting structures.

### Payload-Controlled Signature Count

The payload controls how many signatures are required: either as a fixed count
per `payload_type` or determined by fields within the `payload_body`. This
allows payload types to define their own multi-signature semantics (e.g.,
variable frame counts in EIP-8141) while ensuring all required signatures are
present.

### Trailing vs. Embedded Signatures

The trailing signature table is for signatures that commit to the entire
transaction. EIP-7702 authorization signatures commit to a substructure (the
delegation tuple) rather than the entire transaction, so they remain embedded
within the `payload_body` using the same `[type, body, sig_type, sig_body]`
structure. This distinction is important: trailing signatures can be stripped
for ZK aggregation while embedded signatures are part of the payload semantics.

### CATX-Formatted Authorizations

EIP-7702 authorizations use `SET_CODE_AUTH_TYPE` (`0x05`) as their type byte,
which is the same MAGIC value used in EIP-7702. This means the authorization
signing hash `keccak256(0x05 || rlp([chain_id, address, nonce]))` is unchanged
from EIP-7702, and like the outer transaction, the signed data can be obtained
by slicing the encoded structure rather than re-serializing it.
`SET_CODE_AUTH_TYPE` serves the same role as `payload_type` in the outer
transaction—the signed message can be extracted by slicing the encoded
authorization without re-serialization. This also means existing ECDSA EIP-7702
authorization signatures can be directly converted into CATX-formatted
authorizations without re-signing. If authorization convertibility is not
desired, the same chained hashing scheme used for outer signatures could be
applied to authorization signatures to bind them to the CATX format.

### CATX Signature Domain Separation

The `CA_TX_TYPE || rlp(payload_type)` construction in `payload_commitment`
avoids signature malleability with non-CATX transaction types. Non-CATX types
prefix only their own type byte (e.g., `0x02` for EIP-1559) before the RLP
payload, while CATX prepends the additional `CA_TX_TYPE` byte. This ensures that
a signature produced for a non-CATX transaction cannot be replayed as a CATX
signature, and a CATX signature cannot be extracted and used in a non-CATX
transaction. Transactions must be re-signed when converting between formats.

### Chained Signature Hashes

This EIP uses the chained commitment structure first described
in [EIP-8175](./eip-8175.md). This commits each signature to all prior
signatures in the transaction. This provides stronger guarantees than simple
position indexing because each signer explicitly endorses the identity and
commitment of all preceding signers. The chained hash structure ensures:

- Signature at index 0 signs `hash(payload_commitment)`
- Signature at index 1 signs `hash(payload_commitment || payload_commitment
    || rlp(sig_type_0) || rlp(sig_body_0))`
- Signature at index 2 signs
  `hash(CA_TX_TYPE || payload_commitment payload_commitment
    || rlp(sig_type_0) || rlp(sig_body_0)
    || rlp(sig_type_1) || rlp(sig_body_1))`
- Signatures cannot be reordered or substituted between positions

For example, a paymaster agreeing to sponsor a transaction can include the
sender's signature in its own signing hash, ensuring it only pays for a
transaction actually signed by the expected sender. Without chaining, a
paymaster's signature could be paired with a different sender's signature for
the same payload, potentially changing who benefits from the sponsorship.

This is critical for payload types like EIP-8141 Frame transactions where
multiple parties (sender, sponsor) sign the same transaction body but for
different purposes.

### Legacy Transaction Exclusion

Legacy (type 0) transactions are not supported because payload information is
encoded in the signature, making it impossible to cleanly separate the body from
the signature.

### Address Derivation and Cross-Scheme Collisions

Address derivation depends on the signature type to prevent cross-scheme
collisions. The 63-byte special case inserts a `0x00` padding byte. Without it,
`keccak256(sig_type || 63_byte_key)` would be 64 bytes—the same length as
ECDSA's `keccak256(64_byte_key)`—creating potential collision risk. This
approach is consistent with [EIP-7932](./eip-7932.md).

### Reduced Signature Manipulation

The signature hash uses a byte-slice of the payload instead of a re-written
container for the payload, simplifying memory management.

## Backwards Compatibility

This is a new transaction type and does not affect existing
types. [EIP-2930](./eip-2930.md)/[EIP-1559](./eip-1559.md)/[EIP-4844](./eip-4844.md)/[EIP-7702](./eip-7702.md)
transactions can be converted to CATX format. The `CA_TX_TYPE` prefix in the
signature hash means existing signatures are not directly
compatible—transactions must be re-signed for CATX format.

## Security Considerations

### Signature Algorithm Binding

The `sig_type` field binds each signature to a specific algorithm.
Implementations MUST verify that the correct algorithm is used for each
signature and MUST reject transactions where the signature count does not
exactly match the count required by the payload. The valid set of signature
types can be updated during hard forks, enabling deprecation of vulnerable
schemes while keeping existing transaction structures intact.

### Key Substitution Attacks

In a multi-signature transaction, a key substitution attack (Fujita, Sakai,
Yamashita, & Hanaoka, "On Key Substitution Attacks against Aggregate Signatures
and Multi-Signatures," IACR ePrint 2024/1728) occurs when one signer's signature
is replaced or reordered to change the authorization semantics. The chained
signature hash structure defends against this: because each signature at index
`i` commits to all preceding signatures, later signers cryptographically endorse
the specific identities of all earlier signers. Replacing an earlier signature
invalidates every following signature in the chain, and reordering signatures
changes the hash inputs, making the substituted signatures unverifiable.

### Cross-Scheme Address Collisions

Non-ECDSA types use distinct address derivation that includes the
`sig_type` in the hash input, ensuring that different signature algorithms
produce non-overlapping address spaces. This prevents an attacker from
exploiting a collision between key formats of different algorithms to claim
control of an address derived under a different scheme.

### CATX Signature Domain Separation

The `payload_commitment` includes `CA_TX_TYPE` as a prefix before
`rlp(payload_type)`, ensuring that CATX signatures are not valid for the
corresponding non-CATX transaction types and vice versa. A non-CATX EIP-1559
signature signs `keccak256(0x02 || rlp([...]))` while a CATX signature for the
same payload signs `keccak256(CA_TX_TYPE || 0x02 || rlp([...]))`. This binds all
CATX signatures—including index 0—to the CATX representation, preventing
cross-format signature reuse.

### Authorization Signature Malleability

Because the EIP-7702 authorization signing hash is identical between standard
type-4 transactions and CATX transactions, authorization signatures can be
freely converted between the two formats. This is intentional and safe—the
authorization semantics are identical regardless of the containing transaction
format.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
