Docs
Structured data

Structured data message signing

Structured data signing is slightly different from simple message signing, in that rather than passing an arbitrary message string, you are passing some kind of Clarity value to be signed, likely to also be used in the context of a Clarity smart contract.

Usage

@micro-stacks/react exports a hook to use, useOpenSignStructuredMessage. The hook returns a callback, openSignStructuredMessage which takes a message of type string or ClarityValue value. This method will accept a hex encoded clarity value, or a ClarityValue. Check out the page on Clarity Values to learn more about them.

isRequestPending is also returned, a boolean value to denote if there is a current request open (meaning the user has already requested a signature). The pending state is handled all internally for every wallet request, so you don't need to manually handle any loading logic.

As with all of the hooks that interact with the wallet, you can also pass an onFinish and onCancel callback.

import { useOpenSignStructuredMessage } from '@micro-stacks/react';

Example

Below you will find a component that renders an input and a button. The input is captured in a useState hook, and converted into a Clarity tuple, with the input value being converted to a stringAscii clarity value. This message is then piped into the openSignStructuredMessage callback, wrapped in a useCallback hook.

import * as React from 'react';
import { stringAsciiCV, tupleCV } from 'micro-stacks/clarity';
import { useOpenSignStructuredMessage } from '@micro-stacks/react';
 
const SignMessageComponent = () => {
  const { openSignStructuredMessage, isRequestPending } = useOpenSignStructuredMessage();
 
  const [payload, setPayload] = React.useState(null);
  const [value, setValue] = React.useState('');
 
  const message = tupleCV({
    message: stringAsciiCV(value),
  });
 
  const onClick = React.useCallback(async () => {
    await openSignStructuredMessage({
      message,
      onFinish: walletResponse => {
        setPayload(walletResponse);
      },
    });
  }, [message]);
 
  return (
    <div
      style={{
        display: 'grid',
        gap: '20px',
      }}
    >
      <h5>Sign a message!</h5>
      <input
        style={{ display: 'block' }}
        onChange={e => setMessage(e.currentTarget.value)}
      />
      <button onClick={onClick}>{isRequestPending ? 'Loading...' : 'Sign message'}</button>
      <code>
        <pre>{JSON.stringify(payload, null, 2)}</pre>
      </code>
    </div>
  );
};

Verification

To verify a signed structured message, you can import verifyStructuredMessageSignature from micro-stacks/connect.

Example

import { stringAsciiCV, tupleCV } from 'micro-stacks/clarity';
import { verifyStructuredMessageSignature } from 'micro-stacks/connect';
 
// original message
const message = tupleCV({
  message: stringAsciiCV('hi there'),
});
 
// from the wallet
const payload = {
  signature: '0c956388e3bf84a2873b2fdd...c3fbefdbd48cecf2e01',
  publicKey: '035b08fd4d14786187f51a33...4665fa437a9ad22bbdf7ae716d4599f26943a7',
};
 
const isValid = verifyStructuredMessageSignature({
  message,
  signature: payload.signature,
  publicKey: payload.publicKey,
});