# Convert Signature Formats

## Overview

[`Signature`](/api/Signature) is the structured ECDSA form every Ox signer returns: an object
with `r`, `s`, and an optional recovery bit `yParity`. The module converts between that shape
and every serialized form you will meet in the wild — 64/65-byte hex, DER, compact bytes,
legacy `v` values, JSON-RPC objects, and RLP tuples.

## Recipes

### Instantiate a Signature

[`Signature.from`](/api/Signature/from) accepts a structured object — or any serialized form,
which it parses automatically.

```ts twoslash
import { Signature } from 'ox'

const signature = Signature.from({
  r: '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf',
  s: '0x4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8',
  yParity: 0,
})

const parsed = Signature.from(
  '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81b',
)
// @log: { r: '0x6e100a35…', s: '0x4a90a229…', yParity: 0 }
```

### Convert Between Hex, Bytes, and Objects

[`Signature.toHex`](/api/Signature/toHex) serializes a signature to its 65-byte form, and
[`Signature.fromHex`](/api/Signature/fromHex) parses it back.

```ts twoslash
import { Signature } from 'ox'

const signature = Signature.from({
  r: '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf',
  s: '0x4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8',
  yParity: 0,
})

const hex = Signature.toHex(signature) // [!code hl]
// @log: '0x6e100a35…6db81b'

const restored = Signature.fromHex(hex)
// @log: { r: '0x6e100a35…', s: '0x4a90a229…', yParity: 0 }
```

[`Signature.toBytes`](/api/Signature/toBytes) and
[`Signature.fromBytes`](/api/Signature/fromBytes) do the same for `Uint8Array` values, and
[`Signature.from`](/api/Signature/from) accepts any of these forms directly.

### Convert DER and Compact Forms

Hardware modules and Web Crypto emit DER; some verifiers expect a bare 64-byte `r ++ s`
encoding. Use [`Signature.toDerHex`](/api/Signature/toDerHex) and
[`Signature.toCompactBytes`](/api/Signature/toCompactBytes) to produce them.

```ts twoslash
import { Signature } from 'ox'

const signature = Signature.from({
  r: '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf',
  s: '0x4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8',
})

const der = Signature.toDerHex(signature) // [!code hl]
// @log: '0x304402206e100a35…6db8'

const compact = Signature.toCompactBytes(signature)
// @log: Uint8Array [110, 16, 10, …] (64 bytes)

const fromDer = Signature.fromDerHex(der)
```

[`Signature.fromDerBytes`](/api/Signature/fromDerBytes) and
[`Signature.fromCompactBytes`](/api/Signature/fromCompactBytes) parse the byte variants. DER
and compact forms carry no recovery bit, so the results have no `yParity`.

### Convert Legacy v and yParity

Pre-EIP-155 tooling encodes the recovery bit as `v` (27/28).
[`Signature.fromLegacy`](/api/Signature/fromLegacy) and
[`Signature.toLegacy`](/api/Signature/toLegacy) translate whole signatures;
[`Signature.vToYParity`](/api/Signature/vToYParity) and
[`Signature.yParityToV`](/api/Signature/yParityToV) translate just the bit.

```ts twoslash
import { Signature } from 'ox'

const signature = Signature.fromLegacy({
  r: '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf',
  s: '0x4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8',
  v: 28, // [!code hl]
})
// @log: { r: '0x6e100a35…', s: '0x4a90a229…', yParity: 1 }

const legacy = Signature.toLegacy(signature)
// @log: { r: '0x6e100a35…', s: '0x4a90a229…', v: 28 }

const yParity = Signature.vToYParity(28)
// @log: 1
const v = Signature.yParityToV(1)
// @log: 28
```

### Convert RPC and Tuple Formats

JSON-RPC responses hex-encode `yParity`; transaction envelopes and EIP-7702 authorization
lists serialize signatures as RLP tuples. [`Signature.fromRpc`](/api/Signature/fromRpc),
[`Signature.toRpc`](/api/Signature/toRpc), [`Signature.fromTuple`](/api/Signature/fromTuple),
and [`Signature.toTuple`](/api/Signature/toTuple) cover both.

```ts twoslash
import { Signature } from 'ox'

const signature = Signature.fromRpc({
  r: '0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d',
  s: '0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540',
  yParity: '0x0',
})

const rpc = Signature.toRpc(signature) // [!code hl]
// @log: { r: '0x635dc203…', s: '0x50c2667a…', yParity: '0x0' }

const tuple = Signature.toTuple(signature)
// @log: [yParity: '0x', r: '0x635dc203…', s: '0x50c2667a…']

const restored = Signature.fromTuple(tuple)
```

`fromRpc` also accepts responses that carry a legacy `v` field instead of `yParity`.

## Best Practices

### Preserve yParity

Recovery-based verification (and `eth_sendRawTransaction`) needs the recovery bit. Only drop
to DER or compact forms when the verifier holds the public key.

### Validate Untrusted Input

Run externally supplied signatures through
[`Signature.validate`](/api/Signature/validate) (boolean) or
[`Signature.assert`](/api/Signature/assert) (throws) before use — both check the `r`, `s`,
and `yParity` ranges.

## See More

<Cards>
  <Card icon="lucide:key" title="Work with Secp256k1" description="Produce the signatures these converters operate on." to="/guides/crypto/secp256k1" />

  <Card icon="lucide:send" title="Build, Sign & Send" description="Attach signature tuples to transaction envelopes." to="/guides/transactions/build-sign-send" />

  <Card icon="lucide:mail" title="Sign Personal Messages (EIP-191)" description="Sign and verify user-facing messages." to="/guides/messages/personal-messages" />
</Cards>
