UNPKG

react-native-eth-typed-data

Version:

A library to simplifiy interacting with and signing EIP712 typed data

343 lines (276 loc) 15.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = EIP712Domain; exports.EIP712DomainProperties = void 0; var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _ethers = require("ethers"); var _buffer = require("buffer"); var _AbstractType2 = _interopRequireDefault(require("./AbstractType")); var _Type = _interopRequireDefault(require("./Type")); var _primitives = require("./primitives"); function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } // The set of properties that a EIP712Domain MAY implement var EIP712DomainProperties = [{ name: "name", type: "string" }, { name: "version", type: "string" }, { name: "chainId", type: "uint256" }, { name: "verifyingContract", type: "address" }, { name: "salt", type: "string" }]; /** * A constructor/factory function which constructs EIP712 Domains as types, * then returns a new instance of the domain. Since an instantiated EIP712 * Domain needs to share many static methods with the types it contains * (e.g. encodeType, typeHash), but does not require that all properties * are present in a given instance, we define a new prototype for each Domain * which specifies the provided properties as its type definition, and then * instantiates that prototype with the provided values. * @param {Object} def The definition of the EIP712 domain * @returns {Object} An instantiated EIP712Domain type with the specified properties */ exports.EIP712DomainProperties = EIP712DomainProperties; function EIP712Domain(def) { var vals = {}; // Extract the EIP712 domain properties that were provided var properties = EIP712DomainProperties.reduce(function (props, _ref) { var name = _ref.name, type = _ref.type; // Skip unused EIP712 types if (!(name in def)) return props; // Validate primitive types vals[name] = _primitives.validate[type](def[name]); // Include property in type definition return [].concat((0, _toConsumableArray2["default"])(props), [{ name: name, type: type }]); }, []); // Throw an error if extra properties were provided if (Object.keys(vals).length !== Object.keys(def).length) { throw new Error('Extra key in EIP712Domain definition'); } else if (Object.keys(def).length === 0) { throw new Error('Must supply at least one EIP712Domain property'); } /** * @classdesc * A domain is a scope in which we can define multiple Types which can * reference each other. A domain behaves similarly to a Type, with the * primary difference that it also includes a set of types that can * reference each other within it. The primary method of a * domain is `createType()` which will return a new constructor * for a type that lives within this domain. */ var Domain = /*#__PURE__*/function (_AbstractType) { (0, _inherits2["default"])(Domain, _AbstractType); var _super = _createSuper(Domain); function Domain(vals) { var _this; (0, _classCallCheck2["default"])(this, Domain); _this = _super.call(this); _this.vals = _objectSpread({}, vals); // The types object maps String names to the type prototypes that exist // within this domain. Prototypes are appendended to this.types for every // call to this.createType() _this.types = {}; // Precompute the domainSeparator for use with signing types in this domain _this.domainSeparator = _this.hashStruct(); /** * Construct a new type that will be associated with this domain * @returns {Function} the constructor for the new type class */ _this.createType = _Type["default"].bind((0, _assertThisInitialized2["default"])(_this)); return _this; } /** * Validate that a particular object conforms to a valid type definition in this domain, * and return a standardized version of input value. In particular, structure types will * be coerced to an instance of the corresponding structure class, array types will validate * each item according to the base type of the array, and primitive types will be validated * by the appropriate validator in `validatePrimitive` * * @param {String} type the string name of the type of the value being validated * @param {Any} val the candidate value of the type to be validated/standardized * @returns {Any} the standardized/validated representation of this * * @throws {Error} if the input is an invalid instance of the given type */ (0, _createClass2["default"])(Domain, [{ key: "validate", value: function validate(type, val) { var _this2 = this; if ((0, _primitives.isArrayType)(type)) { // Apply the validator to each item in an array, using the base type return val.map(function (item) { return _this2.validate((0, _primitives.getElementaryType)(type), item); }); } else if ((0, _primitives.isPrimitiveType)(type)) { return _primitives.validate[type](val); } else if (type in this.types) { var StructType = this.types[type]; return val instanceof StructType ? val : new StructType(val); } else { throw new Error("Type ".concat(type, " not recognized in this domain")); } } /** * Recursively expand an object containing instances of structure type classes, * and return a bare javascript object with the same hierarchical structure * Conceptually the opposite of @see this.validate * @param {String} type the string name of the type of value being serialized * @param {Any} val the type instance or primitive literal being serialized * @returns {Object} * @throws {Error} */ }, { key: "serialize", value: function serialize(type, val) { var _this3 = this; if (type in this.types) { // Recursively expand nested structure types return val.toObject(); } else if ((0, _primitives.isArrayType)(type)) { // Map serializer to array types return val.map(function (item) { return _this3.serialize((0, _primitives.getElementaryType)(type), item); }); } else if ((0, _primitives.isPrimitiveType)(type)) { return val; } else { throw new Error("Type ".concat(type, " is not a valid type in this domain")); } } /** * Return an object mapping the names of types contained by this domain * to their list-style type definitions * @returns {Object} Mapping from type name -> type definition */ }, { key: "listTypes", value: function listTypes() { var _this4 = this; return Object.keys(this.types).reduce(function (obj, t) { return _objectSpread(_objectSpread({}, obj), {}, (0, _defineProperty2["default"])({}, t, _this4.types[t].typeDef())); }, {}); } /** * Concatenate the type definition for this domain, with * the definition of all the types that it contains * @returns {Object} */ }, { key: "toDomainDef", value: function toDomainDef() { return _objectSpread((0, _defineProperty2["default"])({}, this.constructor.name, this.constructor.typeDef()), this.listTypes()); } /** * @override * A simplified encodeData function that only needs to handle string * and atomic types. Still defers to abi.rawEncode. * @returns {String} encoding of the definition of this Domain */ }, { key: "encodeData", value: function encodeData() { var _this5 = this; var types = this.constructor.properties.map(function (_ref2) { var type = _ref2.type; return type === 'string' ? 'bytes32' : type; }); var values = this.constructor.properties.map(function (_ref3) { var name = _ref3.name, type = _ref3.type; return type === 'string' ? _buffer.Buffer.from(_ethers.ethers.utils.keccak256(_ethers.ethers.utils.toUtf8Bytes(_this5.vals[name])).substring(2), 'hex') : _this5.vals[name]; }); return _buffer.Buffer.from(_ethers.ethers.utils.defaultAbiCoder.encode(['bytes32'].concat((0, _toConsumableArray2["default"])(types)), [_buffer.Buffer.from(this.constructor.typeHash(), 'hex')].concat((0, _toConsumableArray2["default"])(values))).substring(2), 'hex'); } /** * @override */ }, { key: "toObject", value: function toObject() { return _objectSpread({}, this.vals); } }]); return Domain; }(_AbstractType2["default"]); (0, _defineProperty2["default"])(Domain, "name", 'EIP712Domain'); (0, _defineProperty2["default"])(Domain, "properties", properties); (0, _defineProperty2["default"])(Domain, "dependencies", []); return new Domain(vals); } /** * Create a message object and domain from a raw signature request object. * @param {Object} request An object representing a signature request * @returns {Object} the constructed {domain} and {message} instances * @throws {Error} if signature request contains cyclic dependencies */ EIP712Domain.fromSignatureRequest = function fromSignatureRequest(request) { var types = request.types, rawMessage = request.message, primaryType = request.primaryType, rawDomain = request.domain; // Create the domain instance var domain = new EIP712Domain(rawDomain); // Perform a (reverse) topological sort for dependency resolution, keeping track of depth first search postorder var postorder = []; // Keep track of already visited types, as well as potential cycles var marked = new Set(), cyclecheck = new Set(); // Define recursive depth-first search with cycle detection var dfs = function dfs(type) { var _iterator = _createForOfIteratorHelper(types[type]), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var subtype = _step.value.type; if (marked.has(subtype) || (0, _primitives.isNotStructureType)(subtype)) continue; if (cyclecheck.has(subtype)) { throw new Error('Cannot construct domain from signature request with cyclic dependencies'); } cyclecheck.add(subtype); dfs(subtype); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } postorder.push(type); marked.add(type); }; // Perform the search for (var _i = 0, _Object$keys = Object.keys(types); _i < _Object$keys.length; _i++) { var type = _Object$keys[_i]; if (type !== 'EIP712Domain' && !marked.has(type)) { dfs(type); } } // Create all necessary structure types in this domain // Iterate in postorder to guarantee dependencies are satisfied for (var _i2 = 0, _postorder = postorder; _i2 < _postorder.length; _i2++) { var name = _postorder[_i2]; if (name !== 'EIP712Domain') { domain.createType(name, types[name]); } } // Create the message instance var MessageType = domain.types[primaryType]; var message = new MessageType(rawMessage); return { domain: domain, message: message }; };