react-native-eth-typed-data
Version:
A library to simplifiy interacting with and signing EIP712 typed data
343 lines (276 loc) • 15.4 kB
JavaScript
;
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
};
};