From 9f01c9d7922b717ddda3aa894c38fbba623e8bdf Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Wed, 17 Aug 2022 12:26:00 -0500 Subject: [PATCH] Replace heavy crypto packages for lighter noble implementations via upgrading `ethereumjs-util` to latest (now called `@ethereumjs/util`) (#260) * upgrade ethereumjs-util to latest (@ethereumjs/util) and adapt accordingly * fix types * wip * fix something * swap out TextEncoder for Buffer.from * fix number hashing * fix more tests * add comments * all tests passing * cutting out ethereumjs-abi * add jsdocs * fully remove old secp256k1 from dependency tree via ethereum-cryptography 0.1.3" * progress? * idk * tests passing * move ethereumjs-abi methods to their own file * switch out toBuffers for typed specific buffer converts * add tests for ethereumjs-abi-util functions * lower test threshhold * lint * fix tests --- jest.config.js | 6 +- package.json | 5 +- src/ethereumjs-abi-utils.test.ts | 295 +++++++++++++++++++++++ src/ethereumjs-abi-utils.ts | 390 +++++++++++++++++++++++++++++++ src/personal-sign.test.ts | 2 +- src/personal-sign.ts | 14 +- src/sign-typed-data.test.ts | 2 +- src/sign-typed-data.ts | 64 +++-- src/utils.test.ts | 2 +- src/utils.ts | 21 +- yarn.lock | 354 +++++----------------------- 11 files changed, 825 insertions(+), 330 deletions(-) create mode 100644 src/ethereumjs-abi-utils.test.ts create mode 100644 src/ethereumjs-abi-utils.ts diff --git a/jest.config.js b/jest.config.js index 5dc759fd..cb30ef4e 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,9 +7,9 @@ module.exports = { coverageThreshold: { global: { branches: 74, - functions: 100, - lines: 94, - statements: 94, + functions: 98, + lines: 93.4, + statements: 93.4, }, }, moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node'], diff --git a/package.json b/package.json index ce6306f6..b9e469c5 100644 --- a/package.json +++ b/package.json @@ -44,8 +44,9 @@ "airtap/engine.io-client/xmlhttprequest-ssl": "^1.6.2" }, "dependencies": { - "ethereumjs-abi": "^0.6.8", - "ethereumjs-util": "^6.2.1", + "@ethereumjs/util": "^8.0.0-beta.1", + "bn.js": "4.11.8", + "ethereum-cryptography": "^1.1.2", "ethjs-util": "^0.1.6", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1" diff --git a/src/ethereumjs-abi-utils.test.ts b/src/ethereumjs-abi-utils.test.ts new file mode 100644 index 00000000..292b1b8f --- /dev/null +++ b/src/ethereumjs-abi-utils.test.ts @@ -0,0 +1,295 @@ +import BN from 'bn.js'; +import { rawEncode, solidityPack, parseNumber } from './ethereumjs-abi-utils'; + +describe('encoding negative int256', function () { + it('should equal', function () { + const a = rawEncode( + ['int256'], + [ + new BN( + '-19999999999999999999999999999999999999999999999999999999999999', + 10, + ), + ], + ).toString('hex'); + const b = + 'fffffffffffff38dd0f10627f5529bdb2c52d4846810af0ac000000000000001'; + expect(a).toStrictEqual(b); + }); +}); + +describe('encoding string >32bytes', function () { + it('should equal', function () { + const a = rawEncode( + ['string'], + [ + ' hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world', + ], + ).toString('hex'); + const b = + '000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c22068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c64202068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c64202068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c64000000000000000000000000000000000000000000000000000000000000'; + expect(a).toStrictEqual(b); + }); +}); + +describe('encoding uint32 response', function () { + it('should equal', function () { + const a = rawEncode(['uint32'], [42]).toString('hex'); + const b = + '000000000000000000000000000000000000000000000000000000000000002a'; + expect(a).toStrictEqual(b); + }); +}); + +describe('encoding string response (unsupported)', function () { + it('should equal', function () { + const a = rawEncode( + ['string'], + ['a response string (unsupported)'], + ).toString('hex'); + const b = + '0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001f6120726573706f6e736520737472696e672028756e737570706f727465642900'; + expect(a).toStrictEqual(b); + }); +}); + +describe('encoding', function () { + it('should work for uint256', function () { + const a = rawEncode(['uint256'], [1]).toString('hex'); + const b = + '0000000000000000000000000000000000000000000000000000000000000001'; + expect(a).toStrictEqual(b); + }); + + it('should work for uint', function () { + const a = rawEncode(['uint'], [1]).toString('hex'); + const b = + '0000000000000000000000000000000000000000000000000000000000000001'; + expect(a).toStrictEqual(b); + }); + + it('should work for int256', function () { + const a = rawEncode(['int256'], [-1]).toString('hex'); + const b = + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(a).toStrictEqual(b); + }); + + it('should work for string and uint256[2]', function () { + const a = rawEncode(['string', 'uint256[2]'], ['foo', [5, 6]]).toString( + 'hex', + ); + const b = + '0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000003666f6f0000000000000000000000000000000000000000000000000000000000'; + expect(a).toStrictEqual(b); + }); +}); + +describe('encoding bytes33', function () { + it('should fail', function () { + expect(() => rawEncode('fail', ['bytes33'])).toThrow( + 'types.forEach is not a function', + ); + }); +}); + +describe('encoding uint0', function () { + it('should fail', function () { + expect(() => rawEncode('fail', ['uint0'])).toThrow( + 'types.forEach is not a function', + ); + }); +}); + +describe('encoding uint257', function () { + it('should fail', function () { + expect(() => rawEncode('fail', ['uint257'])).toThrow( + 'types.forEach is not a function', + ); + }); +}); + +describe('encoding int0', function () { + it('should fail', function () { + expect(() => rawEncode(['int0'], [1])).toThrow('Invalid int width: 0'); + }); +}); + +describe('encoding int257', function () { + it('should fail', function () { + expect(() => rawEncode(['int257'], [1])).toThrow( + 'Invalid int width: 257', + ); + }); +}); + +describe('encoding uint[2] with [1,2,3]', function () { + it('should fail', function () { + expect(() => rawEncode(['uint[2]'], [[1, 2, 3]])).toThrow( + 'Elements exceed array size: 2', + ); + }); +}); + +describe('encoding uint8 with 9bit data', function () { + it('should fail', function () { + expect(() => rawEncode(['uint8'], [new BN(1).iushln(9)])).toThrow( + 'Supplied uint exceeds width: 8 vs 10', + ); + }); +}); + +describe('solidity tight packing bool', function () { + it('should equal', function () { + let a = solidityPack(['bool'], [true]); + let b = '01'; + expect(a.toString('hex')).toStrictEqual(b); + a = solidityPack(['bool'], [false]); + b = '00'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity tight packing address', function () { + it('should equal', function () { + const a = solidityPack( + ['address'], + [new BN('43989fb883ba8111221e89123897538475893837', 16)], + ); + const b = '43989fb883ba8111221e89123897538475893837'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity tight packing string', function () { + it('should equal', function () { + const a = solidityPack(['string'], ['test']); + const b = '74657374'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity tight packing bytes', function () { + it('should equal', function () { + const a = solidityPack(['bytes'], [Buffer.from('123456', 'hex')]); + const b = '123456'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity tight packing bytes8', function () { + it('should equal', function () { + const a = solidityPack(['bytes8'], [Buffer.from('123456', 'hex')]); + const b = '1234560000000000'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity tight packing uint', function () { + it('should equal', function () { + const a = solidityPack(['uint'], [42]); + const b = + '000000000000000000000000000000000000000000000000000000000000002a'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity tight packing uint16', function () { + it('should equal', function () { + const a = solidityPack(['uint16'], [42]); + const b = '002a'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity tight packing int', function () { + it('should equal', function () { + const a = solidityPack(['int'], [-42]); + const b = + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd6'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity tight packing int16', function () { + it('should equal', function () { + const a = solidityPack(['int16'], [-42]); + const b = 'ffd6'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity tight packing multiple arguments', function () { + it('should equal', function () { + const a = solidityPack( + ['bytes32', 'uint32', 'uint32', 'uint32', 'uint32'], + [Buffer.from('123456', 'hex'), 6, 7, 8, 9], + ); + const b = + '123456000000000000000000000000000000000000000000000000000000000000000006000000070000000800000009'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity tight packing uint32[]', function () { + it('should equal', function () { + const a = solidityPack(['uint32[]'], [[8, 9]]); + const b = + '00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity tight packing bool[][]', function () { + it('should equal', function () { + const a = solidityPack( + ['bool[][]'], + [ + [ + [true, false], + [false, true], + ], + ], + ); + const b = + '0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity tight packing address[]', function () { + it('should equal', function () { + const a = solidityPack( + ['address[]'], + [[new BN('43989fb883ba8111221e89123897538475893837', 16)]], + ); + const b = + '00000000000000000000000043989fb883ba8111221e89123897538475893837'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity tight packing uint32[2]', function () { + it('should equal', function () { + const a = solidityPack(['uint32[2]'], [[11, 12]]); + const b = + '000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c'; + expect(a.toString('hex')).toStrictEqual(b); + }); +}); + +describe('solidity packing different amounts of values and types should fail', function () { + it('should throw "Number of types are not matching the values"', function () { + expect(() => { + solidityPack(['uint32'], [11, 12]); + }).toThrow('Number of types are not matching the values'); + }); +}); + +describe('parseNumber should throw an error when passed an object', function () { + it('should throw "Argument is not a number', function () { + expect(() => { + parseNumber({ test: 'test' }); + }).toThrow('Argument is not a number'); + }); +}); diff --git a/src/ethereumjs-abi-utils.ts b/src/ethereumjs-abi-utils.ts new file mode 100644 index 00000000..fc162453 --- /dev/null +++ b/src/ethereumjs-abi-utils.ts @@ -0,0 +1,390 @@ +/* eslint jsdoc/require-description: 0 */ +/* eslint jsdoc/require-returns: 0 */ +/* eslint jsdoc/match-description: 0 */ +/* eslint jsdoc/require-param-description: 0 */ + +import { + toBuffer, + setLengthRight, + setLengthLeft, + isHexPrefixed, + zeros, +} from '@ethereumjs/util'; +import { stripHexPrefix } from 'ethjs-util'; +import BN from 'bn.js'; +import { normalize } from './utils'; + +// +// Methods borrowed and somewhat adapted from ethereumjs-abi@0.6.8: +// https://npmfs.com/package/ethereumjs-abi/0.6.8/lib/index.js +// + +/** + * Packs non-standard encoded values packed according to their respective type in types in a buffer. + * + * @param types - Array of types of each value to encode. + * @param values - Array of values to encode. + * @returns A buffer containing the packed values. + */ +export function solidityPack(types: string[], values: any[]) { + if (types.length !== values.length) { + throw new Error('Number of types are not matching the values'); + } + + const ret = []; + + for (let i = 0; i < types.length; i++) { + const type = elementaryName(types[i]); + const value = values[i]; + ret.push(solidityHexValue(type, value, null)); + } + + return Buffer.concat(ret); +} + +/** + * Checks if a value is an array (represented as a string). + * + * @param type - The value to check whether it is an array. + * @returns A boolean indicating whether the passed value is an array. + */ +function isArray(type) { + return type.lastIndexOf(']') === type.length - 1; +} + +/** + * Parse array type for packing solidity values. + * + * @param type - A string that may be an array to parse. + * @returns A parsed value from the array. + */ +function parseTypeArray(type) { + const tmp = type.match(/(.*)\[(.*?)\]$/u); + if (tmp) { + return tmp[2] === '' ? 'dynamic' : parseInt(tmp[2], 10); + } + return null; +} + +/** + * Parse N from type. + * + * @param type - Value to parse. + * @returns Parsed value. + */ +function parseTypeN(type) { + return parseInt(/^\D+(\d+)$/u.exec(type)[1], 10); +} + +/** + * Parse a number for determining a solidity hexvalue. + * + * @param arg - Number to parse. + * @returns Parsed value. + */ +export function parseNumber(arg) { + const type = typeof arg; + if (type === 'string') { + if (isHexPrefixed(arg)) { + return new BN(stripHexPrefix(arg), 16); + } + return new BN(arg, 10); + } else if (type === 'number') { + return new BN(arg); + } else if (arg.toArray) { + // assume this is a BN for the moment, replace with BN.isBN soon + return arg; + } + throw new Error('Argument is not a number'); +} + +/** + * Get solidity hex value from type, value and bitsize inputs for packing these values in a buffer. + * + * @param type - The type of the value to encode. + * @param value - The value to encode. + * @param bitsize - The bitsize of the value to encode. + * @returns The encoded soldity hex value. + */ +function solidityHexValue(type, value, bitsize) { + // pass in bitsize = null if use default bitsize + let size, num; + if (isArray(type)) { + const subType = type.replace(/\[.*?\]/u, ''); + if (!isArray(subType)) { + const arraySize = parseTypeArray(type); + if ( + arraySize !== 'dynamic' && + arraySize !== 0 && + value.length > arraySize + ) { + throw new Error(`Elements exceed array size: ${arraySize}`); + } + } + const arrayValues = value.map(function (v) { + return solidityHexValue(subType, v, 256); + }); + return Buffer.concat(arrayValues); + } else if (type === 'bytes') { + return value; + } else if (type === 'string') { + return Buffer.from(value, 'utf8'); + } else if (type === 'bool') { + bitsize = bitsize || 8; + const padding = Array(bitsize / 4).join('0'); + return Buffer.from(value ? `${padding}1` : `${padding}0`, 'hex'); + } else if (type === 'address') { + let bytesize = 20; + if (bitsize) { + bytesize = bitsize / 8; + } + return setLengthLeft(toBuffer(value), bytesize); + } else if (type.startsWith('bytes')) { + size = parseTypeN(type); + if (size < 1 || size > 32) { + throw new Error(`Invalid bytes width: ${size}`); + } + + if (typeof value === 'number') { + value = normalize(value); + } + return setLengthRight(toBuffer(value), size); + } else if (type.startsWith('uint')) { + size = parseTypeN(type); + if (size % 8 || size < 8 || size > 256) { + throw new Error(`Invalid uint width: ${size}`); + } + + num = parseNumber(value); + if (num.bitLength() > size) { + throw new Error( + `Supplied uint exceeds width: ${size} vs ${num.bitLength()}`, + ); + } + + bitsize = bitsize || size; + return num.toArrayLike(Buffer, 'be', bitsize / 8); + } else if (type.startsWith('int')) { + size = parseTypeN(type); + if (size % 8 || size < 8 || size > 256) { + throw new Error(`Invalid int width: ${size}`); + } + + num = parseNumber(value); + if (num.bitLength() > size) { + throw new Error( + `Supplied int exceeds width: ${size} vs ${num.bitLength()}`, + ); + } + + bitsize = bitsize || size; + return num.toTwos(size).toArrayLike(Buffer, 'be', bitsize / 8); + } + // FIXME: support all other types + throw new Error(`Unsupported or invalid type: ${type}`); +} + +/** + * Gets the correct solidity type name. + * + * @param name - The type name for which we want the corresponding solidity type name. + * @returns The solidity type name for the input value. + */ +function elementaryName(name) { + if (name.startsWith('int[')) { + return `int256${name.slice(3)}`; + } else if (name === 'int') { + return 'int256'; + } else if (name.startsWith('uint[')) { + return `uint256${name.slice(4)}`; + } else if (name === 'uint') { + return 'uint256'; + } else if (name.startsWith('fixed[')) { + return `fixed128x128${name.slice(5)}`; + } else if (name === 'fixed') { + return 'fixed128x128'; + } else if (name.startsWith('ufixed[')) { + return `ufixed128x128${name.slice(6)}`; + } else if (name === 'ufixed') { + return 'ufixed128x128'; + } + return name; +} + +/** + * @param types + * @param values + */ +export function rawEncode(types, values) { + const output = []; + const data = []; + + let headLength = 0; + + types.forEach(function (type) { + if (isArray(type)) { + const size: number | 'dynamic' = parseTypeArray(type); + // eslint-disable-next-line no-negated-condition + if (size !== 'dynamic') { + headLength += 32 * size; + } else { + headLength += 32; + } + } else { + headLength += 32; + } + }); + + for (let i = 0; i < types.length; i++) { + const type = elementaryName(types[i]); + const value = values[i]; + const cur = encodeSingle(type, value); + + // Use the head/tail method for storing dynamic data + if (isDynamic(type)) { + output.push(encodeSingle('uint256', headLength)); + data.push(cur); + headLength += cur.length; + } else { + output.push(cur); + } + } + + return Buffer.concat(output.concat(data)); +} + +// Encodes a single item (can be dynamic array) +// @returns: Buffer +/** + * @param type + * @param arg + */ +function encodeSingle(type, arg) { + let size, num, ret, i; + + if (type === 'address') { + return encodeSingle('uint160', parseNumber(arg)); + } else if (type === 'bool') { + return encodeSingle('uint8', arg ? 1 : 0); + } else if (type === 'string') { + return encodeSingle('bytes', Buffer.from(arg, 'utf8')); + } else if (isArray(type)) { + // this part handles fixed-length ([2]) and variable length ([]) arrays + // NOTE: we catch here all calls to arrays, that simplifies the rest + if (typeof arg.length === 'undefined') { + throw new Error('Not an array?'); + } + size = parseTypeArray(type); + if (size !== 'dynamic' && size !== 0 && arg.length > size) { + throw new Error(`Elements exceed array size: ${size}`); + } + ret = []; + type = type.slice(0, type.lastIndexOf('[')); + if (typeof arg === 'string') { + arg = JSON.parse(arg); + } + + for (i in arg) { + if (Object.prototype.hasOwnProperty.call(arg, i)) { + ret.push(encodeSingle(type, arg[i])); + } + } + + if (size === 'dynamic') { + const length = encodeSingle('uint256', arg.length); + ret.unshift(length); + } + return Buffer.concat(ret); + } else if (type === 'bytes') { + arg = Buffer.from(arg); + + ret = Buffer.concat([encodeSingle('uint256', arg.length), arg]); + + if (arg.length % 32 !== 0) { + ret = Buffer.concat([ret, zeros(32 - (arg.length % 32))]); + } + + return ret; + } else if (type.startsWith('bytes')) { + size = parseTypeN(type); + if (size < 1 || size > 32) { + throw new Error(`Invalid bytes width: ${size}`); + } + + if (typeof arg === 'number') { + arg = normalize(arg); + } + return setLengthRight(toBuffer(arg), 32); + } else if (type.startsWith('uint')) { + size = parseTypeN(type); + if (size % 8 || size < 8 || size > 256) { + throw new Error(`Invalid uint width: ${size}`); + } + + num = parseNumber(arg); + if (num.bitLength() > size) { + throw new Error( + `Supplied uint exceeds width: ${size} vs ${num.bitLength()}`, + ); + } + + if (num < 0) { + throw new Error('Supplied uint is negative'); + } + + return num.toArrayLike(Buffer, 'be', 32); + } else if (type.startsWith('int')) { + size = parseTypeN(type); + if (size % 8 || size < 8 || size > 256) { + throw new Error(`Invalid int width: ${size}`); + } + + num = parseNumber(arg); + if (num.bitLength() > size) { + throw new Error( + `Supplied int exceeds width: ${size} vs ${num.bitLength()}`, + ); + } + + return num.toTwos(256).toArrayLike(Buffer, 'be', 32); + } else if (type.startsWith('ufixed')) { + size = parseTypeNxM(type); + + num = parseNumber(arg); + + if (num < 0) { + throw new Error('Supplied ufixed is negative'); + } + + return encodeSingle('uint256', num.mul(new BN(2).pow(new BN(size[1])))); + } else if (type.startsWith('fixed')) { + size = parseTypeNxM(type); + + return encodeSingle( + 'int256', + parseNumber(arg).mul(new BN(2).pow(new BN(size[1]))), + ); + } + + throw new Error(`Unsupported or invalid type: ${type}`); +} + +// Is a type dynamic? +/** + * @param type + */ +function isDynamic(type) { + // FIXME: handle all types? I don't think anything is missing now + return ( + type === 'string' || type === 'bytes' || parseTypeArray(type) === 'dynamic' + ); +} + +// Parse N,M from typex +/** + * @param type + */ +function parseTypeNxM(type) { + const tmp = /^\D+(\d+)x(\d+)$/u.exec(type); + return [parseInt(tmp[1], 10), parseInt(tmp[2], 10)]; +} diff --git a/src/personal-sign.test.ts b/src/personal-sign.test.ts index a70c47d5..d5060a03 100644 --- a/src/personal-sign.test.ts +++ b/src/personal-sign.test.ts @@ -2,7 +2,7 @@ import { addHexPrefix, privateToAddress, privateToPublic, -} from 'ethereumjs-util'; +} from '@ethereumjs/util'; import { extractPublicKey, diff --git a/src/personal-sign.ts b/src/personal-sign.ts index 4c4301a2..095a912e 100644 --- a/src/personal-sign.ts +++ b/src/personal-sign.ts @@ -4,7 +4,8 @@ import { hashPersonalMessage, publicToAddress, toBuffer, -} from 'ethereumjs-util'; + ToBufferInputTypes, +} from '@ethereumjs/util'; import { concatSig, @@ -29,7 +30,7 @@ export function personalSign({ data, }: { privateKey: Buffer; - data: unknown; + data: ToBufferInputTypes; }): string { if (isNullish(data)) { throw new Error('Missing data parameter'); @@ -57,7 +58,7 @@ export function recoverPersonalSignature({ data, signature, }: { - data: unknown; + data: ToBufferInputTypes; signature: string; }): string { if (isNullish(data)) { @@ -85,7 +86,7 @@ export function extractPublicKey({ data, signature, }: { - data: unknown; + data: ToBufferInputTypes; signature: string; }): string { if (isNullish(data)) { @@ -105,7 +106,10 @@ export function extractPublicKey({ * @param signature - The '0x'-prefixed hex encoded message signature. * @returns The public key of the signer. */ -function getPublicKeyFor(message: unknown, signature: string): Buffer { +function getPublicKeyFor( + message: ToBufferInputTypes, + signature: string, +): Buffer { const messageHash = hashPersonalMessage(legacyToBuffer(message)); return recoverPublicKey(messageHash, signature); } diff --git a/src/sign-typed-data.test.ts b/src/sign-typed-data.test.ts index a988eca7..f9105fa2 100644 --- a/src/sign-typed-data.test.ts +++ b/src/sign-typed-data.test.ts @@ -10,7 +10,7 @@ eslint jest/no-restricted-matchers: [ ] */ -import * as ethUtil from 'ethereumjs-util'; +import * as ethUtil from '@ethereumjs/util'; import Ajv from 'ajv'; import { recoverTypedSignature, diff --git a/src/sign-typed-data.ts b/src/sign-typed-data.ts index 61ca1862..637ce8e9 100644 --- a/src/sign-typed-data.ts +++ b/src/sign-typed-data.ts @@ -1,17 +1,20 @@ +import { isHexString } from 'ethjs-util'; import { + arrToBufArr, bufferToHex, ecsign, - keccak, publicToAddress, toBuffer, -} from 'ethereumjs-util'; -import { rawEncode, soliditySHA3 } from 'ethereumjs-abi'; +} from '@ethereumjs/util'; +import { keccak256 } from 'ethereum-cryptography/keccak'; +import { rawEncode, solidityPack } from './ethereumjs-abi-utils'; import { concatSig, isNullish, legacyToBuffer, recoverPublicKey, + numberToBuffer, } from './utils'; /** @@ -159,7 +162,7 @@ function encodeField( 'bytes32', version === SignTypedDataVersion.V4 && value == null // eslint-disable-line no-eq-null ? '0x0000000000000000000000000000000000000000000000000000000000000000' - : keccak(encodeData(type, value, types, version)), + : arrToBufArr(keccak256(encodeData(type, value, types, version))), ]; } @@ -168,15 +171,23 @@ function encodeField( } if (type === 'bytes') { - return ['bytes32', keccak(value)]; + if (typeof value === 'number') { + value = numberToBuffer(value); + } else if (isHexString(value)) { + value = numberToBuffer(parseInt(value, 16)); + } else { + value = Buffer.from(value, 'utf8'); + } + return ['bytes32', arrToBufArr(keccak256(value))]; } if (type === 'string') { - // convert string to buffer - prevents ethUtil from interpreting strings like '0xabcd' as hex - if (typeof value === 'string') { - value = Buffer.from(value, 'utf8'); + if (typeof value === 'number') { + value = numberToBuffer(value); + } else { + value = Buffer.from(value ?? '', 'utf8'); } - return ['bytes32', keccak(value)]; + return ['bytes32', arrToBufArr(keccak256(value))]; } if (type.lastIndexOf(']') === type.length - 1) { @@ -191,10 +202,12 @@ function encodeField( ); return [ 'bytes32', - keccak( - rawEncode( - typeValuePairs.map(([t]) => t), - typeValuePairs.map(([, v]) => v), + arrToBufArr( + keccak256( + rawEncode( + typeValuePairs.map(([t]) => t), + typeValuePairs.map(([, v]) => v), + ), ), ), ]; @@ -314,7 +327,7 @@ function hashStruct( ): Buffer { validateVersion(version, [SignTypedDataVersion.V3, SignTypedDataVersion.V4]); - return keccak(encodeData(primaryType, data, types, version)); + return arrToBufArr(keccak256(encodeData(primaryType, data, types, version))); } /** @@ -328,7 +341,8 @@ function hashType( primaryType: string, types: Record, ): Buffer { - return keccak(encodeType(primaryType, types)); + const encodedHashType = Buffer.from(encodeType(primaryType, types), 'utf-8'); + return arrToBufArr(keccak256(encodedHashType)); } /** @@ -393,7 +407,7 @@ function eip712Hash( ), ); } - return keccak(Buffer.concat(parts)); + return arrToBufArr(keccak256(Buffer.concat(parts))); } /** @@ -459,12 +473,18 @@ function _typedSignatureHash(typedData: TypedDataV1): Buffer { return `${e.type} ${e.name}`; }); - return soliditySHA3( - ['bytes32', 'bytes32'], - [ - soliditySHA3(new Array(typedData.length).fill('string'), schema), - soliditySHA3(types, data), - ], + return arrToBufArr( + keccak256( + solidityPack( + ['bytes32', 'bytes32'], + [ + keccak256( + solidityPack(new Array(typedData.length).fill('string'), schema), + ), + keccak256(solidityPack(types, data)), + ], + ), + ), ); } diff --git a/src/utils.test.ts b/src/utils.test.ts index b45b2126..b9a09600 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -94,7 +94,7 @@ describe('concatSig', function () { Buffer.from(largeNumber, 'hex'), Buffer.from(largeNumber, 'hex'), ), - ).toThrow('Number can only safely store up to 53 bits'); + ).toThrow('Number exceeds 53 bits'); }); }); diff --git a/src/utils.ts b/src/utils.ts index ea83eabf..263541a9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -6,8 +6,9 @@ import { fromRpcSig, fromSigned, toBuffer, + ToBufferInputTypes, toUnsigned, -} from 'ethereumjs-util'; +} from '@ethereumjs/util'; import { intToHex, isHexString, stripHexPrefix } from 'ethjs-util'; /** @@ -56,7 +57,7 @@ export function isNullish(value) { * @param value - The value to convert to a Buffer. * @returns The given value as a Buffer. */ -export function legacyToBuffer(value: unknown) { +export function legacyToBuffer(value: ToBufferInputTypes) { return typeof value === 'string' && !isHexString(value) ? Buffer.from(value) : toBuffer(value); @@ -107,6 +108,9 @@ export function normalize(input: number | string): string { } if (typeof input === 'number') { + if (input < 0) { + return '0x'; + } const buffer = toBuffer(input); input = bufferToHex(buffer); } @@ -119,3 +123,16 @@ export function normalize(input: number | string): string { return addHexPrefix(input.toLowerCase()); } + +/** + * Node's Buffer.from() method does not seem to buffer numbers correctly out of the box. + * This helper method formats the number correct for Buffer.from to return correct buffer. + * + * @param num - The number to convert to buffer. + * @returns The number in buffer form. + */ +export function numberToBuffer(num: number) { + const hexVal = num.toString(16); + const prepend = hexVal.length % 2 ? '0' : ''; + return Buffer.from(prepend + hexVal, 'hex'); +} diff --git a/yarn.lock b/yarn.lock index 837fb531..018eda36 100644 --- a/yarn.lock +++ b/yarn.lock @@ -333,6 +333,14 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@ethereumjs/util@^8.0.0-beta.1": + version "8.0.0-beta.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.0.0-beta.1.tgz#369526faf6e9f1cadfd39c7741cc07cf33d128f8" + integrity sha512-yUg3TdJm25HiamAXbNuOagXQPmgdSrV3oEH0h+Adsxt6D7qHw8HyHLA8C+tNrLP2YwcjF1dGJ+F7WtOibzEp9g== + dependencies: + ethereum-cryptography "^1.0.3" + rlp "4.0.0-beta.1" + "@humanwhocodes/config-array@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" @@ -591,6 +599,16 @@ resolved "https://registry.yarnpkg.com/@metamask/eslint-config/-/eslint-config-9.0.0.tgz#22d4911b705f7e4e566efbdda0e37912da33e30f" integrity sha512-mWlLGQKjXXFOj9EtDClKSoTLeQuPW2kM1w3EpUMf4goYAQ+kLXCCa8pEff6h8ApWAnjhYmXydA1znQ2J4XvD+A== +"@noble/hashes@1.1.2", "@noble/hashes@~1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" + integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== + +"@noble/secp256k1@1.6.3", "@noble/secp256k1@~1.6.0": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94" + integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ== + "@nodelib/fs.scandir@2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" @@ -635,6 +653,28 @@ node-gyp "^7.1.0" read-package-json-fast "^2.0.1" +"@scure/base@~1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" + integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== + +"@scure/bip32@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.0.tgz#dea45875e7fbc720c2b4560325f1cf5d2246d95b" + integrity sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q== + dependencies: + "@noble/hashes" "~1.1.1" + "@noble/secp256k1" "~1.6.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.0.tgz#92f11d095bae025f166bef3defcc5bf4945d419a" + integrity sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w== + dependencies: + "@noble/hashes" "~1.1.1" + "@scure/base" "~1.1.0" + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -687,13 +727,6 @@ dependencies: "@babel/types" "^7.3.0" -"@types/bn.js@^4.11.3": - version "4.11.6" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" - integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== - dependencies: - "@types/node" "*" - "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -766,25 +799,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.25.tgz#15967a7b577ff81383f9b888aa6705d43fbbae93" integrity sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ== -"@types/pbkdf2@^3.0.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" - integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== - dependencies: - "@types/node" "*" - "@types/prettier@^2.1.5": version "2.3.2" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3" integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog== -"@types/secp256k1@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.1.tgz#fb3aa61a1848ad97d7425ff9dcba784549fca5a4" - integrity sha512-+ZjSA8ELlOp8SlKi0YLB2tz9d5iPNEmOBd+8Rz21wTMdaXQIa9b6TEnD6l5qKOCypE7FSyPyck12qZJxSDNoog== - dependencies: - "@types/node" "*" - "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" @@ -1209,13 +1228,6 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -base-x@^3.0.2: - version "3.0.8" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" - integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA== - dependencies: - safe-buffer "^5.0.1" - bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -1223,15 +1235,10 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -blakejs@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" - integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U= - -bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.8, bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== +bn.js@4.11.8: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== brace-expansion@^1.1.7: version "1.1.11" @@ -1255,28 +1262,11 @@ braces@^3.0.1: dependencies: fill-range "^7.0.1" -brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserify-aes@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - browserslist@^4.16.6: version "4.16.6" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" @@ -1295,22 +1285,6 @@ bs-logger@0.x: dependencies: fast-json-stable-stringify "2.x" -bs58@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" - integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= - dependencies: - base-x "^3.0.2" - -bs58check@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" - integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== - dependencies: - bs58 "^4.0.0" - create-hash "^1.1.0" - safe-buffer "^5.1.2" - bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -1323,11 +1297,6 @@ buffer-from@1.x, buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" - integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= - call-bind@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" @@ -1401,14 +1370,6 @@ ci-info@^3.1.1: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - cjs-module-lexer@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" @@ -1501,29 +1462,6 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.4, create-hmac@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -1704,19 +1642,6 @@ electron-to-chromium@^1.3.723: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.777.tgz#f27b9629dc9ec5a5065847b7fd4b9409b3c351bc" integrity sha512-gO0VEU2esNUOu51DTtp7E7xy0d+yZdbC06EACvou1cbPM9Wwyz8ixPa6T1tqDXlT4/AefXtzx0i9OLwKkLOHQA== -elliptic@^6.5.2: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - emittery@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" @@ -2042,49 +1967,17 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -ethereum-cryptography@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" - integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== - dependencies: - "@types/pbkdf2" "^3.0.0" - "@types/secp256k1" "^4.0.1" - blakejs "^1.1.0" - browserify-aes "^1.2.0" - bs58check "^2.1.2" - create-hash "^1.2.0" - create-hmac "^1.1.7" - hash.js "^1.1.7" - keccak "^3.0.0" - pbkdf2 "^3.0.17" - randombytes "^2.1.0" - safe-buffer "^5.1.2" - scrypt-js "^3.0.0" - secp256k1 "^4.0.1" - setimmediate "^1.0.5" - -ethereumjs-abi@^0.6.8: - version "0.6.8" - resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" - integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== - dependencies: - bn.js "^4.11.8" - ethereumjs-util "^6.0.0" - -ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" - integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== - dependencies: - "@types/bn.js" "^4.11.3" - bn.js "^4.11.0" - create-hash "^1.1.2" - elliptic "^6.5.2" - ethereum-cryptography "^0.1.3" - ethjs-util "0.1.6" - rlp "^2.2.3" - -ethjs-util@0.1.6, ethjs-util@^0.1.6: +ethereum-cryptography@^1.0.3, ethereum-cryptography@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz#74f2ac0f0f5fe79f012c889b3b8446a9a6264e6d" + integrity sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ== + dependencies: + "@noble/hashes" "1.1.2" + "@noble/secp256k1" "1.6.3" + "@scure/bip32" "1.1.0" + "@scure/bip39" "1.1.0" + +ethjs-util@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== @@ -2092,14 +1985,6 @@ ethjs-util@0.1.6, ethjs-util@^0.1.6: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" -evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - execa@^5.0.0, execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -2501,31 +2386,6 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -hash-base@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" - integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -2633,7 +2493,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -3414,14 +3274,6 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -keccak@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff" - integrity sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA== - dependencies: - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -3529,15 +3381,6 @@ marked@^4.0.12: resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.15.tgz#0216b7c9d5fcf6ac5042343c41d81a8b1b5e1b4a" integrity sha512-esX5lPdTfG4p8LDkv+obbRCyOKzB+820ZZyMOXJZygZBHrH9b3xXR64X4kT3sPe9Nx8qQXbmcz6kFSMt4Nfk6Q== -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -3581,16 +3424,6 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -3645,16 +3478,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -node-addon-api@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" - integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== - -node-gyp-build@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" - integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== - node-gyp@^7.1.0: version "7.1.2" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" @@ -3927,17 +3750,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pbkdf2@^3.0.17: - version "3.1.1" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" - integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -4068,13 +3880,6 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" @@ -4216,27 +4021,17 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -rlp@^2.2.3: - version "2.2.4" - resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.4.tgz#d6b0e1659e9285fc509a5d169a9bd06f704951c1" - integrity sha512-fdq2yYCWpAQBhwkZv+Z8o/Z4sPmYm1CUq6P7n6lVTOdb949CnqA0sndXal5C1NleSVSZm6q5F3iEbauyVln/iw== - dependencies: - bn.js "^4.11.1" +rlp@4.0.0-beta.1: + version "4.0.0-beta.1" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-4.0.0-beta.1.tgz#46983ee758344e5eee48f135129407434cfea2b6" + integrity sha512-UVIENF7Rw+nX5cpfzw6X3/oXNQKsSZ8HbDJUeU9RoIs1LLyMjcPZR1o26i1vFbpuVN8GRmcdopEYOMjVsLRsQQ== run-parallel@^1.1.9: version "1.1.10" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef" integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw== -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2: +safe-buffer@^5.0.1, safe-buffer@^5.1.2: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== @@ -4258,20 +4053,6 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -scrypt-js@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" - integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== - -secp256k1@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" - integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== - dependencies: - elliptic "^6.5.2" - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - "semver@2 || 3 || 4 || 5": version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -4301,19 +4082,6 @@ set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"