UNPKG

@ledgerhq/hw-app-eth

Version:
188 lines • 8.17 kB
import BigNumber from "bignumber.js"; import { hexBuffer, intAsHexBytes } from "../../utils"; /** * @ignore for the README * * A Map of helpers to get the wanted binary value for * each type of array possible in a type definition */ var EIP712_ARRAY_TYPE_VALUE; (function (EIP712_ARRAY_TYPE_VALUE) { EIP712_ARRAY_TYPE_VALUE[EIP712_ARRAY_TYPE_VALUE["DYNAMIC"] = 0] = "DYNAMIC"; EIP712_ARRAY_TYPE_VALUE[EIP712_ARRAY_TYPE_VALUE["FIXED"] = 1] = "FIXED"; })(EIP712_ARRAY_TYPE_VALUE || (EIP712_ARRAY_TYPE_VALUE = {})); /** * @ignore for the README * * A Map of helpers to get the id and size to return for each * type that can be used in EIP712 */ export const EIP712_TYPE_PROPERTIES = { CUSTOM: { key: () => 0, sizeInBits: () => null, }, INT: { key: () => 1, sizeInBits: size => Number(size) / 8, }, UINT: { key: () => 2, sizeInBits: size => Number(size) / 8, }, ADDRESS: { key: () => 3, sizeInBits: () => null, }, BOOL: { key: () => 4, sizeInBits: () => null, }, STRING: { key: () => 5, sizeInBits: () => null, }, BYTES: { key: size => (typeof size !== "undefined" ? 6 : 7), sizeInBits: size => (typeof size !== "undefined" ? Number(size) : null), }, }; /** * @ignore for the README * * A Map of encoders to transform a value to formatted buffer */ export const EIP712_TYPE_ENCODERS = { INT(value, sizeInBits = 256) { const failSafeValue = value !== null && value !== void 0 ? value : "0"; if (typeof failSafeValue === "string" && (failSafeValue === null || failSafeValue === void 0 ? void 0 : failSafeValue.startsWith("0x"))) { return hexBuffer(failSafeValue); } let valueAsBN = new BigNumber(failSafeValue); // If negative we'll use `two's complement` method to // "reversibly convert a positive binary number into a negative binary number with equivalent (but negative) value". // thx wikipedia if (valueAsBN.lt(0)) { const sizeInBytes = sizeInBits / 8; // Creates BN from a buffer serving as a mask filled by maximum value 0xff const maskAsBN = new BigNumber(`0x${Buffer.alloc(sizeInBytes, 0xff).toString("hex")}`); // two's complement version of value valueAsBN = maskAsBN.plus(valueAsBN).plus(1); } const paddedHexString = valueAsBN.toString(16).length % 2 ? "0" + valueAsBN.toString(16) : valueAsBN.toString(16); return Buffer.from(paddedHexString, "hex"); }, UINT(value) { return this.INT(value); }, BOOL(value) { return this.INT(typeof value === "boolean" ? Number(value).toString() : value); }, ADDRESS(value) { // Only sending the first 10 bytes (why ?) return hexBuffer(value !== null && value !== void 0 ? value : "").slice(0, 20); }, STRING(value) { return Buffer.from(value !== null && value !== void 0 ? value : "", "utf-8"); }, BYTES(value, sizeInBits) { const failSafeValue = value !== null && value !== void 0 ? value : ""; // Why slice again ? return hexBuffer(failSafeValue).slice(0, sizeInBits !== null && sizeInBits !== void 0 ? sizeInBits : ((failSafeValue === null || failSafeValue === void 0 ? void 0 : failSafeValue.length) - 2) / 2); }, }; /** * @ignore for the README * * Helper parsing an EIP712 Type name to return its type and size(s) * if it's an array or nested arrays * * @see EIP712MessageTypes * * @example "uint8[2][][4]" => [{name: "uint", bits: 8}, [2, null, 4]] * @example "bool" => [{name: "bool", bits: null}, []] * * @param {String} typeName * @returns {[{ name: string; bits: Number | null }, Array<Number | null | undefined>]} */ export const destructTypeFromString = (typeName) => { // Will split "any[][1][10]" in "any", "[][1][10]" const splitNameAndArraysRegex = new RegExp(/^([^[\]]*)(\[.*\])*/g); // Will match all numbers (or null) inside each array. [0][10][] => [0,10,null] const splitArraysRegex = new RegExp(/\[(\d*)\]/g); // Will separate the the name from the potential bits allocation. uint8 => [uint,8] const splitNameAndNumberRegex = new RegExp(/(\D*)(\d*)/); const [, type, maybeArrays] = splitNameAndArraysRegex.exec(typeName || "") || []; const [, name, bits] = splitNameAndNumberRegex.exec(type || "") || []; const typeDescription = name ? { name, bits: bits ? Number(bits) : undefined } : null; const arrays = maybeArrays ? [...maybeArrays.matchAll(splitArraysRegex)] : []; // Parse each size to either a Number or null const arraySizes = arrays.map(([, size]) => (size ? Number(size) : null)); return [typeDescription, arraySizes]; }; /** * @ignore for the README * * Helper to construct the hexadecimal ByteString for the description * of a field in an EIP712 Message * * @param isArray * @param typeSize * @param typeValue * @returns {String} HexByteString */ export const constructTypeDescByteString = (isArray, typeSize, typeValue) => { if (typeValue >= 16) { throw new Error("Eth utils - constructTypeDescByteString - Cannot accept a typeValue >= 16 because the typeValue can only be 4 bits in binary" + { isArray, typeSize, typeValue }); } // 1 is array, 0 is not array const isArrayBit = isArray ? "1" : "0"; // 1 has type size, 0 has no type size const hasTypeSize = typeof typeSize === "number" ? "1" : "0"; // 2 unused bits const unusedBits = "00"; // type key as 4 bits const typeValueBits = typeValue.toString(2).padStart(4, "0"); return intAsHexBytes(parseInt(isArrayBit + hasTypeSize + unusedBits + typeValueBits, 2), 1); }; /** * @ignore for the README * * Helper to create the buffer to describe an EIP712 types' entry structure * * @param {EIP712MessageTypesEntry} entry * @returns {Buffer} */ export const makeTypeEntryStructBuffer = ({ name, type }) => { var _a, _b, _c, _d; const [typeDescription, arrSizes] = destructTypeFromString(type); const isTypeAnArray = Boolean(arrSizes.length); const typeProperties = EIP712_TYPE_PROPERTIES[((_a = typeDescription === null || typeDescription === void 0 ? void 0 : typeDescription.name) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || ""] || EIP712_TYPE_PROPERTIES.CUSTOM; const typeKey = typeProperties.key(typeDescription === null || typeDescription === void 0 ? void 0 : typeDescription.bits); const typeSizeInBits = typeProperties.sizeInBits(typeDescription === null || typeDescription === void 0 ? void 0 : typeDescription.bits); const typeDescData = constructTypeDescByteString(isTypeAnArray, typeSizeInBits, typeKey); const bufferArray = [Buffer.from(typeDescData, "hex")]; if (typeProperties === EIP712_TYPE_PROPERTIES.CUSTOM) { bufferArray.push(Buffer.from(intAsHexBytes((_c = (_b = typeDescription === null || typeDescription === void 0 ? void 0 : typeDescription.name) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0, 1), "hex")); bufferArray.push(Buffer.from((_d = typeDescription === null || typeDescription === void 0 ? void 0 : typeDescription.name) !== null && _d !== void 0 ? _d : "", "utf-8")); } if (typeof typeSizeInBits === "number") { bufferArray.push(Buffer.from(intAsHexBytes(typeSizeInBits, 1), "hex")); } if (isTypeAnArray) { bufferArray.push(Buffer.from(intAsHexBytes(arrSizes.length, 1), "hex")); arrSizes.forEach(size => { if (typeof size === "number") { bufferArray.push(Buffer.from(intAsHexBytes(EIP712_ARRAY_TYPE_VALUE.FIXED, 1), "hex"), Buffer.from(intAsHexBytes(size, 1), "hex")); } else { bufferArray.push(Buffer.from(intAsHexBytes(EIP712_ARRAY_TYPE_VALUE.DYNAMIC, 1), "hex")); } }); } bufferArray.push(Buffer.from(intAsHexBytes(name.length, 1), "hex"), Buffer.from(name, "utf-8")); return Buffer.concat(bufferArray); }; //# sourceMappingURL=utils.js.map