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"