From 2a40b660c449b044fa7310887af418139b487367 Mon Sep 17 00:00:00 2001 From: Muhammad Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Mon, 8 Jan 2024 10:09:45 +0100 Subject: [PATCH] Fix an issue with detecting Uint8Array (#6486) * fix issue with detecting Uint8Array (fix: `value "..." at "/0" must pass "bytes" validation`) * remove a comment that was left be mistake * add tests for Uint8Array in jsdom * update CHANGELOG.md files --- CHANGELOG.md | 4 +- packages/web3-eth-abi/CHANGELOG.md | 4 + packages/web3-eth-abi/src/utils.ts | 4 +- packages/web3-eth-accounts/CHANGELOG.md | 1 + packages/web3-eth-accounts/src/account.ts | 13 +- .../web3-eth-accounts/src/common/utils.ts | 8 +- .../src/tx/transactionFactory.ts | 6 +- .../test/unit/account_dom.test.ts | 248 ++++++++++ packages/web3-utils/CHANGELOG.md | 6 +- packages/web3-utils/src/converters.ts | 9 +- packages/web3-utils/src/formatter.ts | 4 +- packages/web3-utils/src/hash.ts | 7 +- packages/web3-utils/src/uint8array.ts | 8 + .../test/integration/event_emitter.test.ts | 5 - .../test/unit/converters_dom.test.ts | 439 ++++++++++++++++++ .../web3-utils/test/unit/hash_dom.test.ts | 166 +++++++ packages/web3-validator/CHANGELOG.md | 6 +- packages/web3-validator/src/utils.ts | 11 + .../web3-validator/src/validation/address.ts | 9 +- .../web3-validator/src/validation/bytes.ts | 5 +- yarn.lock | 37 +- 21 files changed, 958 insertions(+), 42 deletions(-) create mode 100644 packages/web3-eth-accounts/test/unit/account_dom.test.ts create mode 100644 packages/web3-utils/test/unit/converters_dom.test.ts create mode 100644 packages/web3-utils/test/unit/hash_dom.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 3455d22a904..1b059877dff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2287,7 +2287,7 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-utils -- Fix unecessary array copy when pack encoding (#6553) +- Fix unnecessary array copy when pack encoding (#6553) ## [Unreleased] @@ -2322,4 +2322,4 @@ If there are any bugs, improvements, optimizations or any new feature proposal f #### web3-utils -- Fix unecessary array copy when pack encoding (#6553) +- Fix unnecessary array copy when pack encoding (#6553) diff --git a/packages/web3-eth-abi/CHANGELOG.md b/packages/web3-eth-abi/CHANGELOG.md index 2192a71e872..486ca6ab34b 100644 --- a/packages/web3-eth-abi/CHANGELOG.md +++ b/packages/web3-eth-abi/CHANGELOG.md @@ -159,3 +159,7 @@ Documentation: ### Changed - Use `AbiError` instead of `Error` for errors at web3-eth-abi (#6641). + +### Fixed + +- Fixed an issue with detecting Uint8Array (#6486) diff --git a/packages/web3-eth-abi/src/utils.ts b/packages/web3-eth-abi/src/utils.ts index e8212c28329..081e4e544ce 100644 --- a/packages/web3-eth-abi/src/utils.ts +++ b/packages/web3-eth-abi/src/utils.ts @@ -16,7 +16,7 @@ along with web3.js. If not, see . */ import { AbiError } from 'web3-errors'; -import { isNullish, leftPad, rightPad, toHex } from 'web3-utils'; +import { isNullish, isUint8Array, leftPad, rightPad, toHex } from 'web3-utils'; import { AbiInput, AbiCoderStruct, @@ -189,7 +189,7 @@ export const formatParam = (type: string, _param: unknown): unknown => { // Format correct length for bytes[0-9]+ match = paramTypeBytes.exec(type); if (match) { - const hexParam = param instanceof Uint8Array ? toHex(param) : param; + const hexParam = isUint8Array(param) ? toHex(param) : param; // format to correct length const size = parseInt(match[1], 10); diff --git a/packages/web3-eth-accounts/CHANGELOG.md b/packages/web3-eth-accounts/CHANGELOG.md index 337dd2292b0..11bddfffab1 100644 --- a/packages/web3-eth-accounts/CHANGELOG.md +++ b/packages/web3-eth-accounts/CHANGELOG.md @@ -154,3 +154,4 @@ Documentation: ### Fixed - Send Transaction config used to be ignored if the passed `common` did not have a `copy()` and the `chainId` was not provided (#6663) +- Fixed an issue with detecting Uint8Array (#6486) diff --git a/packages/web3-eth-accounts/src/account.ts b/packages/web3-eth-accounts/src/account.ts index 8b8dcce7fe5..8aeb7f659fa 100644 --- a/packages/web3-eth-accounts/src/account.ts +++ b/packages/web3-eth-accounts/src/account.ts @@ -76,6 +76,7 @@ import { bytesToHex, fromUtf8, hexToBytes, + isUint8Array, numberToHex, randomBytes, sha3Raw, @@ -126,7 +127,7 @@ export const parseAndValidatePrivateKey = (data: Bytes, ignoreLength?: boolean): } try { - privateKeyUint8Array = data instanceof Uint8Array ? data : bytesToUint8Array(data); + privateKeyUint8Array = isUint8Array(data) ? (data ) : bytesToUint8Array(data); } catch { throw new InvalidPrivateKeyError(); } @@ -406,7 +407,7 @@ export const recover = ( const V_INDEX = 130; // r = first 32 bytes, s = second 32 bytes, v = last byte of signature const hashedMessage = prefixedOrR ? data : hashMessage(data); - let v = parseInt(signatureOrV.substring(V_INDEX),16); // 0x + r + s + v + let v = parseInt(signatureOrV.substring(V_INDEX), 16); // 0x + r + s + v if (v > 26) { v -= 27; } @@ -421,7 +422,7 @@ export const recover = ( const address = toChecksumAddress(`0x${publicHash.slice(-40)}`); return address; -}; +};; /** * Get the ethereum Address from a private key @@ -456,7 +457,7 @@ export const privateKeyToAddress = (privateKey: Bytes): string => { * Get the public key from a private key * * @param privateKey - String or Uint8Array of 32 bytes - * @param isCompressed - if true, will generate a 33 byte compressed public key instead of a 65 byte public key + * @param isCompressed - if true, will generate a 33 byte compressed public key instead of a 65 byte public key * @returns The public key * @example * ```ts @@ -465,7 +466,7 @@ export const privateKeyToAddress = (privateKey: Bytes): string => { * > "0x42beb65f179720abaa3ec9a70a539629cbbc5ec65bb57e7fc78977796837e537662dd17042e6449dc843c281067a4d6d8d1a1775a13c41901670d5de7ee6503a" // uncompressed public key * ``` */ - export const privateKeyToPublicKey = (privateKey: Bytes, isCompressed: boolean): string => { +export const privateKeyToPublicKey = (privateKey: Bytes, isCompressed: boolean): string => { const privateKeyUint8Array = parseAndValidatePrivateKey(privateKey); // Get public key from private key in compressed format @@ -562,7 +563,7 @@ export const encrypt = async ( salt = randomBytes(32); } - if (!(isString(password) || password instanceof Uint8Array)) { + if (!(isString(password) || isUint8Array(password))) { throw new InvalidPasswordError(); } diff --git a/packages/web3-eth-accounts/src/common/utils.ts b/packages/web3-eth-accounts/src/common/utils.ts index f243ede269d..a1d46c36140 100644 --- a/packages/web3-eth-accounts/src/common/utils.ts +++ b/packages/web3-eth-accounts/src/common/utils.ts @@ -15,7 +15,7 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ import { isHexPrefixed, isHexString } from 'web3-validator'; -import { bytesToHex, hexToBytes, numberToHex } from 'web3-utils'; +import { bytesToHex, hexToBytes, isUint8Array, numberToHex } from 'web3-utils'; import { secp256k1 } from '../tx/constants.js'; import { Hardfork } from './enums.js'; import { ToBytesInputTypes, TypeOutput, TypeOutputReturnType } from './types.js'; @@ -331,6 +331,10 @@ export const toUint8Array = function (v: ToBytesInputTypes): Uint8Array { return v; } + if (v?.constructor?.name === 'Uint8Array') { + return Uint8Array.from(v as unknown as Uint8Array); + } + if (Array.isArray(v)) { return Uint8Array.from(v); } @@ -420,7 +424,7 @@ const setLength = function (msg: Uint8Array, length: number, right: boolean) { * @param {Uint8Array} input value to check */ export function assertIsUint8Array(input: unknown): asserts input is Uint8Array { - if (!(input instanceof Uint8Array)) { + if (!isUint8Array(input)) { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions const msg = `This method only supports Uint8Array but input was: ${input}`; throw new Error(msg); diff --git a/packages/web3-eth-accounts/src/tx/transactionFactory.ts b/packages/web3-eth-accounts/src/tx/transactionFactory.ts index e4da9fcfe6e..a677543c64f 100644 --- a/packages/web3-eth-accounts/src/tx/transactionFactory.ts +++ b/packages/web3-eth-accounts/src/tx/transactionFactory.ts @@ -14,7 +14,9 @@ GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ + import { Numbers } from 'web3-types'; +import { isUint8Array } from 'web3-utils'; import { toUint8Array, uint8ArrayToBigInt } from '../common/utils.js'; import { FeeMarketEIP1559Transaction } from './eip1559Transaction.js'; import { AccessListEIP2930Transaction } from './eip2930Transaction.js'; @@ -134,8 +136,8 @@ export class TransactionFactory { * @param txOptions - The transaction options */ public static fromBlockBodyData(data: Uint8Array | Uint8Array[], txOptions: TxOptions = {}) { - if (data instanceof Uint8Array) { - return this.fromSerializedData(data, txOptions); + if (isUint8Array(data)) { + return this.fromSerializedData(data , txOptions); } if (Array.isArray(data)) { // It is a legacy transaction diff --git a/packages/web3-eth-accounts/test/unit/account_dom.test.ts b/packages/web3-eth-accounts/test/unit/account_dom.test.ts new file mode 100644 index 00000000000..587996a721c --- /dev/null +++ b/packages/web3-eth-accounts/test/unit/account_dom.test.ts @@ -0,0 +1,248 @@ +/** + * @jest-environment jsdom + */ + +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +// this file contains the unit test for the event emitter in the DOM environment +// it is executed in the jsdom environment (see "@jest-environment jsdom" in the top comment of this file) + +// ignore the following rule to allow keeping `@jest-environment jsdom` on top: +// eslint-disable-next-line header/header +import { TextEncoder } from 'util'; +import crypto from 'crypto'; +// polyfill for jsdom +global.TextEncoder = TextEncoder; +// @ts-expect-error "Cannot assign to 'subtle' because it is a read-only property." +global.crypto.subtle = crypto.webcrypto.subtle; + +/* eslint-disable import/first */ +import { Address } from 'web3-types'; +import { Web3ValidatorError, isHexStrict } from 'web3-validator'; +import { + create, + decrypt, + encrypt, + hashMessage, + privateKeyToAccount, + privateKeyToAddress, + recover, + recoverTransaction, + sign, + signTransaction, + privateKeyToPublicKey, +} from '../../src/account'; +import { + invalidDecryptData, + invalidEncryptData, + invalidKeyStore, + invalidPrivateKeytoAccountData, + invalidPrivateKeyToAddressData, + signatureRecoverData, + transactionsTestData, + validDecryptData, + validEncryptData, + validHashMessageData, + validPrivateKeytoAccountData, + validPrivateKeyToAddressData, + validPrivateKeyToPublicKeyData, + validRecover, +} from '../fixtures/account'; +import { TransactionFactory } from '../../src/tx/transactionFactory'; +import { TxData } from '../../src/tx/types'; + +describe('accounts', () => { + describe('create', () => { + describe('valid cases', () => { + it('%s', () => { + const account = create(); + expect(typeof account.privateKey).toBe('string'); + expect(typeof account.address).toBe('string'); + expect(isHexStrict(account.address)).toBe(true); + expect(typeof account.encrypt).toBe('function'); + expect(typeof account.sign).toBe('function'); + expect(typeof account.signTransaction).toBe('function'); + }); + }); + }); + + describe('privateKeyToAddress', () => { + describe('valid cases', () => { + it.each(validPrivateKeyToAddressData)('%s', (input, output) => { + expect(privateKeyToAddress(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(invalidPrivateKeyToAddressData)('%s', (input, output) => { + expect(() => privateKeyToAddress(input)).toThrow(output); + }); + }); + }); + + describe('privateKeyToAccount', () => { + describe('valid cases', () => { + it.each(validPrivateKeytoAccountData)('%s', (input, output) => { + expect(JSON.stringify(privateKeyToAccount(input.address, input.ignoreLength))).toEqual( + JSON.stringify(output), + ); + }); + }); + + describe('invalid cases', () => { + it.each(invalidPrivateKeytoAccountData)('%s', (input, output) => { + expect(() => privateKeyToAccount(input)).toThrow(output); + }); + }); + }); + describe('privateKeyToPublicKey', () => { + describe('valid cases', () => { + it.each(validPrivateKeyToPublicKeyData)('%s', (privateKey, isCompressed, output) => { + expect(privateKeyToPublicKey(privateKey, isCompressed)).toEqual(output); + }); + }); + }); + + describe('Signing and Recovery of Transaction', () => { + it.each(transactionsTestData)('sign transaction', async txData => { + const account = create(); + + const signedResult = await signTransaction( + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + TransactionFactory.fromTxData(txData as unknown as TxData), + account.privateKey, + ); + expect(signedResult).toBeDefined(); + expect(signedResult.messageHash).toBeDefined(); + expect(signedResult.rawTransaction).toBeDefined(); + expect(signedResult.transactionHash).toBeDefined(); + expect(signedResult.r).toMatch(/0[xX][0-9a-fA-F]{64}/); + expect(signedResult.s).toMatch(/0[xX][0-9a-fA-F]{64}/); + expect(signedResult.v).toMatch(/0[xX][0-9a-fA-F]+/); + }); + + it.each(transactionsTestData)('Recover transaction', async txData => { + const account = create(); + const txObj = { ...txData, from: account.address }; + const signedResult = await signTransaction( + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + TransactionFactory.fromTxData(txObj), + account.privateKey, + ); + expect(signedResult).toBeDefined(); + + const address: Address = recoverTransaction(signedResult.rawTransaction); + expect(address).toBeDefined(); + expect(address).toEqual(account.address); + }); + }); + + describe('Hash Message', () => { + it.each(validHashMessageData)('%s', (message, hash) => { + expect(hashMessage(message)).toEqual(hash); + }); + }); + + describe('Sign Message', () => { + describe('sign', () => { + it.each(signatureRecoverData)('%s', (data, testObj) => { + const result = sign(data, testObj.privateKey); + expect(result.signature).toEqual(testObj.signature || testObj.signatureOrV); // makes sure we get signature and not V value + expect(result.r).toEqual(testObj.r); + expect(result.s).toEqual(testObj.s); + }); + }); + + describe('recover', () => { + it.each(signatureRecoverData)('%s', (data, testObj) => { + const address = recover(data, testObj.signatureOrV, testObj.prefixedOrR, testObj.s); + expect(address).toEqual(testObj.address); + }); + }); + }); + + describe('encrypt', () => { + describe('valid cases', () => { + it.each(validEncryptData)('%s', async (input, output) => { + const result = await encrypt(input[0], input[1], input[2]).catch(err => { + throw err; + }); + expect(result.version).toBe(output.version); + expect(result.address).toBe(output.address); + expect(result.crypto.ciphertext).toBe(output.crypto.ciphertext); + expect(result.crypto.cipherparams).toEqual(output.crypto.cipherparams); + expect(result.crypto.cipher).toEqual(output.crypto.cipher); + expect(result.crypto.kdf).toBe(output.crypto.kdf); + expect(result.crypto.kdfparams).toEqual(output.crypto.kdfparams); + expect(typeof result.version).toBe('number'); + expect(typeof result.id).toBe('string'); + expect(typeof result.crypto.mac).toBe('string'); + }); + }); + + describe('invalid cases', () => { + it.each(invalidEncryptData)('%s', async (input, output) => { + const result = encrypt(input[0], input[1], input[2]); + await expect(result).rejects.toThrow(output); + }); + }); + }); + + describe('decrypt', () => { + describe('valid cases', () => { + it.each(validDecryptData)('%s', async input => { + const keystore = await encrypt(input[0], input[1], input[2]).catch(err => { + throw err; + }); + + // make sure decrypt does not throw invalid password error + const result = await decrypt(keystore, input[1]); + + expect(JSON.stringify(result)).toEqual(JSON.stringify(privateKeyToAccount(input[3]))); + + const keystoreString = JSON.stringify(keystore); + + const stringResult = await decrypt(keystoreString, input[1], true); + + expect(JSON.stringify(stringResult)).toEqual(JSON.stringify(privateKeyToAccount(input[3]))); + }); + }); + + describe('invalid cases', () => { + it.each(invalidDecryptData)('%s', async (input, output) => { + const result = decrypt(input[0], input[1]); + + await expect(result).rejects.toThrow(output); + }); + }); + + describe('invalid keystore, fails validation', () => { + it.each(invalidKeyStore)('%s', async input => { + const result = decrypt(input[0], input[1]); + + await expect(result).rejects.toThrow(Web3ValidatorError); + }); + }); + + describe('valid signatures for recover', () => { + it.each(validRecover)('&s', (data, signature) => { + recover(data, signature); + }); + }); + }); +}); diff --git a/packages/web3-utils/CHANGELOG.md b/packages/web3-utils/CHANGELOG.md index 953d1ff035a..30c854f6e09 100644 --- a/packages/web3-utils/CHANGELOG.md +++ b/packages/web3-utils/CHANGELOG.md @@ -179,6 +179,10 @@ Documentation: ### Fixed -- Fix unecessary array copy when pack encoding (#6553) +- Fix unnecessary array copy when pack encoding (#6553) ## [Unreleased] + +### Fixed + +- Fixed an issue with detecting Uint8Array (#6486) diff --git a/packages/web3-utils/src/converters.ts b/packages/web3-utils/src/converters.ts index a93eea80cf0..b84d5c28335 100644 --- a/packages/web3-utils/src/converters.ts +++ b/packages/web3-utils/src/converters.ts @@ -42,6 +42,7 @@ import { InvalidNumberError, InvalidUnitError, } from 'web3-errors'; +import { isUint8Array } from './uint8array.js'; // Ref: https://ethdocs.org/en/latest/ether.html // Note: this could be simplified using ** operator, but babel does not handle it well (https://github.com/babel/babel/issues/13109) @@ -91,7 +92,7 @@ export type EtherUnits = keyof typeof ethUnitMap; export const bytesToUint8Array = (data: Bytes): Uint8Array | never => { validator.validate(['bytes'], [data]); - if (data instanceof Uint8Array) { + if (isUint8Array(data)) { return data; } @@ -592,7 +593,11 @@ export const toChecksumAddress = (address: Address): string => { const lowerCaseAddress = address.toLowerCase().replace(/^0x/i, ''); - const hash = utils.uint8ArrayToHexString(keccak256(utf8ToBytes(lowerCaseAddress))); + // calling `Uint8Array.from` because `noble-hashes` checks with `instanceof Uint8Array` that fails in some edge cases: + // https://github.com/paulmillr/noble-hashes/issues/25#issuecomment-1750106284 + const hash = utils.uint8ArrayToHexString( + keccak256(validatorUtils.ensureIfUint8Array(utf8ToBytes(lowerCaseAddress))), + ); if ( isNullish(hash) || diff --git a/packages/web3-utils/src/formatter.ts b/packages/web3-utils/src/formatter.ts index 0cfeb600b9a..1901e1fde21 100644 --- a/packages/web3-utils/src/formatter.ts +++ b/packages/web3-utils/src/formatter.ts @@ -20,7 +20,7 @@ import { isNullish, isObject, JsonSchema, utils, ValidationSchemaInput } from 'w import { bytesToUint8Array, bytesToHex, numberToHex, toBigInt } from './converters.js'; import { mergeDeep } from './objects.js'; import { padLeft } from './string_manipulation.js'; -import { uint8ArrayConcat } from './uint8array.js'; +import { isUint8Array, uint8ArrayConcat } from './uint8array.js'; const { parseBaseType } = utils; @@ -112,7 +112,7 @@ export const convertScalarValue = (value: unknown, ethType: string, format: Data let paddedValue; if (baseTypeSize) { if (typeof value === 'string') paddedValue = padLeft(value, baseTypeSize * 2); - else if (value instanceof Uint8Array) { + else if (isUint8Array(value)) { paddedValue = uint8ArrayConcat( new Uint8Array(baseTypeSize - value.length), value, diff --git a/packages/web3-utils/src/hash.ts b/packages/web3-utils/src/hash.ts index bb4bbd44636..6e498138c29 100644 --- a/packages/web3-utils/src/hash.ts +++ b/packages/web3-utils/src/hash.ts @@ -59,7 +59,7 @@ import { TypedObject, TypedObjectAbbreviated, } from 'web3-types'; -import { isAddress, isNullish, isHexStrict } from 'web3-validator'; +import { utils as validatorUtils, isAddress, isNullish, isHexStrict } from 'web3-validator'; import { bytesToUint8Array, bytesToHex, @@ -71,7 +71,6 @@ import { } from './converters.js'; import { leftPad, rightPad, toTwosComplement } from './string_manipulation.js'; - const SHA3_EMPTY_BYTES = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'; /** @@ -100,7 +99,7 @@ export const sha3 = (data: Bytes): string | undefined => { } else { updatedData = data; } - const hash = bytesToHex(keccak256(updatedData)); + const hash = bytesToHex(keccak256(validatorUtils.ensureIfUint8Array(updatedData))); // EIP-1052 if hash is equal to c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, keccak was given empty data return hash === SHA3_EMPTY_BYTES ? undefined : hash; @@ -159,7 +158,7 @@ export const keccak256Wrapper = ( } else { processedData = bytesToUint8Array(data as Bytes); } - return bytesToHex(keccak256(processedData)); + return bytesToHex(keccak256(validatorUtils.ensureIfUint8Array(processedData))); }; export { keccak256Wrapper as keccak256 }; diff --git a/packages/web3-utils/src/uint8array.ts b/packages/web3-utils/src/uint8array.ts index 0f1173b04e7..cf5d7ee7a45 100644 --- a/packages/web3-utils/src/uint8array.ts +++ b/packages/web3-utils/src/uint8array.ts @@ -14,6 +14,14 @@ GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ + +export function isUint8Array(data: unknown | Uint8Array): data is Uint8Array { + return ( + data instanceof Uint8Array || + (data as { constructor: { name: string } })?.constructor?.name === 'Uint8Array' + ); +} + export function uint8ArrayConcat(...parts: Uint8Array[]): Uint8Array { const length = parts.reduce((prev, part) => { const agg = prev + part.length; diff --git a/packages/web3-utils/test/integration/event_emitter.test.ts b/packages/web3-utils/test/integration/event_emitter.test.ts index 7bf018dd00c..68a35796887 100644 --- a/packages/web3-utils/test/integration/event_emitter.test.ts +++ b/packages/web3-utils/test/integration/event_emitter.test.ts @@ -15,11 +15,6 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -// this file contains the unit test for the event emitter in the DOM environment -// it is executed in the jsdom environment (see "@jest-environment jsdom" in the top comment of this file) - -// ignore the following rule to allow keeping `@jest-environment jsdom` on top: -// eslint-disable-next-line header/header import { EventEmitter } from '../../src/event_emitter'; describe('EventEmitter in the browser with Cypress', () => { diff --git a/packages/web3-utils/test/unit/converters_dom.test.ts b/packages/web3-utils/test/unit/converters_dom.test.ts new file mode 100644 index 00000000000..e7ed91a9573 --- /dev/null +++ b/packages/web3-utils/test/unit/converters_dom.test.ts @@ -0,0 +1,439 @@ +/** + * @jest-environment jsdom + */ + +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +// this file contains the unit test for the event emitter in the DOM environment +// it is executed in the jsdom environment (see "@jest-environment jsdom" in the top comment of this file) + +// ignore the following rule to allow keeping `@jest-environment jsdom` on top: +// eslint-disable-next-line header/header +import { TextDecoder, TextEncoder } from 'util'; +// polyfill for jsdom +// @ts-expect-error ignore the error 'Type 'typeof TextDecoder' is not assignable to type ...' +global.TextDecoder = TextDecoder; +global.TextEncoder = TextEncoder; + +/* eslint-disable import/first */ +import { + asciiToHex, + bytesToHex, + fromAscii, + fromDecimal, + fromUtf8, + fromWei, + hexToAscii, + hexToBytes, + hexToNumber, + hexToNumberString, + hexToString, + hexToUtf8, + numberToHex, + stringToHex, + toAscii, + toDecimal, + toHex, + toNumber, + toUtf8, + toWei, + utf8ToHex, + toChecksumAddress, + bytesToUint8Array, + toBigInt, + toBool, +} from '../../src/converters'; + +import { + asciiToHexValidData, + bytesToHexInvalidData, + bytesToHexValidData, + fromWeiInvalidData, + fromWeiValidData, + hexToAsciiValidData, + hexToBytesInvalidData, + hexToBytesValidData, + hexToNumberInvalidData, + hexToNumberValidData, + hexToUtf8InvalidData, + hexToUtf8ValidData, + toUtf8ValidData, + numberToHexInvalidData, + numberToHexValidData, + toHexValidData, + toHexInvalidData, + toWeiInvalidData, + toWeiValidData, + utf8ToHexInvalidData, + utf8ToHexValidData, + toCheckSumValidData, + bytesToUint8ArrayInvalidData, + bytesToUint8ArrayValidData, + toBigIntValidData, + toBigIntInvalidData, + toCheckSumInvalidData, + numberToHexstrictValidData, + toBoolValidData, + toBoolInvalidData, +} from '../fixtures/converters'; + +describe('converters', () => { + describe('bytesToHex', () => { + describe('valid cases', () => { + it.each(bytesToHexValidData)('%s', (input, output) => { + expect(bytesToHex(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(bytesToHexInvalidData)('%s', (input, output) => { + expect(() => bytesToHex(input)).toThrow(output); + }); + }); + }); + + describe('hexToBytes', () => { + describe('valid cases', () => { + it.each(hexToBytesValidData)('%s', (input, output) => { + expect(hexToBytes(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToBytesInvalidData)('%s', (input, output) => { + expect(() => hexToBytes(input)).toThrow(output); + }); + }); + }); + + describe('numberToHex', () => { + describe('valid cases', () => { + it.each(numberToHexValidData)('%s', (input, output) => { + expect(numberToHex(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(numberToHexInvalidData)('%s', (input, output) => { + expect(() => numberToHex(input)).toThrow(output); + }); + }); + + describe('valid hexstrict cases', () => { + it.each(numberToHexstrictValidData)('%s', (input, output) => { + expect(numberToHex(input, true)).toEqual(output); + }); + }); + }); + + describe('fromDecimal', () => { + describe('valid cases', () => { + it.each(numberToHexValidData)('%s', (input, output) => { + expect(fromDecimal(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(numberToHexInvalidData)('%s', (input, output) => { + expect(() => fromDecimal(input)).toThrow(output); + }); + }); + }); + + describe('hexToNumber', () => { + describe('valid cases', () => { + it.each(hexToNumberValidData)('%s', (input, output) => { + expect(hexToNumber(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToNumberInvalidData)('%s', (input, output) => { + expect(() => hexToNumber(input)).toThrow(output); + }); + }); + }); + + describe('toDecimal', () => { + describe('valid cases', () => { + it.each(hexToNumberValidData)('%s', (input, output) => { + expect(toDecimal(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToNumberInvalidData)('%s', (input, output) => { + expect(() => toDecimal(input)).toThrow(output); + }); + }); + }); + + describe('hexToNumberString', () => { + it.each(hexToNumberValidData)('%s', (input, output) => { + expect(hexToNumberString(input)).toEqual(output.toString()); + }); + }); + + describe('utf8ToHex', () => { + describe('valid cases', () => { + it.each(utf8ToHexValidData)('%s', (input, output) => { + expect(utf8ToHex(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(utf8ToHexInvalidData)('%s', (input, output) => { + expect(() => utf8ToHex(input)).toThrow(output); + }); + }); + }); + + describe('fromUtf8', () => { + describe('valid cases', () => { + it.each(utf8ToHexValidData)('%s', (input, output) => { + expect(fromUtf8(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(utf8ToHexInvalidData)('%s', (input, output) => { + expect(() => fromUtf8(input)).toThrow(output); + }); + }); + }); + + describe('stringToHex', () => { + describe('valid cases', () => { + it.each(utf8ToHexValidData)('%s', (input, output) => { + expect(stringToHex(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(utf8ToHexInvalidData)('%s', (input, output) => { + expect(() => stringToHex(input)).toThrow(output); + }); + }); + }); + + describe('hexToUtf8', () => { + describe('valid cases', () => { + it.each(hexToUtf8ValidData)('%s', (input, output) => { + expect(hexToUtf8(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToUtf8InvalidData)('%s', (input, output) => { + expect(() => hexToUtf8(input)).toThrow(output); + }); + }); + }); + + describe('toUtf8', () => { + describe('valid cases', () => { + it.each(toUtf8ValidData)('%s', (input, output) => { + expect(toUtf8(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToUtf8InvalidData)('%s', (input, output) => { + expect(() => toUtf8(input)).toThrow(output); + }); + }); + }); + + describe('hexToString', () => { + describe('valid cases', () => { + it.each(hexToUtf8ValidData)('%s', (input, output) => { + expect(hexToString(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToUtf8InvalidData)('%s', (input, output) => { + expect(() => hexToString(input)).toThrow(output); + }); + }); + }); + + describe('asciiToHex', () => { + describe('valid cases', () => { + it.each(asciiToHexValidData)('%s', (input, output) => { + expect(asciiToHex(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(utf8ToHexInvalidData)('%s', (input, output) => { + expect(() => asciiToHex(input)).toThrow(output); + }); + }); + }); + + describe('fromAscii', () => { + describe('valid cases', () => { + it.each(asciiToHexValidData)('%s', (input, output) => { + expect(fromAscii(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(utf8ToHexInvalidData)('%s', (input, output) => { + expect(() => fromAscii(input)).toThrow(output); + }); + }); + }); + + describe('hexToAscii', () => { + describe('valid cases', () => { + it.each(hexToAsciiValidData)('%s', (input, output) => { + expect(hexToAscii(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToUtf8InvalidData)('%s', (input, output) => { + expect(() => hexToAscii(input)).toThrow(output); + }); + }); + }); + + describe('toAscii', () => { + describe('valid cases', () => { + it.each(hexToAsciiValidData)('%s', (input, output) => { + expect(toAscii(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(hexToUtf8InvalidData)('%s', (input, output) => { + expect(() => toAscii(input)).toThrow(output); + }); + }); + }); + + describe('toHex', () => { + describe('return value', () => { + it.each(toHexValidData)('%s', (input, output) => { + expect(toHex(input)).toEqual(output[0]); + }); + }); + + describe('return type', () => { + it.each(toHexValidData)('%s', (input, output) => { + expect(toHex(input, true)).toEqual(output[1]); + }); + }); + + describe('invalid cases', () => { + it.each(toHexInvalidData)('%s', (input, output) => { + expect(() => toHex(input)).toThrow(output); + }); + }); + }); + + describe('toNumber', () => { + it.each([...hexToNumberValidData, [123, 123], ['123', 123]])('%s', (input, output) => { + expect(toNumber(input)).toEqual(output); + }); + }); + + describe('fromWei', () => { + describe('valid cases', () => { + it.each(fromWeiValidData)('%s', (input, output) => { + expect(fromWei(input[0], input[1])).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(fromWeiInvalidData)('%s', (input, output) => { + expect(() => fromWei(input[0], input[1])).toThrow(output); + }); + }); + }); + + describe('toWei', () => { + describe('valid cases', () => { + it.each(toWeiValidData)('%s', (input, output) => { + expect(toWei(output, input[1])).toEqual(input[0].toString()); + }); + }); + + describe('invalid cases', () => { + it.each(toWeiInvalidData)('%s', (input, output) => { + expect(() => toWei(input[0], input[1])).toThrow(output); + }); + }); + }); + describe('toChecksumAddress', () => { + describe('valid cases', () => { + it.each(toCheckSumValidData)('%s', (input, output) => { + expect(toChecksumAddress(input)).toEqual(output); + }); + }); + describe('invalid cases', () => { + it.each(toCheckSumInvalidData)('%s', (input, output) => { + expect(() => toChecksumAddress(input)).toThrow(output); + }); + }); + }); + describe('bytesToUint8Array', () => { + describe('bytesToUint8Array', () => { + describe('valid cases', () => { + it.each(bytesToUint8ArrayValidData)('%s', (input, output) => { + expect(bytesToUint8Array(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(bytesToUint8ArrayInvalidData)('%s', (input, output) => { + expect(() => bytesToUint8Array(input)).toThrow(output); + }); + }); + }); + }); + describe('toBigInt', () => { + describe('valid cases', () => { + it.each(toBigIntValidData)('%s', (input, output) => { + expect(toBigInt(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(toBigIntInvalidData)('%s', (input, output) => { + expect(() => toBigInt(input)).toThrow(output); + }); + }); + }); + + describe('toBool', () => { + describe('valid cases', () => { + it.each(toBoolValidData)('%s', (input, output) => { + expect(toBool(input)).toEqual(output); + }); + }); + + describe('invalid cases', () => { + it.each(toBoolInvalidData)('%s', (input, output) => { + expect(() => toBool(input)).toThrow(output); + }); + }); + }); +}); diff --git a/packages/web3-utils/test/unit/hash_dom.test.ts b/packages/web3-utils/test/unit/hash_dom.test.ts new file mode 100644 index 00000000000..1e4dd67cf0c --- /dev/null +++ b/packages/web3-utils/test/unit/hash_dom.test.ts @@ -0,0 +1,166 @@ +/** + * @jest-environment jsdom + */ + +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +// this file contains the unit test for the event emitter in the DOM environment +// it is executed in the jsdom environment (see "@jest-environment jsdom" in the top comment of this file) + +// ignore the following rule to allow keeping `@jest-environment jsdom` on top: +// eslint-disable-next-line header/header +import { TextEncoder } from 'util'; +// polyfill for jsdom +global.TextEncoder = TextEncoder; + +/* eslint-disable import/first */ +import { keccak256 } from 'js-sha3'; +import { + sha3, + sha3Raw, + soliditySha3, + soliditySha3Raw, + encodePacked, + keccak256 as web3keccak256, +} from '../../src/hash'; +import { + sha3Data, + sha3ValidData, + soliditySha3RawValidData, + sha3RawValidData, + soliditySha3ValidData, + soliditySha3InvalidData, + compareSha3JSValidData, + compareSha3JSRawValidData, + encodePackData, + encodePackedInvalidData, + keccak256ValidData, + soliditySha3BigIntValidData, +} from '../fixtures/hash'; + +describe('hash', () => { + describe('sha3', () => { + describe('valid cases', () => { + it.each(sha3ValidData)('%s', (input, output) => { + expect(sha3(input)).toEqual(output); + }); + }); + + describe('compare with js-sha3 normal cases', () => { + it.each(sha3Data)('%s', input => { + expect(sha3(input)).toBe(`0x${keccak256(input)}`); + }); + }); + + describe('compare with js-sha3 uint8array cases', () => { + it.each(compareSha3JSValidData)('%s', (input, output) => { + expect(sha3(input)).toBe(`0x${keccak256(output)}`); + }); + }); + }); + + describe('sha3Raw', () => { + describe('valid cases', () => { + it.each(sha3RawValidData)('%s', (input, output) => { + expect(sha3Raw(input)).toEqual(output); + }); + }); + describe('comparing with js-sha3 cases', () => { + it.each(compareSha3JSRawValidData)('%s', (input, output) => { + expect(sha3Raw(input)).toBe(`0x${keccak256(output)}`); + }); + }); + }); + + describe('soliditySha3', () => { + describe('valid cases', () => { + it.each(soliditySha3ValidData)('%s', (input, output) => { + expect(soliditySha3(...input)).toEqual(output); + }); + }); + describe('invalid cases', () => { + it.each(soliditySha3InvalidData)('%s', (input, output) => { + expect(() => soliditySha3(input)).toThrow(output); + }); + }); + }); + + describe('soliditySha3Raw', () => { + describe('valid cases', () => { + it.each(soliditySha3RawValidData)('%s', (input, output) => { + expect(soliditySha3Raw(...input)).toEqual(output); + }); + }); + describe('invalid cases', () => { + it.each(soliditySha3InvalidData)('%s', (input, output) => { + expect(() => soliditySha3Raw(input)).toThrow(output); + }); + }); + }); + + describe('encodePacked', () => { + describe('valid cases', () => { + it.each(encodePackData)('%s', (input, output) => { + expect(encodePacked(...input)).toEqual(output); + }); + }); + describe('invalid cases', () => { + it.each(encodePackedInvalidData)('%s', (input, output) => { + expect(() => encodePacked(input)).toThrow(output); + }); + }); + }); + describe('keccak256', () => { + describe('valid cases', () => { + it.each(keccak256ValidData)('%s', (input, output) => { + expect(web3keccak256(input)).toEqual(output); + }); + }); + }); + + describe('extra types supporting', () => { + it('object', () => { + const res = soliditySha3({ + historicBlock: { + hash: '0xcba0b90a5e65512202091c12a2e3b328f374715b9f1c8f32cb4600c726fe2aa6', + height: 1, + }, + networkId: 5777, + }); + expect(res).toBe('0x00203462b63e3a8ca15da715e490c676b0e370f47823e31383fe43c25da3b78d'); + }); + it('object in string', () => { + const res = soliditySha3( + '{"contents":"pragma solidity >=0.4.21 <0.6.0;\\n\\ncontract Migrations {\\n address public owner;\\n uint public last_completed_migration;\\n\\n constructor() public {\\n owner = msg.sender;\\n }\\n\\n modifier restricted() {\\n if (msg.sender == owner) _;\\n }\\n\\n function setCompleted(uint completed) public restricted {\\n last_completed_migration = completed;\\n }\\n\\n function upgrade(address new_address) public restricted {\\n Migrations upgraded = Migrations(new_address);\\n upgraded.setCompleted(last_completed_migration);\\n }\\n}\\n","sourcePath":"/Users/gnidan/src/work/reproduce/2019/01/21/artifacts/contracts/Migrations.sol"}', + ); + expect(res).toBe('0xdb092e2751b8dcb7c8509baade3c0ac290414a71685823c3cbeb28667970b0bd'); + }); + it('another object in string', () => { + const res = soliditySha3( + '{"bytes":"608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610314806100606000396000f3fe608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100b85780638da5cb5b146100e3578063fdacd5761461013a575b600080fd5b34801561007357600080fd5b506100b66004803603602081101561008a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610175565b005b3480156100c457600080fd5b506100cd61025d565b6040518082815260200191505060405180910390f35b3480156100ef57600080fd5b506100f8610263565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561014657600080fd5b506101736004803603602081101561015d57600080fd5b8101908080359060200190929190505050610288565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561025a5760008190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561024057600080fd5b505af1158015610254573d6000803e3d6000fd5b50505050505b50565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102e557806001819055505b5056fea165627a7a7230582013359aba5684f88626fb6a58a003236e309ef1462172af4afb4afb9bd2532b510029","linkReferences":[]}', + ); + expect(res).toBe('0x46e99868594ceb46b7cd37e4b33d635f12a7751671f8c51dd8218fa0dcf82901'); + }); + + describe('BigInt soliditySha3', () => { + it.each(soliditySha3BigIntValidData)('%s', (input, output) => { + expect(soliditySha3(...input)).toEqual(output); + }); + }); + }); +}); diff --git a/packages/web3-validator/CHANGELOG.md b/packages/web3-validator/CHANGELOG.md index d36b2f7af2a..9d8ac2e190b 100644 --- a/packages/web3-validator/CHANGELOG.md +++ b/packages/web3-validator/CHANGELOG.md @@ -156,4 +156,8 @@ Documentation: - Validator will now properly handle all valid numeric type sizes: intN / uintN where 8 < = N < = 256 and N % 8 == 0 (#6434) - Will now throw SchemaFormatError when unsupported format is passed to `convertToZod` method (#6434) -## [Unreleased] \ No newline at end of file +## [Unreleased] + +### Fixed + +- Fixed an issue with detecting Uint8Array (#6486) diff --git a/packages/web3-validator/src/utils.ts b/packages/web3-validator/src/utils.ts index 6be75256ce3..f7d632d1dc9 100644 --- a/packages/web3-validator/src/utils.ts +++ b/packages/web3-validator/src/utils.ts @@ -495,3 +495,14 @@ export function hexToUint8Array(hex: string): Uint8Array { } return bytes } + +// @TODO: Remove this function and its usages once all sub dependencies uses version 1.3.3 or above of @noble/hashes +export function ensureIfUint8Array(data: T) { + if ( + !(data instanceof Uint8Array) && + (data as { constructor: { name: string } })?.constructor?.name === 'Uint8Array' + ) { + return Uint8Array.from(data as unknown as Uint8Array); + } + return data; +} \ No newline at end of file diff --git a/packages/web3-validator/src/validation/address.ts b/packages/web3-validator/src/validation/address.ts index f1c5b820a35..38ebebe65f6 100644 --- a/packages/web3-validator/src/validation/address.ts +++ b/packages/web3-validator/src/validation/address.ts @@ -18,8 +18,9 @@ along with web3.js. If not, see . import { keccak256 } from 'ethereum-cryptography/keccak.js'; import { utf8ToBytes } from 'ethereum-cryptography/utils.js'; import { ValidInputTypes } from '../types.js'; -import { uint8ArrayToHexString } from '../utils.js'; +import { ensureIfUint8Array, uint8ArrayToHexString } from '../utils.js'; import { isHexStrict } from './string.js'; +import { isUint8Array } from './bytes.js'; /** * Checks the checksum of a given address. Will also return false on non-checksum addresses. @@ -29,7 +30,7 @@ export const checkAddressCheckSum = (data: string): boolean => { const address = data.slice(2); const updatedData = utf8ToBytes(address.toLowerCase()); - const addressHash = uint8ArrayToHexString(keccak256(updatedData)).slice(2); + const addressHash = uint8ArrayToHexString(keccak256(ensureIfUint8Array(updatedData))).slice(2); for (let i = 0; i < 40; i += 1) { // the nth letter should be uppercase if the nth digit of casemap is 1 @@ -47,13 +48,13 @@ export const checkAddressCheckSum = (data: string): boolean => { * Checks if a given string is a valid Ethereum address. It will also check the checksum, if the address has upper and lowercase letters. */ export const isAddress = (value: ValidInputTypes, checkChecksum = true) => { - if (typeof value !== 'string' && !(value instanceof Uint8Array)) { + if (typeof value !== 'string' && !isUint8Array(value)) { return false; } let valueToCheck: string; - if (value instanceof Uint8Array) { + if (isUint8Array(value)) { valueToCheck = uint8ArrayToHexString(value); } else if (typeof value === 'string' && !isHexStrict(value)) { valueToCheck = value.toLowerCase().startsWith('0x') ? value : `0x${value}`; diff --git a/packages/web3-validator/src/validation/bytes.ts b/packages/web3-validator/src/validation/bytes.ts index ef15fbc0120..68c431c50ee 100644 --- a/packages/web3-validator/src/validation/bytes.ts +++ b/packages/web3-validator/src/validation/bytes.ts @@ -22,7 +22,8 @@ import { isHexStrict } from './string.js'; /** * checks input if typeof data is valid Uint8Array input */ -export const isUint8Array = (data: ValidInputTypes) => data instanceof Uint8Array; +export const isUint8Array = (data: ValidInputTypes): data is Uint8Array => + data instanceof Uint8Array || data?.constructor?.name === 'Uint8Array'; export const isBytes = ( value: ValidInputTypes | Uint8Array | number[], @@ -30,7 +31,7 @@ export const isBytes = ( abiType: 'bytes', }, ) => { - if (typeof value !== 'string' && !Array.isArray(value) && !(value instanceof Uint8Array)) { + if (typeof value !== 'string' && !Array.isArray(value) && !isUint8Array(value)) { return false; } diff --git a/yarn.lock b/yarn.lock index bceda55cac0..291e08ef11d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1386,12 +1386,12 @@ dependencies: "@noble/hashes" "1.3.1" -"@noble/hashes@1.1.2", "@noble/hashes@~1.1.1": +"@noble/hashes@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== -"@noble/hashes@1.3.0", "@noble/hashes@~1.3.0": +"@noble/hashes@1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== @@ -1401,10 +1401,15 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== -"@noble/hashes@~1.3.1": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" - integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== +"@noble/hashes@~1.1.1": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.5.tgz#1a0377f3b9020efe2fae03290bd2a12140c95c11" + integrity sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ== + +"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== "@noble/secp256k1@1.6.3", "@noble/secp256k1@~1.6.0": version "1.6.3" @@ -2301,6 +2306,11 @@ dependencies: "@babel/types" "^7.3.0" +"@types/benchmark@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@types/benchmark/-/benchmark-2.1.5.tgz#940c1850c18fdfdaee3fd6ed29cd92ae0d445b45" + integrity sha512-cKio2eFB3v7qmKcvIHLUMw/dIx/8bhWPuzpzRT4unCPRTD8VdA9Zb0afxpcxOqR4PixRS7yT42FqGS8BYL8g1w== + "@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" @@ -3505,6 +3515,14 @@ before-after-hook@^2.2.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== +benchmark@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-2.1.4.tgz#09f3de31c916425d498cc2ee565a0ebf3c2a5629" + integrity sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ== + dependencies: + lodash "^4.17.4" + platform "^1.3.3" + big-integer@^1.6.44: version "1.6.51" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" @@ -8913,7 +8931,7 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -10463,6 +10481,11 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +platform@^1.3.3: + version "1.3.6" + resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" + integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"