Personal Signatures

Personal Signatures

Magic offers out-of-the-box UI when the user is prompted to sign a personal or typed message for the following EVM RPC methods:

  • personal_sign
  • signTypedData_v3
  • signTypedData_v4

These methods allow dapps to verfiably prove the ownership of the user's account through getting a signature from their private key and using it to sign arbitrary and/or typed data. It is also possible to get a signature from the user during login (in one step) through our login with verfication feature.

#Use Cases

  • Used to prove verifiable ownership of a public address through signging arbitirary data provided by the dapp.
  • Used in various scenarios where a user needs to sign a structured message as proof of their approval.

#Usage

After following the quickstart to ensure Magic SDK is setup correctly and the user has succcesfully logged in, you can then request consent to collect their information as follows:

Personal Signature (personal_sign)

Javascript
01import Web3 from "web3";
02import { Magic } from "magic-sdk";
03import { recoverPersonalSignature } from "@metamask/eth-sig-util";
04
05const magic = new Magic("YOUR_API_KEY", {
06  network: "goerli",
07});
08const web3 = new Web3(magic.rpcProvider);
09
10const signAndVerify = async () => {
11  const signedMessage = await web3.eth.personal.sign(
12    "Here is a basic message!",
13    account,
14    ""
15  );
16  console.log("signedMessage:", signedMessage);
17  // recover the public address of the signer to verify
18  const recoveredAddress = recoverPersonalSignature({
19    data: message,
20    signature: signedMessage,
21  });
22  console.log(
23    recoveredAddress.toLocaleLowerCase() === account.toLocaleLowerCase()
24      ? "Signing success!"
25      : "Signing failed!"
26  );
27};

Typed Data V3 (signTypedData_v3)

Javascript
01import Web3 from "web3";
02import { Magic } from "magic-sdk";
03import { recoverTypedSignature } from "@metamask/eth-sig-util";
04
05const magic = new Magic("YOUR_API_KEY", {
06  network: "goerli",
07});
08const web3 = new Web3(magic.rpcProvider);
09
10export const signTypedDataV3Payload = {
11  types: {
12    EIP712Domain: [
13      {
14        name: "name",
15        type: "string",
16      },
17      {
18        name: "version",
19        type: "string",
20      },
21      {
22        name: "verifyingContract",
23        type: "address",
24      },
25    ],
26    Greeting: [
27      {
28        name: "contents",
29        type: "string",
30      },
31    ],
32  },
33  primaryType: "Greeting",
34  domain: {
35    name: "Magic",
36    version: "1",
37    verifyingContract: "0xE0cef4417a772512E6C95cEf366403839b0D6D6D",
38  },
39  message: {
40    contents: "Hello, from Magic!",
41  },
42};
43
44const signAndVerify = async () => {
45  const params = [account, signTypedDataV3Payload];
46  const method = "eth_signTypedData_v4";
47  const signature = await magic.rpcProvider.request({
48    method,
49    params,
50  });
51  console.log("signature:", signature);
52  // recover the public address of the signer to verify
53  const recoveredAddress = recoverTypedSignature({
54    data: signTypedDataV3Payload,
55    signature,
56    version: "V3",
57  });
58
59  console.log(
60    recoveredAddress.toLocaleLowerCase() === account.toLocaleLowerCase()
61      ? "Signing success!"
62      : "Signing failed!"
63  );
64};

Typed Data V4 (signTypedData_v4)

Javascript
01import Web3 from "web3";
02import { Magic } from "magic-sdk";
03import { recoverPersonalSignature } from "@metamask/eth-sig-util";
04
05const magic = new Magic("YOUR_API_KEY", {
06  network: "goerli",
07});
08const web3 = new Web3(magic.rpcProvider);
09
10export const signTypedDataV4Payload = {
11  domain: {
12    // Defining the chain aka Rinkeby goerli or Ethereum Main Net
13    chainId: 5,
14    // Give a user friendly name to the specific contract you are signing for.
15    name: "Ether Mail",
16    // If name isn't enough add verifying contract to make sure you are establishing contracts with the proper entity
17    verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
18    // Just let's you know the latest version. Definitely make sure the field name is correct.
19    version: "1",
20  },
21
22  // Defining the message signing data content.
23  message: {
24    /*
25     - Anything you want. Just a JSON Blob that encodes the data you want to send
26     - No required fields
27     - This is DApp Specific
28     - Be as explicit as possible when building out the message schema.
29    */
30    contents: "Hello, Bob!",
31    attachedMoneyInEth: 4.2,
32    from: {
33      name: "Cow",
34      wallets: [
35        "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
36        "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF",
37      ],
38    },
39    to: [
40      {
41        name: "Bob",
42        wallets: [
43          "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
44          "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57",
45          "0xB0B0b0b0b0b0B000000000000000000000000000",
46        ],
47      },
48    ],
49  },
50  // Refers to the keys of the *types* object below.
51  primaryType: "Mail",
52  types: {
53    // TODO: Clarify if EIP712Domain refers to the domain the contract is hosted on
54    EIP712Domain: [
55      { name: "name", type: "string" },
56      { name: "version", type: "string" },
57      { name: "chainId", type: "uint256" },
58      { name: "verifyingContract", type: "address" },
59    ],
60    // Not an EIP712Domain definition
61    Group: [
62      { name: "name", type: "string" },
63      { name: "members", type: "Person[]" },
64    ],
65    // Refer to PrimaryType
66    Mail: [
67      { name: "from", type: "Person" },
68      { name: "to", type: "Person[]" },
69      { name: "contents", type: "string" },
70    ],
71    // Not an EIP712Domain definition
72    Person: [
73      { name: "name", type: "string" },
74      { name: "wallets", type: "address[]" },
75    ],
76  },
77};
78
79const signAndVerify = async () => {
80  const params = [account, signTypedDataV3Payload];
81  const method = "eth_signTypedData_v4";
82  const signature = await magic.rpcProvider.request({
83    method,
84    params,
85  });
86  console.log("signature:", signature);
87  // recover the public address of the signer to verify
88  const recoveredAddress = recoverTypedSignature({
89    data: signTypedDataV3Payload,
90    signature,
91    version: "V4",
92  });
93
94  console.log(
95    recoveredAddress.toLocaleLowerCase() === account.toLocaleLowerCase()
96      ? "Signing success!"
97      : "Signing failed!"
98  );
99};

#Configuration

#Reference