Skip to content

Signatures

Privy allows your app to easily request signatures from users embedded wallet. By default, Privy will display a UI preview of the message to get approval from the user.

Sample sign message prompt
Sample sign message prompt UI

For embedded wallets on EVM networks, Privy's default signature confirmation modals are highly-customizable, allowing you to contextualize onchain actions within the broader experience of your product.

INFO

Customizing Privy's default confirmation modal for signatures is only available for embedded wallets on EVM networks, not Solana. We are actively building support for customizing the signature confirmation modal for Solana embedded wallets.


In kind, the guide below exclusively refers to embedded wallets on EVM networks.

Sign message

To request a signature from a user with a custom prompt, use the signMessage method returned by the usePrivy hook:

tsx
const {signMessage} = usePrivy();

When invoked, signMessage will request an EIP-191 personal_sign signature from the embedded wallet, and returns a Promise for the user's signature as a string.

signMessage takes the following parameters. In particular, you can use the optional uiConfig to customize the prompt shown to the user to contextualize their signing action.

ParameterTypeDescription
messagestringRequired. The message the user must sign as a string.
uiConfigSignMessageModalUIOptionsOptional. Options to customize the signature prompt shown to the user. Defaults to the values outlined below
uiConfig.showWalletUIsbooleanOptional. Whether to overwrite the configured wallet UI for the signature prompt. Defaults to undefined, which will respect the server-side or SDK configured option.
uiConfig.titlestringOptional. The title text for the signature prompt. Defaults to 'Sign'.
uiConfig.descriptionstringOptional. The description text for the signature prompt. Defaults to 'Sign to continue'.
uiConfig.buttonTextstringOptional. The description text for the signature prompt. Defaults to 'Sign and Continue'.

Example

Below is an example of customizing the signature prompt:

tsx
import {usePrivy} from '@privy-io/react-auth';

function SignMessageButton() {
  const {signMessage} = usePrivy();
  const message = 'Hello world';
  const uiConfig = {
    title: 'Sample title text',
    description: 'Sample description text',
    buttonText: 'Sample button text',
  };

  return (
    <button
      onClick={async () => {
        // Use `signature` below however you'd like
        const signature = await signMessage(message, uiConfig);
      }}
    >
      Sign
    </button>
  );
}

Sign typed data

To have a user sign an EIP-712 typed data signature with a custom UI prompt, use the signTypedData method returned by the usePrivy hook:

tsx
const {signTypedData} = usePrivy();

When invoked, signTypedData will request an EIP-721 eth_signTypedData_v4 signature from the embedded wallet, and returns a Promise for the user's signature as a string.

signTypedData takes the following parameters. In particular, you can use the optional uiConfig to customize the prompt shown to the user to contextualize their signing action.

ParameterTypeDescription
typedDataSignedTypedDataParamsRequired. A JSON object that conforms to the EIP-712 TypedData JSON schema.
uiConfigSignMessageModalUIOptionsOptional. Options to customize the signature prompt shown to the user. Defaults to the values outlined below
uiConfig.showWalletUIsbooleanOptional. Whether to overwrite the configured wallet UI for the signature prompt. Defaults to undefined, which will respect the server-side or SDK configured option.
uiConfig.titlestringOptional. The title text for the signature prompt. Defaults to 'Sign'.
uiConfig.descriptionstringOptional. The description text for the signature prompt. Defaults to 'Sign to continue'.
uiConfig.buttonTextstringOptional. The description text for the signature prompt. Defaults to 'Sign and Continue'.

Example

Below is an example of customizing the typedData prompt:

tsx
import {usePrivy} from '@privy-io/react-auth';

function SignTypedDataButton() {
  const {signTypedData} = usePrivy();

  // Example from https://github.com/MetaMask/test-dapp/blob/285ef74eec90dbbb4994eff4ece8c81ba4fc77f9/src/index.js#L1585
  const domain = {
    name: 'Example Message',
    version: '1.0.0',
    chainId: 1,
    salt: '0',
    verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' as `0x${string}`,
  };

  // The named list of all type definitions
  const types = {
    Person: [
      {name: 'name', type: 'string'},
      {name: 'wallet', type: 'address'},
    ],
    Mail: [
      {name: 'from', type: 'Person'},
      {name: 'to', type: 'Person'},
      {name: 'contents', type: 'string'},
    ],
    // Necessary to define salt param type
    EIP712Domain: [
      {
        name: 'name',
        type: 'string',
      },
      {
        name: 'version',
        type: 'string',
      },
      {
        name: 'chainId',
        type: 'uint256',
      },
      {
        name: 'salt',
        type: 'string',
      },
      {
        name: 'verifyingContract',
        type: 'string',
      },
    ],
  };

  // The data to sign
  const value = {
    from: {
      name: 'Cow',
      wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
    },
    to: {
      name: 'Bob',
      wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
    },
    contents: 'Hello, Bob!',
  };

  const typedData = {primaryType: 'Mail', domain: domain, types: types, message: value};
  const uiConfig = {
    title: 'Sample title text',
    description: 'Sample description text',
    buttonText: 'Sample button text',
  };

  return (
    <button
      onClick={async () => {
        // Use `signature` below however you'd like
        const signature = await signTypedData(typedData, uiConfig);
      }}
    >
      Sign Typed Data
    </button>
  );
}