Signer backend that signs with a local secp256k1 private key.
Implements Onchain.Signer.Backend — its config is the raw private-key
binary. The behaviour's Onchain.Signer.Backend.sign_payload/2 signs the
32-byte digest it is handed directly; sign/2 is a convenience wrapper that
keccaks a raw message first.
Uses the precompiled RustCrypto k256 backend. Private keys remain in BEAM memory;
use Onchain.Signer.CloudKMS when keys must stay in an HSM.
Summary
Functions
Get the Ethereum address associated with the given private key.
Get the uncompressed secp256k1 public key for the given private key.
Signs a raw message, keccak-digesting it first.
Deprecated alias for sign_payload/2; signs an already-digested message.
Signs the 32-byte digest it is handed directly — the pure-payload contract.
Functions
Get the Ethereum address associated with the given private key.
Examples
iex> priv_key = "800509fa3e80882ad0be77c27505bdc91380f800d51ed80897d22f9fcc75f4bf" |> Base.decode16!(case: :mixed)
iex> {:ok, address} = Onchain.Signer.Secp256k1.get_address(priv_key)
iex> Onchain.Hex.to_address(address)
"0x63Cc7c25e0cdb121aBb0fE477a6b9901889F99A7"
Get the uncompressed secp256k1 public key for the given private key.
Examples
iex> priv_key = "800509fa3e80882ad0be77c27505bdc91380f800d51ed80897d22f9fcc75f4bf" |> Base.decode16!(case: :mixed)
iex> {:ok, pub} = Onchain.Signer.Secp256k1.public_key(priv_key)
iex> Onchain.Hex.to_address(Onchain.Address.from_public_key(pub))
"0x63Cc7c25e0cdb121aBb0fE477a6b9901889F99A7"
@spec sign(String.t(), binary()) :: {:ok, Onchain.Signature.t()} | {:error, atom()}
Signs a raw message, keccak-digesting it first.
Back-compat convenience over sign_payload/2 for EIP-191-style raw-message
signing; for typed data (EIP-712, Hyperliquid) the caller should compute the
digest and call sign_payload/2 directly.
Examples
iex> use Onchain.Hex
iex> priv_key = ~h[0x800509fa3e80882ad0be77c27505bdc91380f800d51ed80897d22f9fcc75f4bf]
iex> {:ok, sig} = Onchain.Signer.Secp256k1.sign("test", priv_key)
iex> {:ok, recid} = Onchain.Recover.find_recid("test", sig, ~h[0x63Cc7c25e0cdb121aBb0fE477a6b9901889F99A7])
iex> Onchain.Recover.recover_eth("test", %{sig|recid: recid}) |> Onchain.Hex.to_address()
"0x63Cc7c25e0cdb121aBb0fE477a6b9901889F99A7"
@spec sign_digest(String.t(), binary()) :: {:ok, Onchain.Signature.t()} | {:error, atom()}
Deprecated alias for sign_payload/2; signs an already-digested message.
@spec sign_payload(<<_::256>>, binary()) :: {:ok, Onchain.Signature.t()} | {:error, atom()}
Signs the 32-byte digest it is handed directly — the pure-payload contract.
This performs no hashing: the caller (Onchain.Signer) owns digest
computation, so the same backend serves plain Eth tx, EIP-712, and Hyperliquid.
Examples
iex> use Onchain.Hex
iex> priv_key = ~h[0x800509fa3e80882ad0be77c27505bdc91380f800d51ed80897d22f9fcc75f4bf]
iex> message_hash = ~h[0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658]
iex> {:ok, sig} = Onchain.Signer.Secp256k1.sign_payload(message_hash, priv_key)
iex> {:ok, recid} = Onchain.Recover.find_recid("test", sig, ~h[0x63Cc7c25e0cdb121aBb0fE477a6b9901889F99A7])
iex> Onchain.Recover.recover_eth("test", %{sig|recid: recid}) |> Onchain.Hex.to_address()
"0x63Cc7c25e0cdb121aBb0fE477a6b9901889F99A7"