Bourse.Signing.Derive (bourse v0.1.0)

Copy Markdown View Source

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.

Summary

Functions

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

Computes the 32-byte order hash (hashOrderMessage).

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

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

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

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

Functions

hash_message(message)

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

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

hash_order_message(order, opts \\ [])

@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(request, credentials, config)

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(message, opts)

@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(order, opts)

@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(private_key)

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

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

trade_module_data_hash(base_asset, sub_id, price, amount, max_fee, subaccount_id, is_bid)

@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.