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

First-party signing for Derive (Lyra v2, on Optimism).

Derive has **two** signature paths, both secp256k1 over a Keccak-256 digest:

- **REST auth headers** (every private request) — EIP-191 personal-message
  signing of the millisecond timestamp. Emits the header trio:
  `X-LyraWallet` (smart-contract wallet = `credentials.api_key`),
  `X-LyraTimestamp`, `X-LyraSignature` (session-key = `credentials.secret`).
- **Order signing** (`sign_order/2`) — EIP-712 typed data with a *fixed*
  domain separator. Additive on order endpoints only: when `:order` is in
  params, the packed signature is injected into the JSON body under
  `"signature"`. Never a precondition for private reads.

Both produce Derive's 65-byte packed signature (`0x ‖ r ‖ s ‖ v`).

## Credentials

- `credentials.api_key` — Derive smart-contract wallet address (`X-LyraWallet`).
  Not the owner EOA unless that address is also a registered session key.
- `credentials.secret` — private key of a **registered** session key (or the
  wallet itself when it signs). Edge proxy verifies recovered signer against
  the wallet / session-key registry before the app runs; owner EOA is not
  auto-registered on UI onboarding.

# `hash_message`

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

Computes the EIP-191 personal-message hash (`hashMessage`) for `message`.

# `hash_order_message`

```elixir
@spec hash_order_message(
  [term()],
  keyword()
) :: binary()
```

Computes the 32-byte order hash (`hashOrderMessage`).

`order` is the eight-element list of field values in Derive order:
`[action_typehash, subaccount_id, nonce, trade_module_address,
trade_module_data_hash, signature_expiry, derive_wallet_address, wallet_address]`.

Options: `:testnet` selects the sandbox domain separator (default `false`).

# `sign`

```elixir
@spec sign(Bourse.Signing.request(), Bourse.Credentials.t(), Bourse.Signing.config()) ::
  Bourse.Signing.signed_request()
```

`Bourse.Signing.Behaviour` entry point.

Always emits the REST auth-header trio (`X-LyraWallet` / `X-LyraTimestamp` /
`X-LyraSignature`) for private requests. When `:order` (or `"order"`) is
present in params, also injects the EIP-712 packed order signature into the
JSON body under `"signature"` and drops the order tuple. Order signing is
additive — private reads with only a JSON body (or empty params) succeed
without an `:order` precondition.

# `sign_message`

```elixir
@spec sign_message(
  String.t(),
  keyword()
) :: String.t()
```

Signs `message` with EIP-191 personal signing, returning the packed
`0x ‖ r ‖ s ‖ v` signature string. Options: `:private_key` (required).

# `sign_order`

```elixir
@spec sign_order(
  [term()],
  keyword()
) :: String.t()
```

Signs an order, returning Derive's packed `0x ‖ r ‖ s ‖ v` signature string.

Options: `:private_key` (required), `:testnet` (default `false`).

# `signer_address`

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

Derives the session-key EOA address used by Derive's order envelope.

# `trade_module_data_hash`

```elixir
@spec trade_module_data_hash(
  String.t(),
  integer(),
  String.t(),
  String.t(),
  String.t(),
  integer(),
  boolean()
) :: binary()
```

Hashes Derive's trade-module tuple for an EIP-712 order signature.

---

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