Giter Site home page Giter Site logo

mrtenz / eip-712 Goto Github PK

View Code? Open in Web Editor NEW
108.0 7.0 12.0 1.19 MB

Tiny library with utility functions that can help with signing and verifying EIP-712 based messages

License: MIT License

JavaScript 0.63% TypeScript 99.37%
ethereum eip712 signtypeddata

eip-712's Introduction

eip-712

Version License Actions Status codecov

This is a library for Node.js and web browsers with some utility functions that can help with signing and verifying EIP-712 based messages. It is fully written in TypeScript, and is currently only compatible with the latest specification of EIP-712 (eth_signTypedData_v4).

https://eips.ethereum.org/EIPS/eip-712

Note that this library currently does not handle the signing itself. For this, you can use something like Ethers.js or ethereumjs-util. For examples, please see the examples folder.

Installation

You can install this library with Yarn or NPM:

$ yarn add eip-712
$ npm install eip-712

There is a CommonJS version as well as an ES6 version available. Most tools should automatically pick the right version (e.g. Node.js, Webpack).

Getting Started

First, define your typed data as a JSON object, according to the JSON schema specified by EIP-712. For example:

{
  "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"
  },
  "message": {
    "from": {
      "name": "Cow",
      "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
    },
    "to": {
      "name": "Bob",
      "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
    },
    "contents": "Hello, Bob!"
  }
}

Functions

Here is a brief description of the functions available in this library. For more detailed examples, you can refer to src/eip-712.test.ts, or to the examples in the examples folder.

getMessage(typedData, hash?)

This function will return the full EIP-191 encoded message to be signed as Buffer, for the typed data specified. If hash is enabled, the message will be hashed using Keccak256.

import { getMessage } from 'eip-712';

const typedData = { /*...*/ };
console.log(getMessage(typedData).toString('hex')); // 1901f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090fc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e
console.log(getMessage(typedData, true).toString('hex')); // be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2

asArray(typedData)

This function returns the typed data as an array. This can be useful for encoding typed data as ABI.

import { asArray } from 'eip-712';

const typedData = { /*...*/ };
console.log(asArray(typedData)); // [ ['Cow', '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826'], ['Bob', '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB'], 'Hello, Bob!' ]

getStructHash(typedData, type, data)

This function returns a Keccak-256 hash for a single struct type (e.g. EIP712Domain, Person or Mail).

import { getStructHash } from 'eip-712';

const typedData = { /*...*/ };
console.log(getStructHash(typedData, 'EIP712Domain', typedData.domain).toString('hex')); // f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f

encodeData(typedData, type, data)

This function returns the raw ABI encoded data for the struct type.

import { encodeData } from 'eip-712';

const typedData = { /*...*/ };
console.log(encodeData(typedData, 'EIP712Domain', typedData.domain).toString('hex')); // 8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400fc70ef06638535b4881fafcac8287e210e3769ff1a8e91f1b95d6246e61e4d3c6c89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc60000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cccccccccccccccccccccccccccccccccccccccc

getTypeHash(typedData, type)

This function returns the type hash for a struct type. This is the same as Keccak256(EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)), with optional sub-types automatically included too.

import { getTypeHash } from 'eip-712';

const typedData = { /*...*/ };
console.log(getTypeHash(typedData, 'EIP712Domain').toString('hex')); // 8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f

encodeType(typedData, type)

This function returns the type before hashing it, e.g. EIP712Domain(string name,string version,uint256 chainId,address verifyingContract), with optional sub-types automatically included too.

import { encodeType } from 'eip-712';

const typedData = { /*...*/ };
console.log(encodeType(typedData, 'EIP712Domain')); // EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)

getDependencies(typedData, type)

This function returns all sub-types used by a struct as a string array. If the struct has no sub-types (like EIP712Domain), an array with only the type itself is returned.

import { getDependencies } from 'eip-712';

const typedData = { /*...*/ };
console.log(getDependencies(typedData, 'EIP712Domain')); // ['EIP712Domain']
console.log(getDependencies(typedData, 'Mail')); // ['Mail', 'Person']

Non-standard domains (e.g., CIP-23)

It's possible to use a custom domain format, like from the CIP-23 specification, or to disable verifying the domain format completely, if you want to use a custom implementation of EIP-712.

To do this, you can pass options to each function, as last parameter. For example, in order to get the message hash for a message, you can do the following:

import { getMessage } from 'eip-712';
import type { Options } from 'eip-712';

const typedData = { /*...*/ };
const options: Options = {
  // A custom domain identifier. Defaults to 'EIP712Domain'.
  domain: 'CIP23Domain',

  // Whether to verify the structure of the domain. Defaults to 'true'.
  verifyDomain: false
};

