Signing Messages
Message signing lets a dApp prove a user controls an address without submitting an on-chain transaction — commonly used for off-chain authentication.
signMessage({ message })
Signs a UTF-8 message. The input must be hex-encoded UTF-8 bytes.
const originalTextMessage = 'Welcome to StarKey Wallet'
const utf8ToHexString = '0x' + Buffer.from(originalTextMessage, 'utf8').toString('hex')
const response = await window.starkey.supra.signMessage({ message: utf8ToHexString })Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
message | string | yes | Hex-encoded UTF-8 bytes, prefixed with 0x. |
Returns:
interface SignedMessage {
address: string
publicKey: string
signature: string
}{
address: '0x841c....389',
publicKey: '0xf348...42b',
signature: '0x94208328...c3a100b',
}signHexMessage({ message })
Signs a raw hex-format message directly, with no UTF-8 re-encoding step.
const hexMessage = '0x57656c636f6d6520746f20537461724b65792057616c6c6574'
const response = await window.starkey.supra.signHexMessage({ message: hexMessage })Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
message | string | yes | Raw hex string, prefixed with 0x. Sent to the wallet as-is, not re-encoded. |
Returns: same SignedMessage shape as signMessage.
Verifying a signature
Verify the returned signature against the original message and public key with Ed25519PublicKey from @aptos-labs/ts-sdk (StarKey uses the same Ed25519 scheme on Supra):
import { Ed25519PublicKey, Ed25519Signature } from '@aptos-labs/ts-sdk'
const remove0xPrefix = (hex: string) => (hex.startsWith('0x') ? hex.slice(2) : hex)
const pubKey = new Ed25519PublicKey(publicKey)
const verified = pubKey.verifySignature({
message: isHexMessage
? Uint8Array.from(Buffer.from(remove0xPrefix(originalMessage), 'hex'))
: new TextEncoder().encode(originalMessage),
signature: new Ed25519Signature(remove0xPrefix(signature)),
})
// true if the signature is validDeriving the wallet address from the returned public key
import { Ed25519PublicKey } from '@aptos-labs/ts-sdk'
const getAddressFromPublicKey = (publicKeyHex: string): string => {
const pubKey = new Ed25519PublicKey(publicKeyHex)
return pubKey.authKey().derivedAddress().toString()
}Use this to cross-check that the address returned in SignedMessage actually derives from its own publicKey, rather than trusting the address field on its own.
Error handling
- Both methods resolve to
null— they do not reject — if the user declines the sign prompt. Check fornullbefore readingsignature. See Errors. signHexMessagedoes no UTF-8 encoding on your behalf — passing plain text instead of a0x-prefixed hex string will sign the wrong bytes. UsesignMessagefor plain text.
Security considerations
- Always verify a signature server-side before treating it as proof of address ownership — never trust an unverified
signature/addresspair sent from the client. - Include a nonce or timestamp in the signed message payload for auth flows, to prevent a captured signature from being replayed later.
- Never ask a user to sign a message whose plaintext could double as a valid transaction payload elsewhere in your stack — use a distinct, clearly human-readable prefix for auth messages.
Best practices
- Prefer
signMessagefor human-readable text (it round-trips cleanly through UTF-8); reservesignHexMessagefor cases where you already have raw hex bytes to sign. - Derive and compare the address (as shown above) rather than trusting the
addressfield returned alongside the signature.
Related pages
Last updated on