Skip to main content

Sign Messages

note

Use our prebuilt Stacks.js starter templates to kickstart your frontend application development with your preferred JavaScript framework.

This guide explains how to prompt users to sign a message.

The user will be prompted a popup from the Hiro Wallet showing the message you would like them to sign.

The user can then click on the ‘Sign’ button which will return the signature data and the user's publicKey to your app. You can then verify the signature by passing the signature data and the public key to the stacks.js verifySignature method.

The message can be any utf-8 string.

Internally the string will be hashed using sha256 and signed with secp256k1 using the user's privateKey

Install dependency

tip

In order to utilize the latest transaction signing with the Hiro Wallet, use a version >= 6.6.0 of the @stacks/connect NPM package.

The following dependency must be installed:

npm install @stacks/connect

Initiate session

Users must authenticate to an app before you request message signing. Users can install an authenticator like the Hiro Wallet.

See the authentication guide before proceeding to integrate the following message signing capabilities.

Prompt to sign a message

Call the openSignatureRequestPopup function provided by the connect  package to trigger the display of the message signing prompt.

import { openSignatureRequestPopup } from '@stacks/connect';
import { StacksTestnet } from '@stacks/network';

const message = 'Hello World';

openSignatureRequestPopup({
message,
network: new StacksTestnet(), // for mainnet, `new StacksMainnet()`
appDetails: {
name: 'My App',
icon: window.location.origin + '/my-app-logo.svg',
},
onFinish(data) {
console.log('Signature of the message', data.signature);
console.log('Use public key:', data.publicKey);
},
});

Several parameters are available for calling openSignatureRequestPopup. Here's the exact interface for them:

interface SignatureRequestOptions {
message: string;
onFinish?: (data: SignatureData) => void;
onCancel?: (data: SignatureData) => void;
appDetails: {
name: string;
icon: string;
};
authOrigin?: string;
stxAddress?: string;
userSession?: UserSession;
}

Getting the signed message back after completion

The openSignatureRequestPopup signing method from @stacks/connect allows you to specify an onFinish callback. This callback will be triggered after the user has successfully signed the message.

You can get the signature of the message via the arguments passed to onFinish. Your callback will be fired with a single argument, which is an object with the following properties:

export interface SignatureData {
/* Hex encoded DER signature */
signature: string;
/* Hex encoded private string taken from privateKey */
publicKey: string;
}
const onFinish = (data: SignatureData) => {
console.log('Signature', data.signature);
console.log('PublicKey', data.publicKey);
};

How to verify a signature

You can easily verify the signature using the @stacks/stacks.js package as seen in the following example.

import { verifyMessageSignatureRsv } from '@stacks/encryption';

const message = 'Hello World';

openSignatureRequestPopup({
// ...
onFinish({ publicKey, signature }) {
const verified = verifyMessageSignatureRsv({ message, publicKey, signature });
if (verified) {
// Trigger a notification explaining signature is verified
}
},
});

Specifying the network for a transaction

All of the methods included on this page accept a network option. By default, Connect uses a testnet network option. You can import a network configuration from the @stacks/network package.

import { StacksTestnet, StacksMainnet } from '@stacks/network';

const testnet = new StacksTestnet();
const mainnet = new StacksMainnet();

// use this in your messe signing method:

openSignatureRequestPopup({
network: mainnet,
// other relevant options
});

Usage in React Apps

Import the useConnect helper from connect-react package to sign messages more seamlessly with React apps. You must install a version >= 15.0.0

npm install @stacks/connect-react

Use the function with the same parameters as outlined above. However, you don't have to specify appDetails since they are detected automatically if useConnect has been used already for authentication.

import { useConnect } from '@stacks/connect-react';

const MyComponent = () => {
const { sign } = useConnect();

const onClick = async () => {
const options = {
/** See examples above */
};
await sign(options);
};

return <span onClick={onClick}>Sign message</span>;
};

Signature request / response payload

Under the hood, @stacks/connect will serialize and deserialize data between your app and the Hiro Wallet.

These payloads are tokens that conform to the JSON Web Token (JWT) standard with additional support for the secp256k1 curve used by Bitcoin and many other cryptocurrencies.

Signature Request Payload

When an application triggers a message signing from @stacks/connect, the options of that signature request are serialized into a signatureRequest payload. The signatureRequest is similar to the authRequest payload used for authentication.

The signature request payload has the following schema, in addition to the standard JWT required fields:

interface SignatureRequestPayload {
message: string;
publicKey: string;
/**
* Provide the Hiro Wallet with a suggested account to sign this transaction with.
* This is set by default if a `userSession` option is provided.
*/
stxAddress?: string;
appDetails?: AuthOptions['appDetails'];
network?: StacksNetwork;
}

Signature Response payload

After the user signs the message, a signatureResponse payload is sent back to your app.

interface SignatureData {
/* Hex encoded DER signature */
signature: string;
/* Hex encoded private string taken from privateKey */
publicKey: string;
}

StacksProvider injected variable

When users have the Hiro Wallet extension installed, the extension will inject a global StacksProvider variable into the JavaScript context of your web app. This allows your JavaScript code to hook into the extension, and make authentication, transaction and signature requests. @stacks/connect automatically detects and uses this global variable for you.

At the moment, only the Hiro Wallet extension includes a StacksProvider, however, ideally more wallets (and mobile wallets) will support this format, so that your app can be compatible with any Stacks wallet that has functionality to embed web applications.

In your web application, you can check to see if the user has a compatible wallet installed by checking for the presence of window.StacksProvider.

Here is the interface for the StacksProvider variable.

interface StacksProvider {
/** @deprecated */
getURL: () => Promise<string>;
/**
* Make a transaction request
*
* @param payload - a JSON web token representing a transaction request
*/
transactionRequest(payload: string): Promise<FinishedTxPayload | SponsoredFinishedTxPayload>;
/**
* Make an authentication request
*
* @param payload - a JSON web token representing an auth request
*
* @returns an authResponse string in the form of a JSON web token
*/
authenticationRequest(payload: string): Promise<string>;
signatureRequest(payload: string): Promise<SignatureData>;
getProductInfo:
| undefined
| (() => {
version: string;
name: string;
meta?: {
tag?: string;
commit?: string;
[key: string]: any;
};
[key: string]: any;
});
}