Guide
EVM
Sign Typed Data

Sign Typed data

JoyID implements the EIP-712 ↗ (opens in a new tab) standard for typed data signing. This allows users to sign a message that is human readable and can be verified on-chain. The TypedData struct is serialized and hashed to create the message that is signed.

The TypedData struct is defined as abitype (opens in a new tab).

Signing

You can sign a TypedData struct using the signTypedData function. This function takes a TypedData struct and returns a hex-encoded signature.

SignTypedData.tsx
import * as React from "react";
import { verifyTypedData, Hex } from "viem";
import { signTypedData } from "@joyid/evm";
import { sepolia } from "viem/chains";
 
const buildTypedData = (chainId: number) => {
  return {
    domain: {
      name: "Ether Mail",
      version: "1",
      chainId,
      verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
    },
    types: {
      Person: [
        { name: "name", type: "string" },
        { name: "wallet", type: "address" },
      ],
      Mail: [
        { name: "from", type: "Person" },
        { name: "to", type: "Person" },
        { name: "contents", type: "string" },
      ],
    },
    primaryType: "Mail",
    message: {
      from: {
        name: "Cow",
        wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
      },
      to: {
        name: "Bob",
        wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
      },
      contents: "Hello, Bob!",
    },
  } as const;
};
 
export const SignTypedData = ({ address }: { address: Hex | null }) => {
  const [signature, setSignature] = React.useState<Hex>("0x");
  const chainId = sepolia.id;
  const typedData = buildTypedData(chainId);
  const onSign = async () => {
    const sig = await signTypedData(typedData, address!);
    setSignature(sig);
  };
  const onVerify = async () => {
    try {
      const res = await verifyTypedData({
        ...typedData,
        address: address!,
        signature,
      });
      alert(res);
    } catch (error) {
      alert(error.message);
    }
  };
  return address ? (
    <div className="w-full">
      <h2 className="mb-4 text-lg text-center">Sign Type Data</h2>
      <label className="label">Signature:</label>
      <textarea
        className="textarea textarea-bordered w-full mb-4"
        placeholder="Signature"
        value={signature}
        disabled
      ></textarea>
 
      <button className="btn btn-primary mb-4 mr-4" onClick={onSign}>
        Sign
      </button>
 
      <button className="btn btn-primary btn-outline mb-4" onClick={onVerify}>
        Verify
      </button>
 
      <div className="divider"></div>
    </div>
  ) : null;
};
 

Verify Typed Data

Since the TypedData is signed using the EIP-712 standard, it can be verified using any EIP-712 compliant library.

import { verifyTypedData } from 'ethers/lib/utils'
import { signTypedData } from '@joyid/evm'
 
function buildTypedData(chainId: number) {
  return {
    domain: {
      name: "Ether Mail",
      version: "1",
      chainId,
      verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
    },
    types: {
      Person: [
        { name: "name", type: "string" },
        { name: "wallet", type: "address" },
      ],
      Mail: [
        { name: "from", type: "Person" },
        { name: "to", type: "Person" },
        { name: "contents", type: "string" },
      ],
    },
    primaryType: "Mail",
    message: {
      from: {
        name: "Cow",
        wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
      },
      to: {
        name: "Bob",
        wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
      },
      contents: "Hello, Bob!",
    },
  };
}
 
const typedData = buildTypedData(1)
 
function verify(address: string) {
  const signature = await signTypedData(typedData, address);
  const res = verifyTypedData(
    typedData.domain,
    typedData.types,
    typedData.message,
    signature,
  )
}

API Reference

Try it out