console.log(getMessage(typedData, true, options).toString('hex'));

eip-712's People

Contributors

dependabot[bot] avatar mrtenz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

eip-712's Issues

2nd-order nesting fails to generate correct type string

Maybe related to #34

I am able to successfully test several payloads, but all payloads with 2nd-order nesting fail, specifically meaning they do not match the hashes from ethers-eip712, which are correct.

I think there may be multiple issues, but the first one I found is that encodeType is not working properly for 2nd-order nesting. For example, I am using the following message:

const msg = {
  types: {
    EIP712Domain: [
      { name: 'name', type: 'string' },
      { name: 'version', type: 'string' },
      { name: 'chainId', type: 'uint256' },
      { name: 'verifyingContract', type: 'address' }
    ],
    Wallet: [
      { name: 'balance', type: 'Balance' },
    ],
    Person: [
      { name: 'wallet', type: 'Wallet' }
    ],
    Mail: [
      { name: 'from', type: 'Person' },
    ],
    Balance: [
      { name: 'currency', type: 'string' }
    ]
  },
  primaryType: 'Mail',
  domain: {
    name: 'Ether Mail',
    version: '1',
    chainId: 12,
    verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC'
  },
  message: {
    from: {
      wallet: {
        balance: {
          currency: 'ETH',
        }
      },
    },
  }
};

Here are the full type names I get for primaryType:

  • ethers-eip712 (correct): Mail(Person from)Person(Wallet wallet)Wallet(Balance balance)Balance(string currency)
  • eip-712 (incorrect): Mail(Person from)Balance(string currency)Person(Wallet wallet)Wallet(Balance balance)

It appears there is an edge case somewhere in your encodeType function, which differs significantly from that of ethers-eip712. Note that payloads with 1st order nesting work just fine.

EIP712 - encoding array types throws an error

Hi,
it seems like the encoding for array fields doesn't work.
Example for the typedData

{
  "types": {
    "EIP712Domain": [
      {
        "name": "name",
        "type": "string"
      },
      {
        "name": "version",
        "type": "string"
      },
      {
        "name": "chainId",
        "type": "uint256"
      },
      {
        "name": "verifyingContract",
        "type": "address"
      },
      {
        "name": "salt",
        "type": "bytes32"
      }
    ],
    "Person": [
      {
        "name": "name",
        "type": "string"
      },
      {
        "name": "wallet",
        "type": "address"
      }
    ],
    "Mail": [
      {
        "name": "from",
        "type": "Person[]"
      },
      {
        "name": "to",
        "type": "Person[]"
      }
    ]
  },
  "primaryType": "Mail",
  "domain": {
    "name": "Ether Mail",
    "version": "1",
    "chainId": 3,
    "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
    "salt": "0x00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
  },
  "message": {
    "from": [
      {
        "name": "Alice",
        "wallet": "0xaaAAaaaAaaAAAAaaaaaAaaAAAaAAAAAAAaaAaaAA"
      }
    ],
    "to": [
      {
        "name": "Alice",
        "wallet": "0xaaAAaaaAaaAAAAaaaaaAaaAAAaAAAAAAAaaAaaAA"
      },
      {
        "name": "Bob",
        "wallet": "0xaaAAaaaAaaAAAAaaaaaAaaAAAaAAAAAAAaaAaaAA"
      }
    ]
  }
}

I used the following commands

const eip712 = require('eip-712');
eip712.getMessage(message, false);

And got the following error

Uncaught TypeError: Cannot read property 'map' of undefined
    at /app_dir/app/node_modules/eip-712/lib/cjs/eip-712.js:39:57
    at Array.map (<anonymous>)
    at encodeType (/app_dir/app/node_modules/eip-712/lib/cjs/eip-712.js:38:16)
    at getTypeHash (/app_dir/app/node_modules/eip-712/lib/cjs/eip-712.js:46:32)
    at encodeData (/app_dir/app/node_modules/eip-712/lib/cjs/eip-712.js:96:21)
    at getStructHash (/app_dir/app/node_modules/eip-712/lib/cjs/eip-712.js:103:32)
    at Object.getMessage (/app_dir/app/node_modules/eip-712/lib/cjs/eip-712.js:109:110)

Failed with recursive data

For example

{
        CIP23Domain: [
          { name: 'name', type: 'string' },
          { name: 'version', type: 'string' },
          { name: 'chainId', type: 'uint256' },
          { name: 'verifyingContract', type: 'address' }
        ],
        Person: [
          { name: 'name', type: 'string' },
          { name: 'mother', type: 'Person' },
          { name: 'father', type: 'Person' }
        ]
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.