UNPKG

eip-712

Version:

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

106 lines (89 loc) 3.3 kB
import { array, assign, Infer, number, object, optional, pattern, record, refine, string, union } from 'superstruct'; export const TYPE_REGEX = /^\w+/; export const ARRAY_REGEX = /^(.*)\[([0-9]*?)]$/; export const BYTES_REGEX = /^bytes([0-9]{1,2})$/; export const NUMBER_REGEX = /^u?int([0-9]{0,3})$/; export const STATIC_TYPES = ['address', 'bool', 'bytes', 'string']; const TYPE = refine(string(), 'Type', (type, context) => { return isValidType(context.branch[0].types, type); }); export const EIP_712_TYPE = object({ name: string(), type: TYPE }); /** * A single type, as part of a struct. The `type` field can be any of the EIP-712 supported types. Currently those are: * - Atomic types: bytes1..32, uint8..256, int8..256, bool, address * - Dynamic types: bytes, string * - Reference types: array type (e.g. uint8[], SomeStruct[]), struct type (e.g. SomeStruct) * * Note that the `uint` and `int` aliases like in Solidity, and fixed point numbers are not supported by the EIP-712 * standard. */ export type EIP712Type = Infer<typeof EIP_712_TYPE>; export const EIP_712_DOMAIN_TYPE = object({ name: optional(string()), version: optional(string()), chainId: optional(union([string(), number()])), verifyingContract: optional(pattern(string(), /^0x[0-9a-z]{40}$/i)), salt: optional(union([array(number()), pattern(string(), /^0x[0-9a-z]{64}$/i)])) }); /** * The EIP712 domain struct. Any of these fields are optional, but it must contain at least one field. */ export type EIP712Domain = Infer<typeof EIP_712_DOMAIN_TYPE>; export const EIP_712_TYPED_DATA_TYPE = object({ types: record(string(), array(EIP_712_TYPE)), primaryType: string(), domain: object(), message: object() }); export const EIP_712_STRICT_TYPED_DATA_TYPE = assign( EIP_712_TYPED_DATA_TYPE, object({ domain: EIP_712_DOMAIN_TYPE }) ); /** * The complete typed data, with all the structs, domain data, primary type of the message, and the message itself. */ export type TypedData = Infer<typeof EIP_712_TYPED_DATA_TYPE>; export type StrictTypedData = Infer<typeof EIP_712_STRICT_TYPED_DATA_TYPE>; /** * Checks if a type is valid with the given `typedData`. The following types are valid: * - Atomic types: bytes1..32, uint8..256, int8..256, bool, address * - Dynamic types: bytes, string * - Reference types: array type (e.g. uint8[], SomeStruct[]), struct type (e.g. SomeStruct) * * The `uint` and `int` aliases like in Solidity are not supported. Fixed point numbers are not supported. */ export const isValidType = (types: Record<string, unknown>, type: string): boolean => { if (STATIC_TYPES.includes(type as string)) { return true; } if (types[type]) { return true; } if (type.match(ARRAY_REGEX)) { const match = type.match(TYPE_REGEX); if (match) { const innerType = match[0]; return isValidType(types, innerType); } } const bytesMatch = type.match(BYTES_REGEX); if (bytesMatch) { const length = Number(bytesMatch[1]); if (length >= 1 && length <= 32) { return true; } } const numberMatch = type.match(NUMBER_REGEX); if (numberMatch) { const length = Number(numberMatch[1]); if (length >= 8 && length <= 256 && length % 8 === 0) { return true; } } return false; };