Sign Typed Data (EIP-712)

EIP-712 is an Ethereum standard for hashing and signing typed structured data. This feature allows you to sign more than just a basic string message. It enables you to sign structured data, which can be useful for validating complex objects in decentralized applications, ensuring data integrity, and preventing signature replay attacks.

In this section, we will explore how to implement EIP-712 message signing using this library.

🧱 1. Framework Usage (ES6/Module Bundlers)

πŸ“ Installation

If you haven’t already:

npm install multichain-wallet-connector

Or if you use Yarn:

yarn add multichain-wallet-connector

πŸ” A. Sign Typed Data Example

To sign structured data (e.g., user authentication, contract interactions), the EIP-712 standard requires the specification of a domain and message schema. Below is an example of how you can implement EIP-712 signing in your dApp:

import { connectWallet, signTypedData } from 'multichain-wallet-connector';

let provider, account;

async function connect() {
  const res = await connectWallet({ chainId: 1 });//Replace with your chainId
  provider = res.provider;
  account = res.account;
}

sync function SignTypedData() {
  if (!provider || !account) await connect();

  // Define EIP-712 domain and message
  const typedData = {
    types: {
      EIP712Domain: [
        { name: 'name', type: 'string' },
        { name: 'version', type: 'string' },
        { name: 'chainId', type: 'uint256' },
        { name: 'verifyingContract', type: 'address' },
      ],
      Person: [
        { name: 'name', type: 'string' },
        { name: 'wallet', type: 'address' },
      ],
      Mail: [
        { name: 'from', type: 'Person' },
        { name: 'to', type: 'Person' },
        { name: 'contents', type: 'string' },
      ],
    },
    primaryType: 'Mail',
    domain: {
      name: 'Ether Mail',
      version: '1',
      chainId: 1, // Mainnet
      verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC', // Your contract address here
    },
    message: {
      from: {
        name: 'Alice',
        wallet: '0x1a9e3f6a3bfe827bfc0e9f1a3d9a768f1e36ab7d', // Alice's address
      },
      to: {
        name: 'Bob',
        wallet: '0x2d122fEF1613e82C0C90f443b59E54468e16525C', // Bob's address
      },
      contents: 'Hello Bob, this is a signed message!',
    },
  };

  try {
    const signature = await signTypedData(provider, typedData, account);
    console.log('Signed Typed Data:', signature);
  } catch (error) {
    console.error('Error signing typed data:', error);
  }
}

πŸ’‘ What’s Happening Here:

  • EIP-712 Domain: This defines the context of the signature, like the contract address and chain ID.

  • Primary Type: The main structure being signed β€” in this case, a Mail object containing from, to, and contents.

  • Message: The actual data being signed. This includes the sender (from), recipient (to), and the message body (contents).

  • Provider & Account: As with other functions, we first establish the connection to the wallet.

For typescript users, declare as a module in your types file:

declare module 'multichain-wallet-connector';

🌐 2. CommonJS via CDN (HTML/Vanilla JS)

βœ… Embed Script

<script src="https://cdn.jsdelivr.net/gh/Nworah-Gabriel/multichain-wallet-connector@v1.0.3/dist/connector.umd.min.js"></script>

Ensure this is added before your custom scripts in the HTML body.

πŸ” A. Sign Typed Data Example (CDN)

<script>
  let provider, account;

  async function connect() {
    const res = await WalletConnector.connectWallet({ chainId: 1 }); // Mainnet
    provider = res.provider;
    account = res.account;
  }

  async function SignTypedData() {
    if (!provider || !account) await connect();

    const typedData = {
      types: {
        EIP712Domain: [
          { name: 'name', type: 'string' },
          { name: 'version', type: 'string' },
          { name: 'chainId', type: 'uint256' },
          { name: 'verifyingContract', type: 'address' },
        ],
        Person: [
          { name: 'name', type: 'string' },
          { name: 'wallet', type: 'address' },
        ],
        Mail: [
          { name: 'from', type: 'Person' },
          { name: 'to', type: 'Person' },
          { name: 'contents', type: 'string' },
        ],
      },
      primaryType: 'Mail',
      domain: {
        name: 'Ether Mail',
        version: '1',
        chainId: 1,
        verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC', // Replace with your contract address
      },
      message: {
        from: {
          name: 'Alice',
          wallet: '0x1a9e3f6a3bfe827bfc0e9f1a3d9a768f1e36ab7d',
        },
        to: {
          name: 'Bob',
          wallet: '0x2d122fEF1613e82C0C90f443b59E54468e16525C',
        },
        contents: 'Hello Bob, this is a signed message!',
      },
    };

    try {
      const signature = await WalletConnector.signTypedData(provider, typedData, account);
      console.log('Signed Typed Data:', signature);
    } catch (error) {
      console.error('Error signing typed data:', error);
    }
  }
</script>

<button onclick="SignTypedData()">Sign Typed Data</button>

✨ Key Features of EIP-712:

  • It allows you to type-check data that is being signed, which provides better security than traditional messages.

  • It helps mitigate replay attacks by including a domain separator (chain ID, contract address).

  • Structured data can be signed in a more user-friendly way, which improves the user experience in decentralized applications (dApps).

For production readiness, always wrap async calls with try/catch:

try {
  const signature = await WalletConnector.signMessage(provider, account, message);
} catch (error) {
  console.error("Signing failed:", error.message);
}

πŸ“š Summary

Feature
Method
Gas Required
Use Case

Sign Typed Data

signTypedData(provider, typedData, account)

❌ No

Authentication, contract signatures

Last updated