# `X402.Transaction`
[🔗](https://github.com/cardotrejos/x402/blob/v0.9.0/lib/x402/transaction.ex#L1)

Minimal [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) (type-2)
transaction encoding and signing digests.

Built for `X402.Facilitator.Engine`'s settlement path: the facilitator
assembles a `transferWithAuthorization` call, signs its digest through the
`X402.Signer` behaviour, and broadcasts the raw transaction via
`eth_sendRawTransaction`. The module is deliberately narrow — type-2
transactions only, always with an empty access list — and pure except for
`digest/1`, which needs the optional `ex_keccak` dependency.

The signing preimage is `0x02 || rlp([chainId, nonce, maxPriorityFeePerGas,
maxFeePerGas, gasLimit, to, value, data, accessList])` and the broadcast
form appends the signature as `yParity, r, s`. EIP-1559 signatures carry
`yParity` (`0`/`1`), **not** the legacy `27`/`28` recovery id;
`encode_signed/2` accepts both and normalizes.

## Examples

    iex> tx = %X402.Transaction{
    ...>   chain_id: 1,
    ...>   nonce: 0,
    ...>   max_priority_fee_per_gas: 1,
    ...>   max_fee_per_gas: 2,
    ...>   gas_limit: 21_000,
    ...>   to: "0x1111111111111111111111111111111111111111"
    ...> }
    iex> {:ok, preimage} = X402.Transaction.preimage(tx)
    iex> Base.encode16(preimage, case: :lower)
    "02df0180010282520894111111111111111111111111111111111111111180" <> "80c0"

# `error`

```elixir
@type error() :: :invalid_address | :invalid_transaction | :invalid_signature_format
```

Encoding errors.

# `t`

```elixir
@type t() :: %X402.Transaction{
  chain_id: non_neg_integer(),
  data: binary(),
  gas_limit: non_neg_integer(),
  max_fee_per_gas: non_neg_integer(),
  max_priority_fee_per_gas: non_neg_integer(),
  nonce: non_neg_integer(),
  to: String.t(),
  value: non_neg_integer()
}
```

An EIP-1559 transaction.

`to` is a `0x`-prefixed EVM address, `data` the raw calldata bytes, and all
quantities non-negative integers in wei / gas units. The access list is
always empty.

# `digest`
*since 0.6.0* 

```elixir
@spec digest(t()) :: {:ok, &lt;&lt;_::256&gt;&gt;} | {:error, error() | :missing_dependency}
```

Computes the 32-byte signing digest: `keccak256(preimage)`.

Requires the optional `ex_keccak` dependency and returns
`{:error, :missing_dependency}` when it is unavailable.

# `encode_signed`
*since 0.6.0* 

```elixir
@spec encode_signed(t(), binary()) :: {:ok, binary()} | {:error, error()}
```

Encodes the broadcast-ready raw transaction from a 65-byte signature.

`signature` is `r || s || v` where `v` may be an EIP-1559 `yParity`
(`0`/`1`) **or** a legacy recovery id (`27`/`28`, as produced by
`X402.Signer.sign_eip712/3`); both are normalized to `yParity`. `r` and
`s` are RLP-encoded as integers, so leading zero bytes are stripped per
the Ethereum convention.

Returns the raw bytes to submit via `eth_sendRawTransaction` (hex-encode
with a `0x` prefix first).

## Examples

    iex> tx = %X402.Transaction{
    ...>   chain_id: 1,
    ...>   nonce: 0,
    ...>   max_priority_fee_per_gas: 1,
    ...>   max_fee_per_gas: 2,
    ...>   gas_limit: 21_000,
    ...>   to: "0x1111111111111111111111111111111111111111"
    ...> }
    iex> signature = <<1::unsigned-big-integer-size(256), 2::unsigned-big-integer-size(256), 28>>
    iex> {:ok, raw} = X402.Transaction.encode_signed(tx, signature)
    iex> Base.encode16(raw, case: :lower)
    "02e20180010282520894111111111111111111111111111111111111111180" <> "80c0010102"

    iex> tx = %X402.Transaction{
    ...>   chain_id: 1,
    ...>   nonce: 0,
    ...>   max_priority_fee_per_gas: 1,
    ...>   max_fee_per_gas: 2,
    ...>   gas_limit: 21_000,
    ...>   to: "0x1111111111111111111111111111111111111111"
    ...> }
    iex> X402.Transaction.encode_signed(tx, <<0, 1, 2>>)
    {:error, :invalid_signature_format}

# `preimage`
*since 0.6.0* 

```elixir
@spec preimage(t()) :: {:ok, binary()} | {:error, error()}
```

Returns the unsigned signing preimage: `0x02 || rlp(unsigned fields)`.

The digest a signer signs is the keccak-256 hash of this preimage — see
`digest/1`.

## Examples

    iex> X402.Transaction.preimage(%X402.Transaction{
    ...>   chain_id: 1,
    ...>   nonce: 0,
    ...>   max_priority_fee_per_gas: 1,
    ...>   max_fee_per_gas: 2,
    ...>   gas_limit: 21_000,
    ...>   to: "0xnope"
    ...> })
    {:error, :invalid_address}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
