Simplifying Typed Data for Ethereum

Rob Whitaker
uPort
Published in
5 min readFeb 4, 2019

--

Introducing a new beta library for working with EIP712 structured data and signatures

The increasing popularity of the EIP712 typed data signature specification is great news for the security and usability of Ethereum for real world applications. At uPort, we believe that having a consistent specification for signing and verifying complex data that can be verified on-chain or off is essential for the viability of the Ethereum network, and a Self-Sovereign identity solution built on top of it.

To help developers integrate typed data conforming to the EIP712 spec into their projects, we’re happy to introduce the brand new EthTypedData library in beta: a collection of utilities for easily creating, signing, and verifying Structure Types and EIP712 Domains — in javascript.

$ npm i eth-typed-data

EIP712 in a Nutshell

In case you haven’t already fallen in love with this EIP, let’s start with a quick primer on signature schemes, and why they’re important. Often in the Ethereum world, an app or dapp may request a cyptographic signature from a user on a particular piece of data; this may be used for identity verification, or for authorization of some action on the user’s behalf. No matter the purpose, it is very important for a user to know what she is signing.

Since cryptographic signatures are usually only defined over blobs of binary data, and most users understand data only as part of some higher level data structure, we have a problem: Dapps define their own data schemas, and wallets don’t know how to decode them to inform the user what they’re signing. This can lead to all number of phishing attacks, in which a dapp may trick a user into signing a piece of data that can be used for a nefarious purpose.

The proposed solution is a standardized format for nesting complex data, and an unambiguous way of hashing and signing it. With such a format, Dapps can use JSON to request a signature on an arbitrary nested data structure, and an EIP712-compatible wallet will be able to both display a readable version for the user to approve, and produce a replicable and verifiable signature over the data. Even better, the signature scheme is based on the primitive types available in Solidity, so EIP712 signature can be computed and verified on-chain as well!

There’s only one problem — signature requests look like this:

And that’s just to say “Hello, world.”

For a gentle and more detailed introduction to the elements of an EIP712 signature request, be sure to check out this great article from Koh Wei Jie.

Needless to say, this is cumbersome and verbose, and is a huge barrier to widespread adoption. Why would developers take the time to construct an object an order of magnitude larger than the data they want signed, when users are fine with personal_sign already?

Enter EthTypedData: EIP712, Simplified

To mitigate the cost of poor usability traded for this enhanced security, we at uPort created the EthTypedData library to manage your EIP712-compatible data structures. EthTypedData provides two main classes that you can interact with: EIP712Domains and StructureTypes. We hope that this will ease the burden of implementing safe signature schemes in (d)apps across the stack, making web3 a safer place.

With that, let’s get into the weeds:

Domains

As per the almighty spec, every piece of data must be linked to a Domain, to guard against replay attacks, in which a signature for one Dapp is maliciously reused on another. Domains are defined by any combination of the following key-value pairs: name, version, verifyingContract, chainId, and salt.

Most dapps will only need one Domain, and so can instantiate their domain just once when the app is initialized:

Only do it once!

This is the entry point to the rest of the functionality offered by eth-typed-data, and all types you create will have an internal reference to the domain in which they were constructed.

Creating Structure Types

Once you have a domain, you can use it to create Structure Types, which compose the Solidity primitive types into arrays and structs. Once you create a type in your domain, other types can compose that type as well.

As you can see from this example, Structure Types can reference already- created types in the same domain, using the same string provided in the first argument to createType.

Using your Types

Once you have a set of types constructed, it’s time to use them. Each Structure Type class you create is a constructor of its own, and can be instantiated with new, and will validate that all input properties match their required types.* For nested structure types, you can pass an already constructed instance of a type, or a bare object which provides acceptable values for that structure’s members. Nested structure types will be validated recursively, so you can be sure that your instances are valid type values.

The individual type objects have all manner of convenience methods for working with typed data, including the essential toSignatureRequest for passing to an eth_signTypedData RPC, and methods to calculate the hashStruct and typeHash (see the spec), or even sign in the browser!

In this way, rather than constructing signature requests by hand, and hoping all the required fields are present, you can build your application around the types you need, which are guaranteed to only take valid values. Since the checks are performed at runtime rather than compile time, that leaves your application free to dynamically define types while your application is running, instead of limiting your app to data that can be defined in advance.

*Currently types are validated for structure, valid nesting, and the presence of each key, but individual primitive values (i.e. Solidity primitive types) do not have validators in terms of javascript primitives. This feature will be coming soon!

So now what?

By releasing the EthTypedData library alongside our recent support for EIP712 signature requests, uPort wants to bring typed structure data to the forefront of the Ethereum space. With applications to state channels, verifiable credentials, object capabilities, the possibilities of a signature scheme on arbitrary structured data are endless.

Now, let the complexity of signature requests no longer be a burden! Go forth and let the convenience of typed objects proliferate in your dapps, and bring usable security to the web3 universe! And let us know how it goes — file any bugs or feature requests in the issue tracker on github!

--

--

Rob Whitaker
uPort
Writer for

Software Engineer at uPort. Making complexity less complicated while building the decentralized future.