From 4fb927f4245fe167cb158ef61ada354359919ab1 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Thu, 31 Aug 2023 09:32:34 +0200 Subject: [PATCH 01/16] implement `EventEmitter` compatible with browsers --- packages/web3-core/src/web3_event_emitter.ts | 2 +- .../web3-eth-accounts/src/common/common.ts | 3 +- .../test/unit/__mocks__/isomorphic-ws.ts | 2 +- packages/web3-utils/src/event_emitter.ts | 104 ++++++++++++++++++ packages/web3-utils/src/index.ts | 1 + .../test/unit/socket_provider.test.ts | 2 +- 6 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 packages/web3-utils/src/event_emitter.ts diff --git a/packages/web3-core/src/web3_event_emitter.ts b/packages/web3-core/src/web3_event_emitter.ts index 1a0e6016953..36180c51df3 100644 --- a/packages/web3-core/src/web3_event_emitter.ts +++ b/packages/web3-core/src/web3_event_emitter.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 { EventEmitter } from 'events'; +import { EventEmitter } from 'web3-utils'; export type Web3EventMap = Record; export type Web3EventKey = string & keyof T; diff --git a/packages/web3-eth-accounts/src/common/common.ts b/packages/web3-eth-accounts/src/common/common.ts index 0c668c01579..e941338763a 100644 --- a/packages/web3-eth-accounts/src/common/common.ts +++ b/packages/web3-eth-accounts/src/common/common.ts @@ -15,9 +15,8 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ import pkg from 'crc-32'; -import { EventEmitter } from 'events'; +import { EventEmitter, bytesToHex, hexToBytes, uint8ArrayConcat } from 'web3-utils'; import type { Numbers } from 'web3-types'; -import { bytesToHex, hexToBytes, uint8ArrayConcat } from 'web3-utils'; import { TypeOutput } from './types.js'; import { intToUint8Array, toType, parseGethGenesis } from './utils.js'; import goerli from './chains/goerli.js'; diff --git a/packages/web3-providers-ws/test/unit/__mocks__/isomorphic-ws.ts b/packages/web3-providers-ws/test/unit/__mocks__/isomorphic-ws.ts index bf3bd4cf1dd..42a0cdf4871 100644 --- a/packages/web3-providers-ws/test/unit/__mocks__/isomorphic-ws.ts +++ b/packages/web3-providers-ws/test/unit/__mocks__/isomorphic-ws.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 { EventEmitter } from 'events'; +import { EventEmitter } from 'web3-utils'; export default class WebSocket extends EventEmitter { public readyState: number; diff --git a/packages/web3-utils/src/event_emitter.ts b/packages/web3-utils/src/event_emitter.ts new file mode 100644 index 00000000000..a80989d521b --- /dev/null +++ b/packages/web3-utils/src/event_emitter.ts @@ -0,0 +1,104 @@ +/* +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 . +*/ + +import { EventEmitter as NodeEventEmitter } from 'events'; + +type Callback = (params: any) => void | Promise; + +export class InBrowserEventEmitter extends EventTarget { + private _listeners: Record = {}; + private maxListeners = Number.MAX_SAFE_INTEGER; + + public on(eventName: string, fn: Callback) { + super.addEventListener(eventName, fn as EventListener); + this._addToListeners(eventName, fn); + return this; + } + + public once(eventName: string, fn: Callback) { + const onceCallback = async (params: unknown) => { + await fn(params); + this.off(eventName, onceCallback); + }; + return this.on(eventName, onceCallback); + } + + public off(eventName: string, fn: Callback) { + super.removeEventListener(eventName, fn as EventListener); + this._removeFromListeners(eventName, fn); + return this; + } + + public emit(eventName: string, params: unknown) { + const event = new CustomEvent(eventName, { detail: params }); + return super.dispatchEvent(event); + } + + public listenerCount(eventName: string): number { + const eventListeners = this._listeners[eventName]; + return eventListeners ? eventListeners.length : 0; + } + + public listeners(eventName: string): Callback[] { + return this._listeners[eventName] || []; + } + + public eventNames(): string[] { + return Object.keys(this._listeners); + } + + public removeAllListeners() { + this._listeners = {}; + return this; + } + + public setMaxListeners(maxListeners: number) { + this.maxListeners = maxListeners; + return this; + } + + public getMaxListeners(): number { + return this.maxListeners; + } + + private _addToListeners(eventName: string, fn: Callback) { + if (!this._listeners[eventName]) { + this._listeners[eventName] = []; + } + this._listeners[eventName].push(fn); + } + + private _removeFromListeners(eventName: string, fn: Callback) { + const eventListeners = this._listeners[eventName]; + if (eventListeners) { + const index = eventListeners.indexOf(fn); + if (index !== -1) { + eventListeners.splice(index, 1); + } + } + } +} + +// eslint-disable-next-line import/no-mutable-exports +export let EventEmitter: typeof NodeEventEmitter; +// Check if the code is running in a Node.js environment +if (typeof window === 'undefined') { + EventEmitter = NodeEventEmitter; +} else { + // Fallback for the browser environment + EventEmitter = InBrowserEventEmitter as unknown as typeof NodeEventEmitter; +} diff --git a/packages/web3-utils/src/index.ts b/packages/web3-utils/src/index.ts index 3990d0a6b0a..52bb219692b 100644 --- a/packages/web3-utils/src/index.ts +++ b/packages/web3-utils/src/index.ts @@ -16,6 +16,7 @@ along with web3.js. If not, see . */ export * from './converters.js'; +export * from './event_emitter.js'; export * from './validation.js'; export * from './formatter.js'; export * from './hash.js'; diff --git a/packages/web3-utils/test/unit/socket_provider.test.ts b/packages/web3-utils/test/unit/socket_provider.test.ts index 054e11691e7..d50f9aaffac 100644 --- a/packages/web3-utils/test/unit/socket_provider.test.ts +++ b/packages/web3-utils/test/unit/socket_provider.test.ts @@ -15,8 +15,8 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -import { EventEmitter } from 'stream'; import { Web3APIPayload, EthExecutionAPI, JsonRpcResponse, Web3ProviderStatus } from 'web3-types'; +import { EventEmitter } from '../../src/event_emitter'; // eslint-disable-next-line import/no-relative-packages import { sleep } from '../../../../fixtures/utils'; import { SocketProvider } from '../../src/socket_provider'; From 0e9ce5d11d054abc1ccb399981aa42560cb0c393 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:35:42 +0200 Subject: [PATCH 02/16] some fixes at InBrowserEventEmitter --- packages/web3-utils/src/event_emitter.ts | 35 ++++++++++++++++-------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/web3-utils/src/event_emitter.ts b/packages/web3-utils/src/event_emitter.ts index a80989d521b..2a9e19b7437 100644 --- a/packages/web3-utils/src/event_emitter.ts +++ b/packages/web3-utils/src/event_emitter.ts @@ -17,20 +17,31 @@ along with web3.js. If not, see . import { EventEmitter as NodeEventEmitter } from 'events'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any type Callback = (params: any) => void | Promise; +type EventTargetCallback = (params: CustomEvent) => void; + +const wrapFunction = + (fn: Callback): EventTargetCallback => + (params: CustomEvent) => + fn(params.detail); + +/** + * This class copy the behavior of Node.js EventEmitter class. + * It is used to provide the same interface for the browser environment. + */ export class InBrowserEventEmitter extends EventTarget { - private _listeners: Record = {}; + private _listeners: Record = {}; private maxListeners = Number.MAX_SAFE_INTEGER; public on(eventName: string, fn: Callback) { - super.addEventListener(eventName, fn as EventListener); - this._addToListeners(eventName, fn); + this.addEventListener(eventName, fn); return this; } public once(eventName: string, fn: Callback) { - const onceCallback = async (params: unknown) => { + const onceCallback = async (params: Callback) => { await fn(params); this.off(eventName, onceCallback); }; @@ -38,8 +49,7 @@ export class InBrowserEventEmitter extends EventTarget { } public off(eventName: string, fn: Callback) { - super.removeEventListener(eventName, fn as EventListener); - this._removeFromListeners(eventName, fn); + this.removeEventListener(eventName, fn); return this; } @@ -54,7 +64,7 @@ export class InBrowserEventEmitter extends EventTarget { } public listeners(eventName: string): Callback[] { - return this._listeners[eventName] || []; + return this._listeners[eventName].map(value => value[0]) || []; } public eventNames(): string[] { @@ -75,18 +85,21 @@ export class InBrowserEventEmitter extends EventTarget { return this.maxListeners; } - private _addToListeners(eventName: string, fn: Callback) { + public addEventListener(eventName: string, fn: Callback) { + const wrappedFn = wrapFunction(fn); + super.addEventListener(eventName, wrappedFn as EventListener); if (!this._listeners[eventName]) { this._listeners[eventName] = []; } - this._listeners[eventName].push(fn); + this._listeners[eventName].push([fn, wrappedFn]); } - private _removeFromListeners(eventName: string, fn: Callback) { + public removeEventListener(eventName: string, fn: Callback) { const eventListeners = this._listeners[eventName]; if (eventListeners) { - const index = eventListeners.indexOf(fn); + const index = eventListeners.findIndex(item => item[0] === fn); if (index !== -1) { + super.removeEventListener(eventName, eventListeners[index][1] as EventListener); eventListeners.splice(index, 1); } } From 28ba5dc1f078158f878310b76409c6400feab505 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Mon, 4 Sep 2023 16:38:13 +0200 Subject: [PATCH 03/16] some renaming inside event_emitter.ts --- packages/web3-utils/src/event_emitter.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/web3-utils/src/event_emitter.ts b/packages/web3-utils/src/event_emitter.ts index 2a9e19b7437..6d2a6e298e8 100644 --- a/packages/web3-utils/src/event_emitter.ts +++ b/packages/web3-utils/src/event_emitter.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 { EventEmitter as NodeEventEmitter } from 'events'; +import { EventEmitter as EventEmitterAtNode } from 'events'; // eslint-disable-next-line @typescript-eslint/no-explicit-any type Callback = (params: any) => void | Promise; @@ -31,7 +31,7 @@ const wrapFunction = * This class copy the behavior of Node.js EventEmitter class. * It is used to provide the same interface for the browser environment. */ -export class InBrowserEventEmitter extends EventTarget { +export class EventEmitterAtBrowser extends EventTarget { private _listeners: Record = {}; private maxListeners = Number.MAX_SAFE_INTEGER; @@ -107,11 +107,11 @@ export class InBrowserEventEmitter extends EventTarget { } // eslint-disable-next-line import/no-mutable-exports -export let EventEmitter: typeof NodeEventEmitter; +export let EventEmitter: typeof EventEmitterAtNode; // Check if the code is running in a Node.js environment if (typeof window === 'undefined') { - EventEmitter = NodeEventEmitter; + EventEmitter = EventEmitterAtNode; } else { // Fallback for the browser environment - EventEmitter = InBrowserEventEmitter as unknown as typeof NodeEventEmitter; + EventEmitter = EventEmitterAtBrowser as unknown as typeof EventEmitterAtNode; } From 195463d433e43c3ac730e83bc3113da4318d95ef Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:25:22 +0200 Subject: [PATCH 04/16] export EventEmitter as a class --- packages/web3-utils/src/event_emitter.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/web3-utils/src/event_emitter.ts b/packages/web3-utils/src/event_emitter.ts index 6d2a6e298e8..5254c49e668 100644 --- a/packages/web3-utils/src/event_emitter.ts +++ b/packages/web3-utils/src/event_emitter.ts @@ -107,11 +107,13 @@ export class EventEmitterAtBrowser extends EventTarget { } // eslint-disable-next-line import/no-mutable-exports -export let EventEmitter: typeof EventEmitterAtNode; +let EventEmitterType: typeof EventEmitterAtNode; // Check if the code is running in a Node.js environment if (typeof window === 'undefined') { - EventEmitter = EventEmitterAtNode; + EventEmitterType = EventEmitterAtNode; } else { // Fallback for the browser environment - EventEmitter = EventEmitterAtBrowser as unknown as typeof EventEmitterAtNode; + EventEmitterType = EventEmitterAtBrowser as unknown as typeof EventEmitterAtNode; } + +export class EventEmitter extends EventEmitterType {} \ No newline at end of file From 180805813b10401068407d7db37833e8c90e731b Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:25:57 +0200 Subject: [PATCH 05/16] add unit tests for EventEmitter --- .../test/unit/event_emitter.test.ts | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 packages/web3-utils/test/unit/event_emitter.test.ts diff --git a/packages/web3-utils/test/unit/event_emitter.test.ts b/packages/web3-utils/test/unit/event_emitter.test.ts new file mode 100644 index 00000000000..d3bdeb7a623 --- /dev/null +++ b/packages/web3-utils/test/unit/event_emitter.test.ts @@ -0,0 +1,124 @@ +/* +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 will contain the unit test for the event emitter + +import { EventEmitter } from '../../src/event_emitter'; + +describe('EventEmitter', () => { + // let emitter = new EventEmitter(); + let emitter: EventEmitter; + + beforeEach(() => { + emitter = new EventEmitter(); + + //emitter.removeAllListeners('test'); + }); + + describe('on', () => { + it('should add a listener for the specified event', () => { + const callback = jest.fn(); + emitter.on('test', callback); + emitter.emit('test', 'hello'); + expect(callback).toHaveBeenCalledWith('hello'); + }); + }); + + describe('once', () => { + it('should add a listener for the specified event that is only called once', () => { + const callback = jest.fn(); + emitter.once('test', callback); + emitter.emit('test', 'hello'); + emitter.emit('test', 'world'); + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith('hello'); + }); + }); + + describe('off', () => { + it('should remove a listener for the specified event', () => { + const callback = jest.fn(); + emitter.on('test', callback); + emitter.off('test', callback); + emitter.emit('test', 'hello'); + expect(callback).not.toHaveBeenCalled(); + }); + }); + + describe('emit', () => { + it('should call all listeners for the specified event', () => { + const callback1 = jest.fn(); + const callback2 = jest.fn(); + emitter.on('test', callback1); + emitter.on('test', callback2); + emitter.emit('test', 'hello'); + expect(callback1).toHaveBeenCalledWith('hello'); + expect(callback2).toHaveBeenCalledWith('hello'); + }); + }); + + describe('listenerCount', () => { + it('should return the number of listeners for the specified event', () => { + const callback1 = jest.fn(); + const callback2 = jest.fn(); + emitter.on('test', callback1); + emitter.on('test', callback2); + expect(emitter.listenerCount('test')).toBe(2); + }); + }); + + describe('listeners', () => { + it('should return an array of listeners for the specified event', () => { + const callback1 = jest.fn(); + const callback2 = jest.fn(); + emitter.on('test', callback1); + emitter.on('test', callback2); + expect(emitter.listeners('test')).toEqual([callback1, callback2]); + }); + }); + + describe('eventNames', () => { + it('should return an array of event names that have listeners', () => { + const callback1 = jest.fn(); + const callback2 = jest.fn(); + emitter.on('test1', callback1); + emitter.on('test2', callback2); + expect(emitter.eventNames()).toEqual(['test1', 'test2']); + }); + }); + + describe('removeAllListeners', () => { + it('should remove all listeners for all events', () => { + const callback1 = jest.fn(); + const callback2 = jest.fn(); + emitter.on('test1', callback1); + emitter.on('test2', callback2); + emitter.removeAllListeners(); + emitter.emit('test1', 'hello'); + emitter.emit('test2', 'world'); + expect(callback1).not.toHaveBeenCalled(); + expect(callback2).not.toHaveBeenCalled(); + }); + }); + + describe('setMaxListeners', () => { + it('should set the maximum number of listeners for an event', () => { + emitter.setMaxListeners(2); + expect(emitter.getMaxListeners()).toBe(2); + }); + }); +}); From 607f4b25f6d946ea3f7ed5184a9961eeabb2dca6 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:32:25 +0200 Subject: [PATCH 06/16] disable lint rule in a file and update yarn.lock --- packages/web3-utils/src/event_emitter.ts | 3 ++- .../web3-utils/test/unit/event_emitter.test.ts | 2 -- yarn.lock | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/web3-utils/src/event_emitter.ts b/packages/web3-utils/src/event_emitter.ts index 5254c49e668..821acc68c20 100644 --- a/packages/web3-utils/src/event_emitter.ts +++ b/packages/web3-utils/src/event_emitter.ts @@ -14,6 +14,7 @@ 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 . */ +/* eslint-disable max-classes-per-file */ import { EventEmitter as EventEmitterAtNode } from 'events'; @@ -31,7 +32,7 @@ const wrapFunction = * This class copy the behavior of Node.js EventEmitter class. * It is used to provide the same interface for the browser environment. */ -export class EventEmitterAtBrowser extends EventTarget { +class EventEmitterAtBrowser extends EventTarget { private _listeners: Record = {}; private maxListeners = Number.MAX_SAFE_INTEGER; diff --git a/packages/web3-utils/test/unit/event_emitter.test.ts b/packages/web3-utils/test/unit/event_emitter.test.ts index d3bdeb7a623..21a1aec7dbf 100644 --- a/packages/web3-utils/test/unit/event_emitter.test.ts +++ b/packages/web3-utils/test/unit/event_emitter.test.ts @@ -25,8 +25,6 @@ describe('EventEmitter', () => { beforeEach(() => { emitter = new EventEmitter(); - - //emitter.removeAllListeners('test'); }); describe('on', () => { diff --git a/yarn.lock b/yarn.lock index f937f1f8418..e3012ba1959 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4021,13 +4021,20 @@ cross-fetch@^2.1.0: node-fetch "^2.6.7" whatwg-fetch "^2.0.4" -cross-fetch@^3.0.4, cross-fetch@^3.1.5: +cross-fetch@^3.0.4: version "3.1.5" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== dependencies: node-fetch "2.6.7" +cross-fetch@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" + integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== + dependencies: + node-fetch "^2.6.12" + cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -8593,6 +8600,13 @@ node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" +node-fetch@^2.6.12: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-gyp-build@4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4" From 4c8bc1bfd157b016bb01748630c1fab576b08647 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 4 Oct 2023 14:24:07 +0200 Subject: [PATCH 07/16] apply fixes and add tests for EventEmitter --- packages/web3-utils/src/event_emitter.ts | 10 +- .../test/unit/event_emitter_dom.test.ts | 125 ++++++++++++++++++ ...ter.test.ts => event_emitter_node.test.ts} | 4 +- 3 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 packages/web3-utils/test/unit/event_emitter_dom.test.ts rename packages/web3-utils/test/unit/{event_emitter.test.ts => event_emitter_node.test.ts} (96%) diff --git a/packages/web3-utils/src/event_emitter.ts b/packages/web3-utils/src/event_emitter.ts index 821acc68c20..7d9c3ccdb2e 100644 --- a/packages/web3-utils/src/event_emitter.ts +++ b/packages/web3-utils/src/event_emitter.ts @@ -43,8 +43,8 @@ class EventEmitterAtBrowser extends EventTarget { public once(eventName: string, fn: Callback) { const onceCallback = async (params: Callback) => { - await fn(params); this.off(eventName, onceCallback); + await fn(params); }; return this.on(eventName, onceCallback); } @@ -73,6 +73,14 @@ class EventEmitterAtBrowser extends EventTarget { } public removeAllListeners() { + Object.keys(this._listeners).forEach(event => { + this._listeners[event].forEach( + (listener: [key: Callback, value: EventTargetCallback]) => { + super.removeEventListener(event, listener[1] as EventListener); + }, + ); + }); + this._listeners = {}; return this; } diff --git a/packages/web3-utils/test/unit/event_emitter_dom.test.ts b/packages/web3-utils/test/unit/event_emitter_dom.test.ts new file mode 100644 index 00000000000..432bdcc54a8 --- /dev/null +++ b/packages/web3-utils/test/unit/event_emitter_dom.test.ts @@ -0,0 +1,125 @@ +/* +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 will contain the unit test for the event emitter in the DOM environment + +/** + * @jest-environment jsdom + */ + +import { EventEmitter } from '../../src/event_emitter'; + +describe('EventEmitter with DOM', () => { + let emitter: EventEmitter; + + beforeEach(() => { + emitter = new EventEmitter(); + }); + + describe('on', () => { + it('should add a listener for the specified event', () => { + const callback = jest.fn(); + emitter.on('test', callback); + emitter.emit('test', 'hello'); + expect(callback).toHaveBeenCalledWith('hello'); + }); + }); + + describe('once', () => { + it('should add a listener for the specified event that is only called once', () => { + const callback = jest.fn(); + emitter.once('test', callback); + emitter.emit('test', 'hello'); + emitter.emit('test', 'world'); + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith('hello'); + }); + }); + + describe('off', () => { + it('should remove a listener for the specified event', () => { + const callback = jest.fn(); + emitter.on('test', callback); + emitter.off('test', callback); + emitter.emit('test', 'hello'); + expect(callback).not.toHaveBeenCalled(); + }); + }); + + describe('emit', () => { + it('should call all listeners for the specified event', () => { + const callback1 = jest.fn(); + const callback2 = jest.fn(); + emitter.on('test', callback1); + emitter.on('test', callback2); + emitter.emit('test', 'hello'); + expect(callback1).toHaveBeenCalledWith('hello'); + expect(callback2).toHaveBeenCalledWith('hello'); + }); + }); + + describe('listenerCount', () => { + it('should return the number of listeners for the specified event', () => { + const callback1 = jest.fn(); + const callback2 = jest.fn(); + emitter.on('test', callback1); + emitter.on('test', callback2); + expect(emitter.listenerCount('test')).toBe(2); + }); + }); + + describe('listeners', () => { + it('should return an array of listeners for the specified event', () => { + const callback1 = jest.fn(); + const callback2 = jest.fn(); + emitter.on('test', callback1); + emitter.on('test', callback2); + expect(emitter.listeners('test')).toEqual([callback1, callback2]); + }); + }); + + describe('eventNames', () => { + it('should return an array of event names that have listeners', () => { + const callback1 = jest.fn(); + const callback2 = jest.fn(); + emitter.on('test1', callback1); + emitter.on('test2', callback2); + expect(emitter.eventNames()).toEqual(['test1', 'test2']); + }); + }); + + describe('removeAllListeners', () => { + it('should remove all listeners for all events', () => { + const callback1 = jest.fn(); + const callback2 = jest.fn(); + emitter.on('test1', callback1); + emitter.on('test2', callback2); + emitter.removeAllListeners(); + emitter.emit('test1', 'hello'); + emitter.emit('test2', 'world'); + expect(callback1).not.toHaveBeenCalled(); + expect(callback2).not.toHaveBeenCalled(); + }); + }); + + describe('setMaxListeners', () => { + it('should set the maximum number of listeners for an event', () => { + emitter.setMaxListeners(2); + expect(emitter.getMaxListeners()).toBe(2); + }); + }); +}); diff --git a/packages/web3-utils/test/unit/event_emitter.test.ts b/packages/web3-utils/test/unit/event_emitter_node.test.ts similarity index 96% rename from packages/web3-utils/test/unit/event_emitter.test.ts rename to packages/web3-utils/test/unit/event_emitter_node.test.ts index 21a1aec7dbf..a78fe8f63e1 100644 --- a/packages/web3-utils/test/unit/event_emitter.test.ts +++ b/packages/web3-utils/test/unit/event_emitter_node.test.ts @@ -15,11 +15,11 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -// this file will contain the unit test for the event emitter +// this file will contain the unit test for the event emitter in the Node environment import { EventEmitter } from '../../src/event_emitter'; -describe('EventEmitter', () => { +describe('EventEmitter with Node', () => { // let emitter = new EventEmitter(); let emitter: EventEmitter; From e4d7317b21eafb4b3d4c77cc240adc7c793603fb Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 4 Oct 2023 15:33:16 +0200 Subject: [PATCH 08/16] configure `EventEmitter` test to run inside `jsdom` --- packages/web3-utils/package.json | 1 + .../test/unit/event_emitter_dom.test.ts | 13 +- yarn.lock | 367 +++++++++++++++++- 3 files changed, 370 insertions(+), 11 deletions(-) diff --git a/packages/web3-utils/package.json b/packages/web3-utils/package.json index 89728c246ac..3958ddabe1c 100644 --- a/packages/web3-utils/package.json +++ b/packages/web3-utils/package.json @@ -52,6 +52,7 @@ "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", "jest": "^28.1.3", + "jest-environment-jsdom": "^29.7.0", "jest-extended": "^3.0.1", "js-sha3": "^0.8.0", "prettier": "^2.7.1", diff --git a/packages/web3-utils/test/unit/event_emitter_dom.test.ts b/packages/web3-utils/test/unit/event_emitter_dom.test.ts index 432bdcc54a8..b58ab6de1a8 100644 --- a/packages/web3-utils/test/unit/event_emitter_dom.test.ts +++ b/packages/web3-utils/test/unit/event_emitter_dom.test.ts @@ -1,3 +1,7 @@ +/** + * @jest-environment jsdom + */ + /* This file is part of web3.js. @@ -15,12 +19,11 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -// this file will contain the unit test for the event emitter in the DOM environment - -/** - * @jest-environment jsdom - */ +// 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 with DOM', () => { diff --git a/yarn.lock b/yarn.lock index e3012ba1959..bf95ec3254e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -712,6 +712,16 @@ "@types/node" "*" jest-mock "^28.1.3" +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== + dependencies: + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + "@jest/expect-utils@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" @@ -739,6 +749,18 @@ jest-mock "^28.1.3" jest-util "^28.1.3" +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== + dependencies: + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" + "@jest/globals@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.3.tgz#a601d78ddc5fdef542728309894895b4a42dc333" @@ -793,6 +815,13 @@ dependencies: "@sinclair/typebox" "^0.25.16" +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + "@jest/source-map@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" @@ -882,6 +911,18 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jridgewell/gen-mapping@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" @@ -1823,6 +1864,11 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -1830,6 +1876,20 @@ dependencies: type-detect "4.0.8" +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers@^9.1.2": version "9.1.2" resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" @@ -2079,6 +2139,15 @@ expect "^28.0.0" pretty-format "^28.0.0" +"@types/jsdom@^20.0.0": + version "20.0.1" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808" + integrity sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ== + dependencies: + "@types/node" "*" + "@types/tough-cookie" "*" + parse5 "^7.0.0" + "@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -2188,6 +2257,11 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/tough-cookie@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.3.tgz#3d06b6769518450871fbc40770b7586334bdfd90" + integrity sha512-THo502dA5PzG/sfQH+42Lw3fvmYkceefOspdCwpHRul8ik2Jv1K8I5OZz1AT3/rs46kwgMCe9bSBmDLYkkOMGg== + "@types/webpack@^5.28.0": version "5.28.0" resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-5.28.0.tgz#78dde06212f038d77e54116cfe69e88ae9ed2c03" @@ -2491,6 +2565,11 @@ JSONStream@^1.0.4: jsonparse "^1.2.0" through ">=2.2.7 <3" +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -2560,6 +2639,14 @@ accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +acorn-globals@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" + integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== + dependencies: + acorn "^8.1.0" + acorn-walk "^8.0.2" + acorn-import-assertions@^1.7.6: version "1.8.0" resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" @@ -2570,7 +2657,7 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^8.0.0, acorn-walk@^8.1.1: +acorn-walk@^8.0.0, acorn-walk@^8.0.2, acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== @@ -2580,6 +2667,11 @@ acorn@^8.0.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== +acorn@^8.1.0, acorn@^8.8.1: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" @@ -4054,6 +4146,23 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + cypress-jest-adapter@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/cypress-jest-adapter/-/cypress-jest-adapter-0.1.1.tgz#d1aa9d84393b6a5007022d1d33b3cdd3ce9672af" @@ -4076,6 +4185,15 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-urls@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" + integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + date-fns@^2.30.0: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" @@ -4127,6 +4245,11 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +decimal.js@^10.4.2: + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + declaration-bundler-webpack-plugin@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/declaration-bundler-webpack-plugin/-/declaration-bundler-webpack-plugin-1.0.3.tgz#9fb5ae393c63ed4175e7b6de4b58bafde34f097a" @@ -4337,6 +4460,13 @@ dom-walk@^0.1.0: resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + dot-prop@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" @@ -4471,6 +4601,11 @@ enquirer@^2.3.0, enquirer@~2.3.6: dependencies: ansi-colors "^4.1.1" +entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + env-paths@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" @@ -4580,6 +4715,17 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== +escodegen@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + eslint-config-airbnb-base@^15.0.0: version "15.0.0" resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz#6b09add90ac79c2f8d723a2580e07f3925afd236" @@ -4776,7 +4922,7 @@ espree@^9.3.2, espree@^9.3.3: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" -esprima@^4.0.0: +esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -6232,6 +6378,13 @@ hosted-git-info@^6.0.0, hosted-git-info@^6.1.1: dependencies: lru-cache "^7.5.1" +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== + dependencies: + whatwg-encoding "^2.0.0" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -6309,7 +6462,7 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== -https-proxy-agent@^5.0.0: +https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -6346,7 +6499,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.6.2: +iconv-lite@0.6.3, iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -6808,6 +6961,11 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -7121,6 +7279,20 @@ jest-each@^28.1.3: jest-util "^28.1.3" pretty-format "^28.1.3" +jest-environment-jsdom@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz#d206fa3551933c3fd519e5dfdb58a0f5139a837f" + integrity sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/jsdom" "^20.0.0" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + jsdom "^20.0.0" + jest-environment-node@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5" @@ -7240,6 +7412,21 @@ jest-message-util@^28.1.3: slash "^3.0.0" stack-utils "^2.0.3" +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.3.tgz#d4e9b1fc838bea595c77ab73672ebf513ab249da" @@ -7248,6 +7435,15 @@ jest-mock@^28.1.3: "@jest/types" "^28.1.3" "@types/node" "*" +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-util "^29.7.0" + jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" @@ -7382,6 +7578,18 @@ jest-util@^28.0.0, jest-util@^28.1.3: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" @@ -7481,6 +7689,38 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== +jsdom@^20.0.0: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" + integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== + dependencies: + abab "^2.0.6" + acorn "^8.8.1" + acorn-globals "^7.0.0" + cssom "^0.5.0" + cssstyle "^2.3.0" + data-urls "^3.0.2" + decimal.js "^10.4.2" + domexception "^4.0.0" + escodegen "^2.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.2" + parse5 "^7.1.1" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + ws "^8.11.0" + xml-name-validator "^4.0.0" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -8914,6 +9154,11 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== +nwsapi@^2.2.2: + version "2.2.7" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" + integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== + nx@15.9.4, "nx@>=15.5.2 < 16": version "15.9.4" resolved "https://registry.yarnpkg.com/nx/-/nx-15.9.4.tgz#1075bc33fe8ee6c6546c21ec6ffcfd2e000946c6" @@ -9358,6 +9603,13 @@ parse-url@^8.1.0: dependencies: parse-path "^7.0.0" +parse5@^7.0.0, parse5@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -9567,6 +9819,15 @@ pretty-format@^28.0.0, pretty-format@^28.1.3: ansi-styles "^5.0.0" react-is "^18.0.0" +pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + proc-log@^2.0.0, proc-log@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" @@ -9666,7 +9927,7 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== -psl@^1.1.28: +psl@^1.1.28, psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== @@ -9700,6 +9961,11 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2, queue-microtask@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -10182,6 +10448,13 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + schema-utils@^3.1.0, schema-utils@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" @@ -10556,7 +10829,7 @@ source-map@^0.5.6: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.6.0, source-map@^0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -10868,6 +11141,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + synckit@^0.8.5: version "0.8.5" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" @@ -11064,6 +11342,16 @@ totalist@^1.0.0: resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== +tough-cookie@^4.1.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -11072,6 +11360,13 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== + dependencies: + punycode "^2.1.1" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -11389,6 +11684,11 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" @@ -11437,6 +11737,14 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" @@ -11576,6 +11884,13 @@ vscode-textmate@5.2.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== + dependencies: + xml-name-validator "^4.0.0" + walk-up-path@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-1.0.0.tgz#d4745e893dd5fd0dbb58dd0a4c6a33d9c9fec53e" @@ -11636,6 +11951,11 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + webpack-bundle-analyzer@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz#33c1c485a7fcae8627c547b5c3328b46de733c66" @@ -11712,11 +12032,31 @@ webpack@^5, webpack@^5.73.0: watchpack "^2.4.0" webpack-sources "^3.2.3" +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== + dependencies: + iconv-lite "0.6.3" + whatwg-fetch@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" + integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -11878,6 +12218,11 @@ ws@^7.3.1, ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== +ws@^8.11.0: + version "8.14.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" + integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== + ws@^8.8.1: version "8.8.1" resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" @@ -11893,6 +12238,16 @@ xhr@^2.2.0: parse-headers "^2.0.0" xtend "^4.0.0" +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" From 1eed66dc2ff4449c1a117e2d904ed9d2532064ed Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Fri, 6 Oct 2023 09:27:01 +0200 Subject: [PATCH 09/16] fix issue with detecting Uint8Array (fix: `value "..." at "/0" must pass "bytes" validation`) --- packages/web3-eth-accounts/src/account.ts | 5 ++++- .../web3-eth-accounts/src/tx/transactionFactory.ts | 4 ++-- packages/web3-utils/src/converters.ts | 10 +++++++--- packages/web3-validator/src/validation/bytes.ts | 12 +++++++++--- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/web3-eth-accounts/src/account.ts b/packages/web3-eth-accounts/src/account.ts index 793f1f76bbc..d2a642cf9b5 100644 --- a/packages/web3-eth-accounts/src/account.ts +++ b/packages/web3-eth-accounts/src/account.ts @@ -82,7 +82,10 @@ export const parseAndValidatePrivateKey = (data: Bytes, ignoreLength?: boolean): } try { - privateKeyUint8Array = data instanceof Uint8Array ? data : bytesToUint8Array(data); + privateKeyUint8Array = + data instanceof Uint8Array || data.constructor.name === 'Uint8Array' + ? (data as Uint8Array) + : bytesToUint8Array(data); } catch { throw new InvalidPrivateKeyError(); } diff --git a/packages/web3-eth-accounts/src/tx/transactionFactory.ts b/packages/web3-eth-accounts/src/tx/transactionFactory.ts index dd74bee4eee..a17cf567536 100644 --- a/packages/web3-eth-accounts/src/tx/transactionFactory.ts +++ b/packages/web3-eth-accounts/src/tx/transactionFactory.ts @@ -104,8 +104,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 (data instanceof Uint8Array || data.constructor.name === 'Uint8Array') { + return this.fromSerializedData(data as Uint8Array, txOptions); } if (Array.isArray(data)) { // It is a legacy transaction diff --git a/packages/web3-utils/src/converters.ts b/packages/web3-utils/src/converters.ts index eed4e5caa1a..d75c35b8a4c 100644 --- a/packages/web3-utils/src/converters.ts +++ b/packages/web3-utils/src/converters.ts @@ -88,8 +88,8 @@ export type EtherUnits = keyof typeof ethUnitMap; export const bytesToUint8Array = (data: Bytes): Uint8Array | never => { validator.validate(['bytes'], [data]); - if (data instanceof Uint8Array) { - return data; + if (data instanceof Uint8Array || data.constructor.name === 'Uint8Array') { + return data as Uint8Array; } if (Array.isArray(data)) { @@ -585,7 +585,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(Uint8Array.from(utf8ToBytes(lowerCaseAddress))), + ); if ( isNullish(hash) || diff --git a/packages/web3-validator/src/validation/bytes.ts b/packages/web3-validator/src/validation/bytes.ts index ef15fbc0120..7947be68cbd 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 instanceof Uint8Array || data.constructor.name === 'Uint8Array'; export const isBytes = ( value: ValidInputTypes | Uint8Array | number[], @@ -30,7 +31,12 @@ export const isBytes = ( abiType: 'bytes', }, ) => { - if (typeof value !== 'string' && !Array.isArray(value) && !(value instanceof Uint8Array)) { + if ( + typeof value !== 'string' && + !Array.isArray(value) && + !(value instanceof Uint8Array) && + value.constructor.name !== 'Uint8Array' + ) { return false; } @@ -57,7 +63,7 @@ export const isBytes = ( } valueToCheck = new Uint8Array(value); } else { - valueToCheck = value; + valueToCheck = value as Uint8Array; } if (options?.abiType) { From 5f195bb01468c66c2ac7bad42dedd7e7afa64cd5 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Fri, 6 Oct 2023 10:41:43 +0200 Subject: [PATCH 10/16] prevent throwing if value is null when checking value.constructor.name --- packages/web3-eth-accounts/src/account.ts | 2 +- packages/web3-eth-accounts/src/tx/transactionFactory.ts | 2 +- packages/web3-utils/src/converters.ts | 2 +- packages/web3-validator/src/validation/bytes.ts | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/web3-eth-accounts/src/account.ts b/packages/web3-eth-accounts/src/account.ts index d2a642cf9b5..dcdbe3fc4ed 100644 --- a/packages/web3-eth-accounts/src/account.ts +++ b/packages/web3-eth-accounts/src/account.ts @@ -83,7 +83,7 @@ export const parseAndValidatePrivateKey = (data: Bytes, ignoreLength?: boolean): try { privateKeyUint8Array = - data instanceof Uint8Array || data.constructor.name === 'Uint8Array' + data instanceof Uint8Array || data?.constructor?.name === 'Uint8Array' ? (data as Uint8Array) : bytesToUint8Array(data); } catch { diff --git a/packages/web3-eth-accounts/src/tx/transactionFactory.ts b/packages/web3-eth-accounts/src/tx/transactionFactory.ts index a17cf567536..605b32ca1aa 100644 --- a/packages/web3-eth-accounts/src/tx/transactionFactory.ts +++ b/packages/web3-eth-accounts/src/tx/transactionFactory.ts @@ -104,7 +104,7 @@ export class TransactionFactory { * @param txOptions - The transaction options */ public static fromBlockBodyData(data: Uint8Array | Uint8Array[], txOptions: TxOptions = {}) { - if (data instanceof Uint8Array || data.constructor.name === 'Uint8Array') { + if (data instanceof Uint8Array || data?.constructor?.name === 'Uint8Array') { return this.fromSerializedData(data as Uint8Array, txOptions); } if (Array.isArray(data)) { diff --git a/packages/web3-utils/src/converters.ts b/packages/web3-utils/src/converters.ts index d75c35b8a4c..66e3047024d 100644 --- a/packages/web3-utils/src/converters.ts +++ b/packages/web3-utils/src/converters.ts @@ -88,7 +88,7 @@ export type EtherUnits = keyof typeof ethUnitMap; export const bytesToUint8Array = (data: Bytes): Uint8Array | never => { validator.validate(['bytes'], [data]); - if (data instanceof Uint8Array || data.constructor.name === 'Uint8Array') { + if (data instanceof Uint8Array || data?.constructor?.name === 'Uint8Array') { return data as Uint8Array; } diff --git a/packages/web3-validator/src/validation/bytes.ts b/packages/web3-validator/src/validation/bytes.ts index 7947be68cbd..5cc4bcf63d8 100644 --- a/packages/web3-validator/src/validation/bytes.ts +++ b/packages/web3-validator/src/validation/bytes.ts @@ -23,7 +23,7 @@ import { isHexStrict } from './string.js'; * checks input if typeof data is valid Uint8Array input */ export const isUint8Array = (data: ValidInputTypes) => - data instanceof Uint8Array || data.constructor.name === 'Uint8Array'; + data instanceof Uint8Array || data?.constructor?.name === 'Uint8Array'; export const isBytes = ( value: ValidInputTypes | Uint8Array | number[], @@ -35,7 +35,7 @@ export const isBytes = ( typeof value !== 'string' && !Array.isArray(value) && !(value instanceof Uint8Array) && - value.constructor.name !== 'Uint8Array' + value?.constructor?.name !== 'Uint8Array' ) { return false; } From 4b5d6cff211869015875ae04ba8d15ce5e0a1948 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Tue, 10 Oct 2023 00:46:50 +0200 Subject: [PATCH 11/16] improve type checking for Uint8Array --- packages/web3-eth-abi/src/utils.ts | 4 ++-- packages/web3-eth-accounts/src/account.ts | 16 +++++++--------- packages/web3-eth-accounts/src/common/utils.ts | 8 ++++++-- .../src/tx/transactionFactory.ts | 5 +++-- packages/web3-utils/src/converters.ts | 7 ++++--- packages/web3-utils/src/formatter.ts | 4 ++-- packages/web3-utils/src/uint8array.ts | 17 +++++++++++++++++ .../web3-validator/src/validation/address.ts | 5 +++-- packages/web3-validator/src/validation/bytes.ts | 11 +++-------- 9 files changed, 47 insertions(+), 30 deletions(-) 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/src/account.ts b/packages/web3-eth-accounts/src/account.ts index dcdbe3fc4ed..323162d1e14 100644 --- a/packages/web3-eth-accounts/src/account.ts +++ b/packages/web3-eth-accounts/src/account.ts @@ -49,6 +49,7 @@ import { bytesToHex, fromUtf8, hexToBytes, + isUint8Array, numberToHex, randomBytes, sha3Raw, @@ -82,10 +83,7 @@ export const parseAndValidatePrivateKey = (data: Bytes, ignoreLength?: boolean): } try { - privateKeyUint8Array = - data instanceof Uint8Array || data?.constructor?.name === 'Uint8Array' - ? (data as Uint8Array) - : bytesToUint8Array(data); + privateKeyUint8Array = isUint8Array(data) ? (data ) : bytesToUint8Array(data); } catch { throw new InvalidPrivateKeyError(); } @@ -344,7 +342,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; } @@ -359,7 +357,7 @@ export const recover = ( const address = toChecksumAddress(`0x${publicHash.slice(-40)}`); return address; -}; +};; /** * Get the ethereum Address from a private key @@ -393,7 +391,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 @@ -401,7 +399,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 @@ -495,7 +493,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 605b32ca1aa..16be0c7c9cd 100644 --- a/packages/web3-eth-accounts/src/tx/transactionFactory.ts +++ b/packages/web3-eth-accounts/src/tx/transactionFactory.ts @@ -14,6 +14,7 @@ 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 { isUint8Array } from 'web3-utils'; import { toUint8Array, uint8ArrayToBigInt } from '../common/utils.js'; import { FeeMarketEIP1559Transaction } from './eip1559Transaction.js'; import { AccessListEIP2930Transaction } from './eip2930Transaction.js'; @@ -104,8 +105,8 @@ export class TransactionFactory { * @param txOptions - The transaction options */ public static fromBlockBodyData(data: Uint8Array | Uint8Array[], txOptions: TxOptions = {}) { - if (data instanceof Uint8Array || data?.constructor?.name === 'Uint8Array') { - return this.fromSerializedData(data as Uint8Array, txOptions); + if (isUint8Array(data)) { + return this.fromSerializedData(data , txOptions); } if (Array.isArray(data)) { // It is a legacy transaction diff --git a/packages/web3-utils/src/converters.ts b/packages/web3-utils/src/converters.ts index 66e3047024d..649545df7dc 100644 --- a/packages/web3-utils/src/converters.ts +++ b/packages/web3-utils/src/converters.ts @@ -37,6 +37,7 @@ import { InvalidNumberError, InvalidUnitError, } from 'web3-errors'; +import { ensureIfUint8Array, isUint8Array } from './uint8array.js'; const base = BigInt(10); const expo10 = (expo: number) => base ** BigInt(expo); @@ -88,8 +89,8 @@ export type EtherUnits = keyof typeof ethUnitMap; export const bytesToUint8Array = (data: Bytes): Uint8Array | never => { validator.validate(['bytes'], [data]); - if (data instanceof Uint8Array || data?.constructor?.name === 'Uint8Array') { - return data as Uint8Array; + if (isUint8Array(data)) { + return data; } if (Array.isArray(data)) { @@ -588,7 +589,7 @@ export const toChecksumAddress = (address: Address): string => { // 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(Uint8Array.from(utf8ToBytes(lowerCaseAddress))), + keccak256(ensureIfUint8Array(utf8ToBytes(lowerCaseAddress))), ); if ( 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/uint8array.ts b/packages/web3-utils/src/uint8array.ts index 0f1173b04e7..772e648920c 100644 --- a/packages/web3-utils/src/uint8array.ts +++ b/packages/web3-utils/src/uint8array.ts @@ -14,6 +14,23 @@ 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 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; +} + export function uint8ArrayConcat(...parts: Uint8Array[]): Uint8Array { const length = parts.reduce((prev, part) => { const agg = prev + part.length; diff --git a/packages/web3-validator/src/validation/address.ts b/packages/web3-validator/src/validation/address.ts index f1c5b820a35..2fb61c5b9a3 100644 --- a/packages/web3-validator/src/validation/address.ts +++ b/packages/web3-validator/src/validation/address.ts @@ -20,6 +20,7 @@ import { utf8ToBytes } from 'ethereum-cryptography/utils.js'; import { ValidInputTypes } from '../types.js'; import { 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. @@ -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 5cc4bcf63d8..ab6ab2fbdd0 100644 --- a/packages/web3-validator/src/validation/bytes.ts +++ b/packages/web3-validator/src/validation/bytes.ts @@ -22,7 +22,7 @@ import { isHexStrict } from './string.js'; /** * checks input if typeof data is valid Uint8Array input */ -export const isUint8Array = (data: ValidInputTypes) => +export const isUint8Array = (data: ValidInputTypes): data is Uint8Array => data instanceof Uint8Array || data?.constructor?.name === 'Uint8Array'; export const isBytes = ( @@ -31,12 +31,7 @@ export const isBytes = ( abiType: 'bytes', }, ) => { - if ( - typeof value !== 'string' && - !Array.isArray(value) && - !(value instanceof Uint8Array) && - value?.constructor?.name !== 'Uint8Array' - ) { + if (typeof value !== 'string' && !Array.isArray(value) && !isUint8Array(value)) { return false; } @@ -63,7 +58,7 @@ export const isBytes = ( } valueToCheck = new Uint8Array(value); } else { - valueToCheck = value as Uint8Array; + valueToCheck = value ; } if (options?.abiType) { From c5a71542c174e20bee9f19f1e50bdb9ea4a6ad43 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Tue, 10 Oct 2023 01:00:12 +0200 Subject: [PATCH 12/16] remove a white space --- packages/web3-validator/src/validation/bytes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web3-validator/src/validation/bytes.ts b/packages/web3-validator/src/validation/bytes.ts index ab6ab2fbdd0..68c431c50ee 100644 --- a/packages/web3-validator/src/validation/bytes.ts +++ b/packages/web3-validator/src/validation/bytes.ts @@ -58,7 +58,7 @@ export const isBytes = ( } valueToCheck = new Uint8Array(value); } else { - valueToCheck = value ; + valueToCheck = value; } if (options?.abiType) { From 992d5bf467525bdd1c69b6acf3bf76ccea303871 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:46:42 +0100 Subject: [PATCH 13/16] remove a comment that was left be mistake --- packages/web3-utils/test/integration/event_emitter.test.ts | 5 ----- 1 file changed, 5 deletions(-) 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', () => { From 62c8c85cf6988f047005cce0e6dd447cf9e252fc Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 3 Jan 2024 15:34:43 +0100 Subject: [PATCH 14/16] add tests for Uint8Array in jsdom --- .../test/unit/account_dom.test.ts | 248 ++++++++++ packages/web3-utils/src/uint8array.ts | 2 + .../test/unit/converters_dom.test.ts | 439 ++++++++++++++++++ .../web3-utils/test/unit/hash_dom.test.ts | 166 +++++++ yarn.lock | 37 +- 5 files changed, 885 insertions(+), 7 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/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/src/uint8array.ts b/packages/web3-utils/src/uint8array.ts index 772e648920c..5fa8b952711 100644 --- a/packages/web3-utils/src/uint8array.ts +++ b/packages/web3-utils/src/uint8array.ts @@ -21,6 +21,8 @@ export function isUint8Array(data: unknown | Uint8Array): data is Uint8Array { (data as { constructor: { name: string } })?.constructor?.name === 'Uint8Array' ); } + +// @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) && 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/yarn.lock b/yarn.lock index 18cbb781cec..90bbf007848 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" From f865290adb6325bcdaf6a56bf26aeb07895b295d Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 3 Jan 2024 18:29:00 +0100 Subject: [PATCH 15/16] update CHANGELOG.md files --- CHANGELOG.md | 4 ++-- packages/web3-eth-abi/CHANGELOG.md | 4 ++++ packages/web3-eth-accounts/CHANGELOG.md | 1 + packages/web3-utils/CHANGELOG.md | 6 +++++- packages/web3-validator/CHANGELOG.md | 6 +++++- 5 files changed, 17 insertions(+), 4 deletions(-) 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-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-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-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) From 557132b5e65f98a9651047779d5a670ff5af5257 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 3 Jan 2024 23:55:26 +0100 Subject: [PATCH 16/16] move `ensureIfUint8Array` to `web3-validator` --- packages/web3-utils/src/converters.ts | 4 ++-- packages/web3-utils/src/hash.ts | 7 +++---- packages/web3-utils/src/uint8array.ts | 11 ----------- packages/web3-validator/src/utils.ts | 11 +++++++++++ packages/web3-validator/src/validation/address.ts | 4 ++-- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/packages/web3-utils/src/converters.ts b/packages/web3-utils/src/converters.ts index 52fb864e4fc..b84d5c28335 100644 --- a/packages/web3-utils/src/converters.ts +++ b/packages/web3-utils/src/converters.ts @@ -42,7 +42,7 @@ import { InvalidNumberError, InvalidUnitError, } from 'web3-errors'; -import { ensureIfUint8Array, isUint8Array } from './uint8array.js'; +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) @@ -596,7 +596,7 @@ export const toChecksumAddress = (address: Address): string => { // 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(ensureIfUint8Array(utf8ToBytes(lowerCaseAddress))), + keccak256(validatorUtils.ensureIfUint8Array(utf8ToBytes(lowerCaseAddress))), ); if ( 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 5fa8b952711..cf5d7ee7a45 100644 --- a/packages/web3-utils/src/uint8array.ts +++ b/packages/web3-utils/src/uint8array.ts @@ -22,17 +22,6 @@ export function isUint8Array(data: unknown | Uint8Array): data is Uint8Array { ); } -// @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; -} - export function uint8ArrayConcat(...parts: Uint8Array[]): Uint8Array { const length = parts.reduce((prev, part) => { const agg = prev + part.length; 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 2fb61c5b9a3..38ebebe65f6 100644 --- a/packages/web3-validator/src/validation/address.ts +++ b/packages/web3-validator/src/validation/address.ts @@ -18,7 +18,7 @@ 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'; @@ -30,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