Skip to main content
The SDK exposes two signing methods. Both require a provisioned wallet, and both verify the PIN before calling the native layer when requirePin: true. All signatures are:
  • 0x-prefixed 130-character hex strings
  • Recoverable ECDSA in r(32) ‖ s(32) ‖ v(1) format
  • Low-s normalised (EIP-2 compliant)
  • v ∈ {27, 28} — verifiable with ecrecover

Sign a personal message (EIP-191)

signMessage prepends the \x19Ethereum Signed Message:\n{length} prefix before hashing and signing. This matches the personal_sign RPC method used by SIWE, login challenges, and most wallet connect flows.
When requirePin: false, omit the pin argument:

Sign a 32-byte hash

signTransactionHash signs a pre-computed 32-byte digest directly, without adding any prefix. Use this for:
  • ERC-4337 userOpHash
  • EIP-712 structured-data digests
  • Raw Ethereum transaction hashes
  • Any other 32-byte payload you’ve already hashed yourself
The argument must be a 0x-prefixed 64-hex-character (32-byte) string. Anything else throws signInvalidHash.

Server-side verification

Signatures are ECDSA-recoverable. Your backend (or a smart contract) only needs the address from initWallet():

Gating pattern

A common pattern is to ask for the PIN only when requirePin is true, and skip the prompt otherwise:

Prerequisites checklist

Before calling either signing method you need:
  1. A provisioned wallet (initWallet() succeeded or hasWallet() is true)
  2. A PIN set (hasPin() is true) — only when requirePin: true
Calling a signing method without satisfying these will throw pinNotSet or result in a native error.