Bourse.Signing.Crypto (bourse v0.8.0)

Copy Markdown View Source

Shared low-level crypto primitives for the custom DEX signing modules (Bourse.Signing.Hyperliquid, Bourse.Signing.Derive) and Lighter L1 ChangePubKey personal-message signatures.

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.

Summary

Functions

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

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

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

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

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

Recovers the EIP-191 signer address from message and a packed 0x ‖ r ‖ s ‖ v signature. v may be 27/28 or the raw recovery id 0/1.

Signs a 32-byte digest with the secp256k1 private_key.

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

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

Types

signature()

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

Functions

decode_hex(hex)

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

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

decode_private_key(private_key)

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

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

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

hash_message(message)

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

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

keccak256(data)

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

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

recover_signer_address(message, signature)

@spec recover_signer_address(String.t(), String.t()) ::
  {:ok, String.t()} | {:error, :invalid_signature}

Recovers the EIP-191 signer address from message and a packed 0x ‖ r ‖ s ‖ v signature. v may be 27/28 or the raw recovery id 0/1.

sign_hash(digest, private_key)

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

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

strip_0x(hex)

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

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