# `Bourse.Signing.Crypto`
[🔗](https://github.com/ZenHive/bourse/blob/main/lib/bourse/signing/crypto.ex#L1)

Shared low-level crypto primitives for the custom DEX signing modules
(`Bourse.Signing.Hyperliquid`, `Bourse.Signing.Derive`).

Wraps the Keccak-256 (`ex_keccak`) and secp256k1 (`ex_secp256k1`) NIFs.
Signatures use RFC-6979 deterministic nonces, low-`s` normalization, and
Ethereum recovery value `v = 27 + recovery_id`.

# `signature`

```elixir
@type signature() :: %{r: String.t(), s: String.t(), v: non_neg_integer()}
```

# `decode_hex`

```elixir
@spec decode_hex(String.t()) :: binary()
```

Decodes a (`0x`-prefixed) hex string into a binary.

# `decode_private_key`

```elixir
@spec decode_private_key(String.t()) :: binary()
```

Decodes a (`0x`-prefixed) hex private key into the 32-byte binary
`ExSecp256k1` expects. Accepts the last 64 hex characters.

# `encode_hex`

```elixir
@spec encode_hex(binary()) :: String.t()
```

Lowercase hex-encodes a binary (no `0x` prefix).

# `keccak256`

```elixir
@spec keccak256(iodata()) :: binary()
```

Keccak-256 digest of `data` (32 raw bytes).

# `sign_hash`

```elixir
@spec sign_hash(binary(), binary()) :: signature()
```

Signs a 32-byte `digest` with the secp256k1 `private_key`.

Returns `%{r, s, v}` where `r`/`s` are lowercase
64-char hex strings (no `0x`) and `v = 27 + recovery_id`. Signatures use
RFC-6979 deterministic nonces and canonical low-`s` values.

# `strip_0x`

```elixir
@spec strip_0x(String.t()) :: String.t()
```

Strips a leading `0x`/`0X` prefix if present.

---

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