From 601fefd17a99343fd2daee835ea845b89d259322 Mon Sep 17 00:00:00 2001 From: Julian Ariel Martinez Date: Thu, 9 Feb 2023 13:28:34 -0300 Subject: [PATCH 01/11] chore: changes for Beta version. --- .../src/utils/constants/initialState.ts | 4 +-- packages/ui/src/components/AppVersion.tsx | 12 ++++--- packages/ui/webpack/config/env.js | 1 + public/index.html | 2 +- public/manifest.json | 35 ++++++------------- 5 files changed, 23 insertions(+), 31 deletions(-) diff --git a/packages/background/src/utils/constants/initialState.ts b/packages/background/src/utils/constants/initialState.ts index 46b6959ce..0699bd4a7 100644 --- a/packages/background/src/utils/constants/initialState.ts +++ b/packages/background/src/utils/constants/initialState.ts @@ -141,7 +141,7 @@ const initialState: BlankAppState = { showDefaultWalletPreferences: false, localeInfo: 'en-US', nativeCurrency: 'usd', - showTestNetworks: false, + showTestNetworks: true, antiPhishingImage: '', popupTab: 'activity', settings: { @@ -180,7 +180,7 @@ const initialState: BlankAppState = { }, }, NetworkController: { - selectedNetwork: 'mainnet', + selectedNetwork: 'goerli', availableNetworks: INITIAL_NETWORKS, isNetworkChanging: false, isUserNetworkOnline: true, diff --git a/packages/ui/src/components/AppVersion.tsx b/packages/ui/src/components/AppVersion.tsx index 5b05f5f3f..163acd602 100644 --- a/packages/ui/src/components/AppVersion.tsx +++ b/packages/ui/src/components/AppVersion.tsx @@ -1,8 +1,12 @@ -const AppVersion = () => - process.env.VERSION ? ( - Version: v{process.env.VERSION} +const AppVersion = () => { + const { VERSION, VERSION_NAME } = process.env + + return VERSION ? ( + + Version: v{[VERSION, VERSION_NAME].filter(Boolean).join(" - ")} + ) : ( DEVELOPMENT ) - +} export default AppVersion diff --git a/packages/ui/webpack/config/env.js b/packages/ui/webpack/config/env.js index cd39c6bf1..99f7d9793 100644 --- a/packages/ui/webpack/config/env.js +++ b/packages/ui/webpack/config/env.js @@ -91,6 +91,7 @@ function getClientEnvironment(publicUrl) { // It is defined here so it is available in the webpackHotDevClient. FAST_REFRESH: process.env.FAST_REFRESH !== "false", VERSION: require("../../../../public/manifest.json").version, + VERSION_NAME: require("../../../../public/manifest.json").version_name, } ) // Stringify all values so we can feed into webpack DefinePlugin diff --git a/public/index.html b/public/index.html index 3573fd18f..2963edae6 100644 --- a/public/index.html +++ b/public/index.html @@ -7,7 +7,7 @@ - BlockWallet + [BETA] BlockWallet diff --git a/public/manifest.json b/public/manifest.json index 2333d38ca..9805c65fb 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,20 +1,12 @@ { "author": "BlockWallet", "background": { - "scripts": [ - "hot-reload.js", - "background.js" - ] + "scripts": ["hot-reload.js", "background.js"] }, "content_scripts": [ { - "js": [ - "content.js" - ], - "matches": [ - "http://*/*", - "https://*/*" - ], + "js": ["content.js"], + "matches": ["http://*/*", "https://*/*"], "exclude_matches": [ "https://block-wallet.github.io/eth-ledger-bridge-keyring/*", "https://connect.trezor.io/*" @@ -23,17 +15,13 @@ "all_frames": true }, { - "js": [ - "vendor/trezor/trezor-content.js" - ], - "matches": [ - "*://connect.trezor.io/*/popup.html" - ] + "js": ["vendor/trezor/trezor-content.js"], + "matches": ["*://connect.trezor.io/*/popup.html"] } ], "browser_action": { "default_popup": "popup.html", - "default_title": "BlockWallet" + "default_title": "[BETA] BlockWallet" }, "description": "The first crypto wallet protecting you on Web3 without any compromises.", "homepage_url": "https://www.blockwallet.io/", @@ -44,7 +32,7 @@ }, "content_security_policy": "script-src 'self'; object-src 'self'", "manifest_version": 2, - "name": "BlockWallet", + "name": "[BETA] BlockWallet", "permissions": [ "activeTab", "storage", @@ -54,9 +42,8 @@ "https://*.bscscan.com/*", "https://*.polygonscan.com/*" ], - "short_name": "BlockWallet", + "short_name": "[BETA] BlockWallet", "version": "0.5.6", - "web_accessible_resources": [ - "blankProvider.js" - ] -} \ No newline at end of file + "version_name": "beta", + "web_accessible_resources": ["blankProvider.js"] +} From 4bef6408272004913da02be1fd409f692328f71a Mon Sep 17 00:00:00 2001 From: Julian Ariel Martinez Date: Thu, 9 Feb 2023 17:22:28 -0300 Subject: [PATCH 02/11] chore: beta changes. --- .../ui/src/components/info/WelcomeInfo.tsx | 3 ++ public/index.html | 2 +- public/manifest.json | 36 +++++++++---------- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/ui/src/components/info/WelcomeInfo.tsx b/packages/ui/src/components/info/WelcomeInfo.tsx index ba070fac2..5fdde898e 100644 --- a/packages/ui/src/components/info/WelcomeInfo.tsx +++ b/packages/ui/src/components/info/WelcomeInfo.tsx @@ -28,6 +28,9 @@ const WelcomeInfo: FC = ({ onDismiss }) => { Welcome to BlockWallet!
+ + EXPERIMENTAL VERSION, USE AT YOUR OWN RISK + {settings.defaultBrowserWallet ? ( BlockWallet is your default browser wallet diff --git a/public/index.html b/public/index.html index 2963edae6..cc81dd8d2 100644 --- a/public/index.html +++ b/public/index.html @@ -7,7 +7,7 @@ - [BETA] BlockWallet + [Experimental] BlockWallet diff --git a/public/manifest.json b/public/manifest.json index 9805c65fb..b72238d75 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,7 +1,7 @@ { "author": "BlockWallet", "background": { - "scripts": ["hot-reload.js", "background.js"] + "service_worker": "background.js" }, "content_scripts": [ { @@ -19,31 +19,31 @@ "matches": ["*://connect.trezor.io/*/popup.html"] } ], - "browser_action": { + "action": { "default_popup": "popup.html", - "default_title": "[BETA] BlockWallet" + "default_title": "[Experimental] BlockWallet" }, - "description": "The first crypto wallet protecting you on Web3 without any compromises.", + "description": "The most private, non-custodial cryptocurrency wallet", "homepage_url": "https://www.blockwallet.io/", "icons": { "16": "icons/icon-16.png", "48": "icons/icon-48.png", "128": "icons/icon-128.png" }, - "content_security_policy": "script-src 'self'; object-src 'self'", - "manifest_version": 2, - "name": "[BETA] BlockWallet", - "permissions": [ - "activeTab", - "storage", - "notifications", - "https://*.blockwallet.io/*", - "https://*.etherscan.io/*", - "https://*.bscscan.com/*", - "https://*.polygonscan.com/*" - ], - "short_name": "[BETA] BlockWallet", + "content_security_policy": { + "extension_pages": "script-src 'self'; object-src 'self'" + }, + "manifest_version": 3, + "name": "[Experimental] BlockWallet", + "permissions": ["activeTab", "storage", "notifications", "alarms"], + "host_permissions": ["https://*.blockwallet.io/*"], + "short_name": "BlockWallet", "version": "0.5.6", "version_name": "beta", - "web_accessible_resources": ["blankProvider.js"] + "web_accessible_resources": [ + { + "resources": ["blankProvider.js"], + "matches": ["https://*/*"] + } + ] } From 7715687b13376b5dbf488d19cddda9779ba44374 Mon Sep 17 00:00:00 2001 From: Julian Ariel Martinez Date: Tue, 21 Feb 2023 14:51:10 -0300 Subject: [PATCH 03/11] chore: changed manifestv3 properties. --- public/manifest.json | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/public/manifest.json b/public/manifest.json index 557ab9a0c..0adf56f1b 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -35,15 +35,21 @@ }, "manifest_version": 3, "name": "[Experimental] BlockWallet", - "permissions": ["activeTab", "storage", "notifications", "alarms"], + "permissions": [ + "activeTab", + "storage", + "notifications", + "alarms", + "scripting" + ], "host_permissions": ["https://*.blockwallet.io/*"], "short_name": "BlockWallet", "version": "0.5.7", "version_name": "beta", "web_accessible_resources": [ { - "resources": ["blankProvider.js"], - "matches": ["https://*/*"] + "resources": ["blankProvider.js", "keep-alive"], + "matches": [""] } ] } From 6708ab64185a5ed77b25131d70a5bbab878a46b4 Mon Sep 17 00:00:00 2001 From: Facundo Pezzola Date: Tue, 21 Feb 2023 17:24:51 -0300 Subject: [PATCH 04/11] fix: provider injection for mv3 and silent context invalidated err --- packages/background/src/index.ts | 19 +++++++++ packages/provider/src/content.ts | 71 ++++++++++++++++++++------------ public/manifest.json | 2 +- 3 files changed, 64 insertions(+), 28 deletions(-) diff --git a/packages/background/src/index.ts b/packages/background/src/index.ts index c48d3fc51..b653dc74a 100644 --- a/packages/background/src/index.ts +++ b/packages/background/src/index.ts @@ -191,6 +191,24 @@ chrome.runtime.onInstalled.addListener(({ reason }) => { } }); +const registerInPageContentScript = async () => { + try { + await (chrome.scripting as any).registerContentScripts([ + { + id: 'blankProvider', + matches: ['file://*/*', 'http://*/*', 'https://*/*'], + js: ['blankProvider.js'], + runAt: 'document_start', + world: 'MAIN', + }, + ]); + } catch (err) { + console.warn( + `Dropped attempt to register blankProvider content script. ${err}` + ); + } +}; + if (isManifestV3()) { // this keeps alive the service worker. // when it goes 'inactive' it is restarted. @@ -198,4 +216,5 @@ if (isManifestV3()) { chrome.alarms.onAlarm.addListener(() => { fetch(chrome.runtime.getURL('keep-alive')); }); + registerInPageContentScript(); } diff --git a/packages/provider/src/content.ts b/packages/provider/src/content.ts index 0cca13fdd..4fa839423 100644 --- a/packages/provider/src/content.ts +++ b/packages/provider/src/content.ts @@ -14,22 +14,27 @@ import { checkScriptLoad } from './utils/site'; import blankProvider from '../../../dist/blankProvider.js?raw'; import { isManifestV3 } from '@block-wallet/background/utils/manifest'; +const EXTENSION_CONTEXT_INVALIDATED_CHROMIUM_ERROR = + 'Extension context invalidated.'; + let providerOverridden = false; function injectProvider() { - const injectableScript = blankProvider; - const injectableScriptSourceMapURL = `//# sourceURL=${chrome.runtime.getURL( - 'blankProvider.js' - )}\n`; - const BUNDLE = injectableScript + injectableScriptSourceMapURL; - - const container = document.head || document.documentElement; - const script = document.createElement('script'); - script.type = 'text/javascript'; - script.textContent = BUNDLE; - script.setAttribute('async', 'false'); - container.insertBefore(script, container.children[0]); - container.removeChild(script); + if (!isManifestV3()) { + const injectableScript = blankProvider; + const injectableScriptSourceMapURL = `//# sourceURL=${chrome.runtime.getURL( + 'blankProvider.js' + )}\n`; + const BUNDLE = injectableScript + injectableScriptSourceMapURL; + + const container = document.head || document.documentElement; + const script = document.createElement('script'); + script.type = 'text/javascript'; + script.textContent = BUNDLE; + script.setAttribute('async', 'false'); + container.insertBefore(script, container.children[0]); + container.removeChild(script); + } } window.addEventListener('ethereum#initialized', (e: Event) => { @@ -43,26 +48,38 @@ window.addEventListener('ethereum#initialized', (e: Event) => { injectProvider(); -const SW_KEEP_ALIVE_INTERVAL = 10; +const SW_KEEP_ALIVE_INTERVAL = 1000; let SW_ALIVE = false; let portReinitialized = false; let intervalRef: NodeJS.Timer; if (isManifestV3()) { intervalRef = setInterval(() => { - chrome.runtime.sendMessage({ message: CONTENT.SW_KEEP_ALIVE }, () => { - if (chrome.runtime.lastError) { - log.info( - 'Error keeping alive:', - chrome.runtime.lastError.message || chrome.runtime.lastError - ); - const err = chrome.runtime.lastError.message || ''; - SW_ALIVE = !err.includes('Receiving end does not exist'); - portReinitialized = SW_ALIVE; - } else { - SW_ALIVE = true; - } - }); + try { + chrome.runtime.sendMessage( + { message: CONTENT.SW_KEEP_ALIVE }, + () => { + if (chrome.runtime.lastError) { + log.info( + 'Error keeping alive:', + chrome.runtime.lastError.message || + chrome.runtime.lastError + ); + const err = chrome.runtime.lastError.message || ''; + SW_ALIVE = !err.includes( + 'Receiving end does not exist' + ); + portReinitialized = SW_ALIVE; + } else { + SW_ALIVE = true; + } + } + ); + } catch (e) { + e.message === EXTENSION_CONTEXT_INVALIDATED_CHROMIUM_ERROR + ? log.error(`Please refresh the page. BlockWallet: ${e}`) + : log.error(`BlockWallet: ${e}`); + } }, SW_KEEP_ALIVE_INTERVAL); } else { SW_ALIVE = true; diff --git a/public/manifest.json b/public/manifest.json index 0adf56f1b..c6db52e1d 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -42,7 +42,7 @@ "alarms", "scripting" ], - "host_permissions": ["https://*.blockwallet.io/*"], + "host_permissions": ["file://*/*", "http://*/*", "https://*/*"], "short_name": "BlockWallet", "version": "0.5.7", "version_name": "beta", From d4066fc75486fc1a2775a0d3199518ea05b51f77 Mon Sep 17 00:00:00 2001 From: Facundo Pezzola Date: Wed, 22 Feb 2023 11:52:37 -0300 Subject: [PATCH 05/11] fix: Remove Experimental prefix --- packages/background/src/index.ts | 4 ++-- packages/background/src/utils/constants/initialState.ts | 4 ++-- packages/ui/src/components/info/WelcomeInfo.tsx | 3 --- public/index.html | 2 +- public/manifest.json | 4 ++-- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/background/src/index.ts b/packages/background/src/index.ts index b653dc74a..4adf8d2ce 100644 --- a/packages/background/src/index.ts +++ b/packages/background/src/index.ts @@ -191,7 +191,7 @@ chrome.runtime.onInstalled.addListener(({ reason }) => { } }); -const registerInPageContentScript = async () => { +const registerBlankProviderContentScript = async () => { try { await (chrome.scripting as any).registerContentScripts([ { @@ -216,5 +216,5 @@ if (isManifestV3()) { chrome.alarms.onAlarm.addListener(() => { fetch(chrome.runtime.getURL('keep-alive')); }); - registerInPageContentScript(); + registerBlankProviderContentScript(); } diff --git a/packages/background/src/utils/constants/initialState.ts b/packages/background/src/utils/constants/initialState.ts index 2d4705087..24fc2af0a 100644 --- a/packages/background/src/utils/constants/initialState.ts +++ b/packages/background/src/utils/constants/initialState.ts @@ -145,7 +145,7 @@ const initialState: BlankAppState = { showDefaultWalletPreferences: false, localeInfo: 'en-US', nativeCurrency: 'usd', - showTestNetworks: true, + showTestNetworks: false, antiPhishingImage: '', popupTab: 'activity', settings: { @@ -184,7 +184,7 @@ const initialState: BlankAppState = { }, }, NetworkController: { - selectedNetwork: 'goerli', + selectedNetwork: 'mainnet', availableNetworks: INITIAL_NETWORKS, isNetworkChanging: false, isUserNetworkOnline: true, diff --git a/packages/ui/src/components/info/WelcomeInfo.tsx b/packages/ui/src/components/info/WelcomeInfo.tsx index 5fdde898e..ba070fac2 100644 --- a/packages/ui/src/components/info/WelcomeInfo.tsx +++ b/packages/ui/src/components/info/WelcomeInfo.tsx @@ -28,9 +28,6 @@ const WelcomeInfo: FC = ({ onDismiss }) => { Welcome to BlockWallet!
- - EXPERIMENTAL VERSION, USE AT YOUR OWN RISK - {settings.defaultBrowserWallet ? ( BlockWallet is your default browser wallet diff --git a/public/index.html b/public/index.html index cc81dd8d2..3573fd18f 100644 --- a/public/index.html +++ b/public/index.html @@ -7,7 +7,7 @@ - [Experimental] BlockWallet + BlockWallet diff --git a/public/manifest.json b/public/manifest.json index 84df2f492..747c02363 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -21,7 +21,7 @@ ], "action": { "default_popup": "popup.html", - "default_title": "[Experimental] BlockWallet" + "default_title": "BlockWallet" }, "description": "The most private, non-custodial cryptocurrency wallet", "homepage_url": "https://www.blockwallet.io/", @@ -34,7 +34,7 @@ "extension_pages": "script-src 'self'; object-src 'self'" }, "manifest_version": 3, - "name": "[Experimental] BlockWallet", + "name": "BlockWallet", "permissions": [ "activeTab", "storage", From a3e9b9273e3ae6c50fd662ffd9965b5ff03dd7a3 Mon Sep 17 00:00:00 2001 From: Facundo Pezzola Date: Wed, 22 Feb 2023 11:53:59 -0300 Subject: [PATCH 06/11] fix: remove version name --- public/manifest.json | 1 - 1 file changed, 1 deletion(-) diff --git a/public/manifest.json b/public/manifest.json index 747c02363..5627d3593 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -45,7 +45,6 @@ "host_permissions": ["file://*/*", "http://*/*", "https://*/*"], "short_name": "BlockWallet", "version": "0.6.0", - "version_name": "beta", "web_accessible_resources": [ { "resources": ["blankProvider.js", "keep-alive"], From 6c1896c8fc120dcad8e6081932aee9d7ddd3ee57 Mon Sep 17 00:00:00 2001 From: Facundo Pezzola <22504074+fpezzola@users.noreply.github.com> Date: Fri, 24 Feb 2023 15:50:41 -0300 Subject: [PATCH 07/11] Feat/mv3 update keyring (#363) * feat: update keyring controller and match new API * fix: renamte and encapsulate method * lint: turn-off ts for chrome.storage directive --- packages/background/package.json | 4 +- .../src/controllers/AppStateController.ts | 101 +++++++++--- .../controllers/KeyringControllerDerivated.ts | 33 +++- .../src/controllers/OnboardingController.ts | 2 +- .../src/typings/browser-passworder.d.ts | 2 +- .../src/typings/eth-keyring-controller.d.ts | 69 ++++++-- .../src/utils/constants/initialState.ts | 4 +- .../KeyringControllerDerivated.test.ts | 25 ++- .../stores/migrator/reconciler.test.ts | 4 + packages/background/yarn.lock | 154 +++++++----------- packages/provider/src/content.ts | 35 +++- 11 files changed, 268 insertions(+), 165 deletions(-) diff --git a/packages/background/package.json b/packages/background/package.json index 18206845e..f28fd65f0 100644 --- a/packages/background/package.json +++ b/packages/background/package.json @@ -8,15 +8,15 @@ "@block-wallet/explorer-link": "https://github.com/block-wallet/explorer-link#v2.2.2", "@block-wallet/remote-configs": "https://github.com/block-wallet/remote-configs#v1.1.0", "@ethereumjs/tx": "^3.5.2", + "@metamask/browser-passworder": "https://github.com/block-wallet/browser-passworder#v1.0.1", + "@metamask/eth-keyring-controller": "^10.0.0", "@metamask/eth-sig-util": "^5.0.2", "@unstoppabledomains/resolution": "^8.3.3", "async-mutex": "^0.3.2", "bip39": "^3.0.3", - "browser-passworder": "^2.0.3", "compare-versions": "^3.6.0", "eslint-webpack-plugin": "^3.2.0", "eth-ens-namehash": "^2.0.8", - "eth-keyring-controller": "^6.2.0", "eth-trezor-keyring": "https://github.com/block-wallet/eth-trezor-keyring#v0.10.2", "ethereumjs-util": "^7.0.7", "ethereumjs-wallet": "^1.0.1", diff --git a/packages/background/src/controllers/AppStateController.ts b/packages/background/src/controllers/AppStateController.ts index b393b4fec..aa4b79f9e 100644 --- a/packages/background/src/controllers/AppStateController.ts +++ b/packages/background/src/controllers/AppStateController.ts @@ -17,6 +17,11 @@ export enum AppStateEvents { APP_UNLOCKED = 'APP_UNLOCKED', } +export type SessionToken = { + encryptionKey: string; + encryptionSalt: string; +}; + export default class AppStateController extends BaseController { private _timer: ReturnType | null; @@ -117,19 +122,15 @@ export default class AppStateController extends BaseController => { try { // Unlock vault - const loginToken = await this._keyringController.submitPassword( + const sessionToken = await this._keyringController.submitPassword( password ); if (isManifestV3()) { - // @ts-ignore - chrome.storage.session && - // @ts-ignore - chrome.storage.session - .set({ loginToken }) - .catch((err: any) => { - log.error('error setting loginToken', err); - }); + this._storeSessionToken({ + encryptionKey: sessionToken.encryptionKey, + encryptionSalt: sessionToken.encryptionSalt, + }); } await this._postLoginAction(); @@ -138,24 +139,76 @@ export default class AppStateController extends BaseController { + return new Promise((resolve) => { + // @ts-ignore + if (chrome.storage.session) { + // @ts-ignore + chrome.storage.session.get( + ['sessionToken'], + async ({ sessionToken }: { [key: string]: any }) => { + if (!sessionToken) { + resolve(undefined); + } + resolve(sessionToken as SessionToken); + } + ); + } else { + resolve(undefined); + } + }); + } + + private async _storeSessionToken( + sessionToken: SessionToken + ): Promise { + // @ts-ignore + if (chrome.storage.session) { + // @ts-ignore + await chrome.storage.session + .set({ + sessionToken: { + encryptionKey: sessionToken.encryptionKey, + encryptionSalt: sessionToken.encryptionSalt, + }, + }) + .catch((err: any) => { + log.error('error setting sessionToken', err); + }); + } + + return sessionToken; + } + public autoUnlock = async (): Promise => { if (isManifestV3()) { const { isAppUnlocked } = this.store.getState(); - if (!isAppUnlocked) { - // @ts-ignore - chrome.storage.session && - // @ts-ignore - chrome.storage.session.get( - ['loginToken'], - async ({ loginToken }: { [key: string]: string }) => { - if (loginToken) { - await (this._keyringController as any)[ - 'submitEncryptionKey' - ](loginToken); - await this._postLoginAction(); - } - } - ); + if (isAppUnlocked) { + let forceLock = true; + try { + const sessionToken = await this._getSessionToken(); + if (sessionToken) { + const newSessionToken = + await this._keyringController.submitEncryptionKey( + sessionToken.encryptionKey, + sessionToken.encryptionSalt + ); + await this._storeSessionToken({ + encryptionKey: newSessionToken.encryptionKey, + encryptionSalt: newSessionToken.encryptionSalt, + }); + await this._postLoginAction(); + forceLock = false; + } + } catch (e) { + log.error('Unable to autoUnlock keyring', e); + } finally { + // if we were unable to unlock keyring we should lock the wallet + // the user needs to unlock the keyring and the wallet by his own + if (forceLock) { + this.lock(); + } + } } } }; diff --git a/packages/background/src/controllers/KeyringControllerDerivated.ts b/packages/background/src/controllers/KeyringControllerDerivated.ts index 938c09399..a0e22cd84 100644 --- a/packages/background/src/controllers/KeyringControllerDerivated.ts +++ b/packages/background/src/controllers/KeyringControllerDerivated.ts @@ -1,7 +1,10 @@ -import KeyringController, { +import { + KeyringController, + keyringBuilderFactory, KeyringControllerProps, KeyringControllerState, -} from 'eth-keyring-controller'; +} from '@metamask/eth-keyring-controller'; +import * as customEncryptor from '@metamask/browser-passworder'; import { Hash, Hasheable } from '../utils/hasher'; import { Mutex } from 'async-mutex'; import LedgerBridgeKeyring from '@block-wallet/eth-ledger-bridge-keyring'; @@ -10,6 +13,9 @@ import { Devices } from '../utils/types/hardware'; import log from 'loglevel'; import { HDPaths, BIP44_PATH } from '../utils/types/hardware'; import { TypedTransaction } from '@ethereumjs/tx'; +import { isManifestV3 } from '../utils/manifest'; +import { bufferToHex } from 'ethereumjs-util'; +import { hexToString } from '../utils/signature'; /** * Available keyring types */ @@ -24,7 +30,12 @@ export default class KeyringControllerDerivated extends KeyringController { private readonly _mutex: Mutex; constructor(opts: KeyringControllerProps) { - opts.keyringTypes = [LedgerBridgeKeyring, TrezorKeyring]; + opts.keyringBuilders = [ + keyringBuilderFactory(LedgerBridgeKeyring), + keyringBuilderFactory(TrezorKeyring), + ]; + opts.cacheEncryptionKey = isManifestV3(); + opts.encryptor = customEncryptor; super(opts); this._mutex = new Mutex(); @@ -155,8 +166,7 @@ export default class KeyringControllerDerivated extends KeyringController { KeyringTypes.HD_KEY_TREE )[0]; const serialized = await primaryKeyring.serialize(); - const seedPhrase = serialized.mnemonic; - + const seedPhrase = hexToString(bufferToHex(serialized.mnemonic)); return seedPhrase; } @@ -252,15 +262,20 @@ export default class KeyringControllerDerivated extends KeyringController { // Generate a new keyring const keyringController = new KeyringController({}); - const Keyring = keyringController.getKeyringClassForType( - KeyringTypes.HD_KEY_TREE - ); + const opts = { mnemonic: seedPhrase, numberOfAccounts: createdAccounts.length, }; - const keyring = new Keyring(opts); + const keyring = await keyringController._newKeyring( + KeyringTypes.HD_KEY_TREE, + opts + ); + if (!keyring) { + throw new Error('Unable to generate keyring of type HD_KEY_TREE'); + } + const restoredAccounts = await keyring.getAccounts(); if (restoredAccounts.length !== createdAccounts.length) { diff --git a/packages/background/src/controllers/OnboardingController.ts b/packages/background/src/controllers/OnboardingController.ts index 822a72371..84701da84 100644 --- a/packages/background/src/controllers/OnboardingController.ts +++ b/packages/background/src/controllers/OnboardingController.ts @@ -1,4 +1,4 @@ -import { KeyringControllerState } from 'eth-keyring-controller'; +import { KeyringControllerState } from '@metamask/eth-keyring-controller'; import { BaseController } from '../infrastructure/BaseController'; import KeyringControllerDerivated from './KeyringControllerDerivated'; diff --git a/packages/background/src/typings/browser-passworder.d.ts b/packages/background/src/typings/browser-passworder.d.ts index 4abc354ce..0e105873f 100644 --- a/packages/background/src/typings/browser-passworder.d.ts +++ b/packages/background/src/typings/browser-passworder.d.ts @@ -1,4 +1,4 @@ -declare module 'browser-passworder' { +declare module '@metamask/browser-passworder' { declare function encrypt(password: string, dataObj: T): Promise; declare function decrypt( password: string, diff --git a/packages/background/src/typings/eth-keyring-controller.d.ts b/packages/background/src/typings/eth-keyring-controller.d.ts index 0ae5fbee7..e92a30e78 100644 --- a/packages/background/src/typings/eth-keyring-controller.d.ts +++ b/packages/background/src/typings/eth-keyring-controller.d.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/ban-types */ -declare module 'eth-keyring-controller' { + +declare module '@metamask/eth-keyring-controller' { import { TypedTransaction } from '@ethereumjs/tx'; import { IObservableStore } from '../infrastructure/stores/ObservableStore'; import { EventEmitter } from 'events'; @@ -10,6 +11,8 @@ declare module 'eth-keyring-controller' { keyringTypes: any; keyrings: any[]; vault: string; + encryptionKey: string; + encryptionSalt: string; } export interface KeyringControllerMemState { @@ -18,15 +21,27 @@ declare module 'eth-keyring-controller' { keyrings: any[]; } + export type Keyring = any; + + export type KeyringBuilder = { + (): Keyring; + type: KeyringTypes; + }; + + export type KeyringBuilderFactoryType = ( + keyring: Keyring + ) => KeyringBuilder; + + export const keyringBuilderFactory: KeyringBuilderFactoryType; + export interface KeyringControllerProps { initState?: Partial; encryptor?: any; - keyringTypes?: any; + cacheEncryptionKey?: boolean; + keyringBuilders?: KeyringBuilderFactory[]; } - export type Keyring = any; - - export default class KeyringController extends EventEmitter { + export class KeyringController extends EventEmitter { memStore: IObservableStore; store: IObservableStore; @@ -125,6 +140,22 @@ declare module 'eth-keyring-controller' { */ submitPassword(password: string): Promise; + /** + * Submit Encryption Key. + * + * Attempts to decrypt the current vault and load its keyrings + * into memory based on the vault and CryptoKey information. + * + * @fires KeyringController#unlock + * @param {string} encryptionKey - The encrypted key information used to decrypt the vault. + * @param {string} encryptionSalt - The salt used to generate the last key. + * @returns {Promise} A Promise that resolves to the state. + */ + submitEncryptionKey( + encryptionKey: string, + encryptionSalt: string + ): Promise; + /** * Verify Password * @@ -205,19 +236,6 @@ declare module 'eth-keyring-controller' { */ removeAccount(address: string | string[]): Promise; - /** - * Get Keyring Class For Type - * - * Searches the current `keyringTypes` array - * for a Keyring class whose unique `type` property - * matches the provided `type`, - * returning it if it exists. - * - * @param {string} type - The type whose class to get. - * @returns {Keyring|undefined} The class, if it exists. - */ - getKeyringClassForType(type: string): Keyring | undefined; - /** * Sign Ethereum Transaction * @@ -386,6 +404,19 @@ declare module 'eth-keyring-controller' { */ restoreKeyring(serialized: any): Promise; + /** + * Get Keyring Class For Type + * + * Searches the current `keyringBuilders` array + * for a Keyring builder whose unique `type` property + * matches the provided `type`, + * returning it if it exists. + * + * @param {string} type - The type whose class to get. + * @returns {Keyring|undefined} The class, if it exists. + */ + getKeyringBuilderForType(type: string): KeyringBuilder | undefined; + /** * Display For Keyring * @@ -402,5 +433,7 @@ declare module 'eth-keyring-controller' { * Used before initializing a new vault. */ clearKeyrings(): Promise; + + _newKeyring(type: string, data: any): Promise; } } diff --git a/packages/background/src/utils/constants/initialState.ts b/packages/background/src/utils/constants/initialState.ts index 24fc2af0a..72fbe9df7 100644 --- a/packages/background/src/utils/constants/initialState.ts +++ b/packages/background/src/utils/constants/initialState.ts @@ -1,7 +1,7 @@ import { KeyringControllerState, KeyringControllerMemState, -} from 'eth-keyring-controller'; +} from '@metamask/eth-keyring-controller'; import { ValuesOf } from '../types/helpers'; import { IObservableStore } from '../../infrastructure/stores/ObservableStore'; @@ -134,6 +134,8 @@ const initialState: BlankAppState = { keyringTypes: [], keyrings: [], vault: '', + encryptionKey: '', + encryptionSalt: '', }, OnboardingController: { isOnboarded: false, diff --git a/packages/background/test/controllers/KeyringControllerDerivated.test.ts b/packages/background/test/controllers/KeyringControllerDerivated.test.ts index 8ca6a8801..57dcc36a2 100644 --- a/packages/background/test/controllers/KeyringControllerDerivated.test.ts +++ b/packages/background/test/controllers/KeyringControllerDerivated.test.ts @@ -3,7 +3,10 @@ import { expect } from 'chai'; import KeyringControllerDerivated, { KeyringTypes, } from '@block-wallet/background/controllers/KeyringControllerDerivated'; -import KeyringController from 'eth-keyring-controller'; +import { + KeyringBuilder, + KeyringController, +} from '@metamask/eth-keyring-controller'; import mockEncryptor from 'test/mocks/mock-encryptor'; describe('KeyringControllerDerivated', () => { @@ -92,14 +95,19 @@ describe('KeyringControllerDerivated', () => { }); }; } + const mockKeyTreeBuilder: KeyringBuilder = () => mockKeyTree; + mockKeyTreeBuilder.type = KeyringTypes.HD_KEY_TREE; + + const keyTreeBuilder: KeyringBuilder = () => keyTree; + keyTreeBuilder.type = KeyringTypes.HD_KEY_TREE; sinon - .stub(KeyringController.prototype, 'getKeyringsByType') - .returns([keyTree]); + .stub(KeyringController.prototype, 'getKeyringBuilderForType') + .returns(keyTreeBuilder); sinon - .stub(KeyringController.prototype, 'getKeyringClassForType') - .returns(mockKeyTree); + .stub(KeyringController.prototype, 'getKeyringBuilderForType') + .returns(mockKeyTreeBuilder); try { await (keyringControllerDerivated as any)['verifyAccounts'](); @@ -136,13 +144,16 @@ describe('KeyringControllerDerivated', () => { }; } + const mockKeyTreeBuilder: KeyringBuilder = () => mockKeyTree; + mockKeyTreeBuilder.type = KeyringTypes.HD_KEY_TREE; + sinon .stub(KeyringController.prototype, 'getKeyringsByType') .returns([keyTree]); sinon - .stub(KeyringController.prototype, 'getKeyringClassForType') - .returns(mockKeyTree); + .stub(KeyringController.prototype, 'getKeyringBuilderForType') + .returns(mockKeyTreeBuilder); try { await (keyringControllerDerivated as any)['verifyAccounts'](); diff --git a/packages/background/test/infrastructure/stores/migrator/reconciler.test.ts b/packages/background/test/infrastructure/stores/migrator/reconciler.test.ts index 0028de578..de7e332be 100644 --- a/packages/background/test/infrastructure/stores/migrator/reconciler.test.ts +++ b/packages/background/test/infrastructure/stores/migrator/reconciler.test.ts @@ -199,6 +199,8 @@ const initialState: newBlankAppState = { keyringTypes: {}, keyrings: [], vault: '', + encryptionKey: '', + encryptionSalt: '', }, NetworkController: { selectedNetwork: 'mainnet', @@ -425,6 +427,8 @@ describe('State reconciler', () => { keyringTypes: {}, keyrings: [], vault: 'encrypted-vault', + encryptionKey: '', + encryptionSalt: '', }, OnboardingController: { isOnboarded: true, diff --git a/packages/background/yarn.lock b/packages/background/yarn.lock index 8b4a9cd2a..883e67807 100644 --- a/packages/background/yarn.lock +++ b/packages/background/yarn.lock @@ -274,6 +274,15 @@ async "^3.2.4" ethereum-cryptography "^1.1.2" +"@ethereumjs/util@^8.0.2": + version "8.0.3" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.0.3.tgz#410c2dc8c6d519b29f1a471aa9b9df9952e41239" + integrity sha512-0apCbwc8xAaie6W7q6QyogfyRS2BMU816a8KwpnpRw9Qrc6Bws+l7J3LfCLMt2iL6Wi8CYb0B29AeIr2N4vHnw== + dependencies: + "@ethereumjs/rlp" "^4.0.0-beta.2" + async "^3.2.4" + ethereum-cryptography "^1.1.2" + "@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.1", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" @@ -707,6 +716,43 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@metamask/browser-passworder@^4.0.2", "@metamask/browser-passworder@https://github.com/block-wallet/browser-passworder", "@metamask/browser-passworder@https://github.com/block-wallet/browser-passworder#v1.0.1": + version "4.0.2" + resolved "https://github.com/block-wallet/browser-passworder#f8fa5a8a9dfdba84afc8b67c481e6c9374e99d13" + +"@metamask/eth-hd-keyring@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-hd-keyring/-/eth-hd-keyring-6.0.0.tgz#a46788d4bbc7aa5d8263ed6e4348101a80519a69" + integrity sha512-dEj/I6Ag9FyCmjPcRXeXCkRXkVJE/uElhDVRcLBU6mT/GsXKgzVWXC/k0dhE8rEDrQbidhl+8wEElSJ2LI1InA== + dependencies: + "@ethereumjs/util" "^8.0.2" + "@metamask/eth-sig-util" "^5.0.2" + "@metamask/scure-bip39" "^2.0.3" + ethereum-cryptography "^1.1.2" + +"@metamask/eth-keyring-controller@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-keyring-controller/-/eth-keyring-controller-10.0.0.tgz#71e8fd84a02fae70e7afe75f09c4551114cad742" + integrity sha512-Ypox0BunIQO6x0E3KUzhw125zoWKwkpZl9xw4rnVNbZOHrsD+/uXGTAZRSPLdoKbq0DYdkQAvcds8FeoJEWEYw== + dependencies: + "@metamask/browser-passworder" "^4.0.2" + "@metamask/eth-hd-keyring" "^6.0.0" + "@metamask/eth-sig-util" "5.0.2" + "@metamask/eth-simple-keyring" "^5.0.0" + obs-store "^4.0.3" + +"@metamask/eth-sig-util@5.0.2", "@metamask/eth-sig-util@^5.0.1", "@metamask/eth-sig-util@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-5.0.2.tgz#c518279a6e17a88135a13d53a0b970f145ff8bce" + integrity sha512-RU6fG/H6/UlBol221uBkq5C7w3TwLK611nEZliO2u+kO0vHKGBXnIPlhI0tzKUigjhUeOd9mhCNbNvhh0LKt9Q== + dependencies: + "@ethereumjs/util" "^8.0.0" + bn.js "^4.11.8" + ethereum-cryptography "^1.1.2" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + "@metamask/eth-sig-util@^4.0.0": version "4.0.1" resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" @@ -718,17 +764,23 @@ tweetnacl "^1.0.3" tweetnacl-util "^0.15.1" -"@metamask/eth-sig-util@^5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-5.0.2.tgz#c518279a6e17a88135a13d53a0b970f145ff8bce" - integrity sha512-RU6fG/H6/UlBol221uBkq5C7w3TwLK611nEZliO2u+kO0vHKGBXnIPlhI0tzKUigjhUeOd9mhCNbNvhh0LKt9Q== +"@metamask/eth-simple-keyring@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-simple-keyring/-/eth-simple-keyring-5.0.0.tgz#307772d1aa3298e41a2444b428cd8f4522b7bf5c" + integrity sha512-UJfP36Z9g1eeD8mSHWaVqUvkgbgYm3S7YuzlMzQi+WgPnWu81CdbldMMtvreTlu4I1mTyljXLDMjIp65P0bygQ== dependencies: "@ethereumjs/util" "^8.0.0" - bn.js "^4.11.8" + "@metamask/eth-sig-util" "^5.0.1" ethereum-cryptography "^1.1.2" - ethjs-util "^0.1.6" - tweetnacl "^1.0.3" - tweetnacl-util "^0.15.1" + randombytes "^2.1.0" + +"@metamask/scure-bip39@^2.0.3": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@metamask/scure-bip39/-/scure-bip39-2.1.0.tgz#13456884736e56ede15e471bd93c0aa0acdedd0b" + integrity sha512-Ndwdnld0SI6YaftEUUVq20sdoWcWNXsJXxvQkbiY42FKmrA16U6WoSh9Eq+NpugpKKwK6f5uvaTDusjndiEDGQ== + dependencies: + "@noble/hashes" "~1.1.1" + "@scure/base" "~1.1.0" "@noble/hashes@1.1.2": version "1.1.2" @@ -1680,17 +1732,6 @@ bindings@^1.3.0, bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -bip39@^2.2.0, bip39@^2.4.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.6.0.tgz#9e3a720b42ec8b3fbe4038f1e445317b6a99321c" - integrity sha512-RrnQRG2EgEoqO24ea+Q/fftuPUZLmrEM3qNhhGsA3PbaXaCW791LTzPuVyx/VprXQcTbPJ3K3UeTna8ZnVl2sg== - dependencies: - create-hash "^1.1.0" - pbkdf2 "^3.0.9" - randombytes "^2.0.1" - safe-buffer "^5.0.1" - unorm "^1.3.3" - bip39@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.4.tgz#5b11fed966840b5e1b8539f0f54ab6392969b2a0" @@ -1727,11 +1768,6 @@ blakejs@^1.1.0: resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== -bluebird@^3.5.0: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" @@ -1774,13 +1810,6 @@ brorand@^1.0.1, brorand@^1.0.5, brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== -browser-passworder@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/browser-passworder/-/browser-passworder-2.0.3.tgz#6fdd2082e516a176edbcb3dcee0b7f9fce4f7917" - integrity sha512-8mTcGjsVqYkRW0qLmdussBjf/5joBUpvZfR8jUojITBJVVZIS5BL41Qt/xehS+n2ChA2YJHHLPhlkXziK+gvsw== - dependencies: - browserify-unibabel "^3.0.0" - browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -1840,11 +1869,6 @@ browserify-sign@^4.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" -browserify-unibabel@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/browserify-unibabel/-/browserify-unibabel-3.0.0.tgz#5a6b8f0f704ce388d3927df47337e25830f71dda" - integrity sha512-j3MX0k2dC1/DEo9jSUyj7zpv5wLd1+klpFwYlM0E5mr7MX6LblQOWb+jkBEKV2iRszmhJuLHAtNuqranlGnpNQ== - browserslist@^4.14.5, browserslist@^4.21.3: version "4.21.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" @@ -2690,32 +2714,6 @@ eth-ens-namehash@^2.0.8: idna-uts46-hx "^2.3.1" js-sha3 "^0.5.7" -eth-hd-keyring@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/eth-hd-keyring/-/eth-hd-keyring-3.6.0.tgz#6835d30aa411b8d3ef098e82f6427b5325082abb" - integrity sha512-n2CwE9VNXsxLrXQa6suv0Umt4NT6+HtoahKgWx3YviXx4rQFwVT5nDwZfjhwrT31ESuoXYNIeJgz5hKLD96QeQ== - dependencies: - bip39 "^2.2.0" - eth-sig-util "^3.0.1" - eth-simple-keyring "^4.2.0" - ethereumjs-util "^7.0.9" - ethereumjs-wallet "^1.0.1" - -eth-keyring-controller@^6.2.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/eth-keyring-controller/-/eth-keyring-controller-6.2.1.tgz#61901071fc74059ed37cb5ae93870fdcae6e3781" - integrity sha512-x2gTM1iHp2Kbvdtd9Eslysw0qzVZiqOzpVB3AU/ni2Xiit+rlcv2H80zYKjrEwlfWFDj4YILD3bOqlnEMmRJOA== - dependencies: - bip39 "^2.4.0" - bluebird "^3.5.0" - browser-passworder "^2.0.3" - eth-hd-keyring "^3.6.0" - eth-sig-util "^3.0.1" - eth-simple-keyring "^4.2.0" - ethereumjs-util "^7.0.9" - loglevel "^1.5.0" - obs-store "^4.0.3" - eth-rpc-errors@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/eth-rpc-errors/-/eth-rpc-errors-4.0.3.tgz#6ddb6190a4bf360afda82790bb7d9d5e724f423a" @@ -2733,26 +2731,6 @@ eth-sig-util@^2.0.0: tweetnacl "^1.0.3" tweetnacl-util "^0.15.0" -eth-sig-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-3.0.1.tgz#8753297c83a3f58346bd13547b59c4b2cd110c96" - integrity sha512-0Us50HiGGvZgjtWTyAI/+qTzYPMLy5Q451D0Xy68bxq1QMWdoOddDwGvsqcFT27uohKgalM9z/yxplyt+mY2iQ== - dependencies: - ethereumjs-abi "^0.6.8" - ethereumjs-util "^5.1.1" - tweetnacl "^1.0.3" - tweetnacl-util "^0.15.0" - -eth-simple-keyring@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/eth-simple-keyring/-/eth-simple-keyring-4.2.0.tgz#c197a4bd4cce7d701b5f3607d0b843112ddb17e3" - integrity sha512-lBxFObXJTBjktDkQszXrqoB317wghEUYATQ3W19vLAjaznrmbdy1lccPhXIRMT9bHIUgNKOJQkLohNZiT9tO8Q== - dependencies: - eth-sig-util "^3.0.1" - ethereumjs-util "^7.0.9" - ethereumjs-wallet "^1.0.1" - events "^1.1.1" - "eth-trezor-keyring@https://github.com/block-wallet/eth-trezor-keyring#v0.10.2": version "0.10.0" resolved "https://github.com/block-wallet/eth-trezor-keyring#ac701bf5c20590e0ba882d4748e578601de74a70" @@ -2897,11 +2875,6 @@ ethjs-util@0.1.6, ethjs-util@^0.1.3, ethjs-util@^0.1.6: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" -events@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" - integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== - events@^3.0.0, events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -3983,7 +3956,7 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -loglevel@^1.5.0, loglevel@^1.7.1: +loglevel@^1.7.1: version "1.8.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== @@ -5631,11 +5604,6 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -unorm@^1.3.3: - version "1.6.0" - resolved "https://registry.yarnpkg.com/unorm/-/unorm-1.6.0.tgz#029b289661fba714f1a9af439eb51d9b16c205af" - integrity sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA== - update-browserslist-db@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" diff --git a/packages/provider/src/content.ts b/packages/provider/src/content.ts index 4fa839423..bad4aa40e 100644 --- a/packages/provider/src/content.ts +++ b/packages/provider/src/content.ts @@ -50,11 +50,12 @@ injectProvider(); const SW_KEEP_ALIVE_INTERVAL = 1000; let SW_ALIVE = false; +let EXTENSION_CONTEXT_VALID = true; let portReinitialized = false; -let intervalRef: NodeJS.Timer; +let timeoutRef: NodeJS.Timer; -if (isManifestV3()) { - intervalRef = setInterval(() => { +function swKeepAlive() { + return new Promise((resolve) => { try { chrome.runtime.sendMessage( { message: CONTENT.SW_KEEP_ALIVE }, @@ -73,14 +74,30 @@ if (isManifestV3()) { } else { SW_ALIVE = true; } + resolve(); } ); } catch (e) { - e.message === EXTENSION_CONTEXT_INVALIDATED_CHROMIUM_ERROR - ? log.error(`Please refresh the page. BlockWallet: ${e}`) - : log.error(`BlockWallet: ${e}`); + let message = `BlockWallet: ${e}`; + if (e.message === EXTENSION_CONTEXT_INVALIDATED_CHROMIUM_ERROR) { + EXTENSION_CONTEXT_VALID = false; + message = `BlockWallet: Please refresh the page. ${e}`; + } + log.error(message); + resolve(); } - }, SW_KEEP_ALIVE_INTERVAL); + }); +} + +async function keepExtensionAlive() { + await swKeepAlive(); + if (EXTENSION_CONTEXT_VALID) { + timeoutRef = setTimeout(keepExtensionAlive, SW_KEEP_ALIVE_INTERVAL); + } +} + +if (isManifestV3()) { + keepExtensionAlive(); } else { SW_ALIVE = true; } @@ -104,8 +121,8 @@ chrome.runtime.sendMessage( //If provider has been overridden by another wallet, then remove connection. providerOverridden ) { - if (isManifestV3() && intervalRef) { - clearInterval(intervalRef); + if (isManifestV3() && timeoutRef) { + clearTimeout(timeoutRef); } port.disconnect(); window.removeEventListener('message', windowListener); From 157107fdfea5c4e0175a64a40838c1d558370310 Mon Sep 17 00:00:00 2001 From: Rodrigo <10286502+rodr-r@users.noreply.github.com> Date: Wed, 9 Oct 2024 01:21:21 -0300 Subject: [PATCH 08/11] yarn --- packages/background/yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/background/yarn.lock b/packages/background/yarn.lock index 883e67807..fa844d4cb 100644 --- a/packages/background/yarn.lock +++ b/packages/background/yarn.lock @@ -716,7 +716,7 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@metamask/browser-passworder@^4.0.2", "@metamask/browser-passworder@https://github.com/block-wallet/browser-passworder", "@metamask/browser-passworder@https://github.com/block-wallet/browser-passworder#v1.0.1": +"@metamask/browser-passworder@^4.0.2", "@metamask/browser-passworder@https://github.com/block-wallet/browser-passworder#v1.0.1": version "4.0.2" resolved "https://github.com/block-wallet/browser-passworder#f8fa5a8a9dfdba84afc8b67c481e6c9374e99d13" From ce34b32ce21ff98b139a5a4cc2835aca63554e93 Mon Sep 17 00:00:00 2001 From: Rodrigo <10286502+rodr-r@users.noreply.github.com> Date: Thu, 10 Oct 2024 12:38:15 -0300 Subject: [PATCH 09/11] master to mv3 (does not work) --- .github/workflows/build.yml | 195 +- .github/workflows/ci.yml | 7 +- .github/workflows/release.yml | 4 +- .gitignore | 1 + .nvmrc | 2 +- LICENSE | 6 +- Makefile | 43 +- README.md | 2 +- docs/acknowledgments.md | 72 +- docs/convention.md | 12 +- docs/guideline.md | 2 +- docs/workflow.md | 4 +- public/manifest.json => manifest/base.json | 13 +- manifest/chrome.json | 21 + manifest/firefox.json | 37 + package.json | 5 +- packages/background/.nvmrc | 2 +- packages/background/LICENSE | 2 +- packages/background/Makefile | 17 +- packages/background/contributing.md | 2 +- packages/background/package.json | 100 +- .../controllers/AccountTrackerController.ts | 155 +- .../src/controllers/AddressBookController.ts | 19 +- .../src/controllers/AppStateController.ts | 18 +- .../src/controllers/BlankController.ts | 388 +- .../controllers/BlankProviderController.ts | 74 +- .../src/controllers/BridgeController.ts | 6 +- .../src/controllers/CampaignsController.ts | 70 + .../controllers/ExchangeRatesController.ts | 32 +- .../src/controllers/GasPricesController.ts | 337 +- .../controllers/KeyringControllerDerivated.ts | 539 +- .../src/controllers/NetworkController.ts | 483 +- .../src/controllers/NotificationController.ts | 446 + .../src/controllers/OnrampController.ts | 142 + .../src/controllers/PermissionsController.ts | 2 +- .../src/controllers/PreferencesController.ts | 59 +- .../src/controllers/SwapController.ts | 301 +- .../TransactionWatcherController.ts | 45 +- .../src/controllers/UDController.ts | 4 +- .../block-updates/BlockFetchController.ts | 10 +- .../src/controllers/erc-20/Token.ts | 3 +- .../src/controllers/erc-20/TokenController.ts | 15 +- .../src/controllers/erc-20/TokenList.ts | 42 +- .../erc-20/transactions/ApproveTransaction.ts | 2 +- .../transactions/ContractSignatureParser.ts | 17 +- .../transactions/TransactionController.ts | 202 +- .../controllers/transactions/utils/types.ts | 14 + .../controllers/transactions/utils/utils.ts | 4 +- packages/background/src/index.ts | 41 +- .../src/infrastructure/connection.ts | 9 +- .../hardware/trezor/trezor-content.ts | 5 +- .../hardware/trezor/trezor-usb-permissions.ts | 33 +- .../infrastructure/stores/BaseStorageStore.ts | 27 +- .../stores/migrator/migrations/index.ts | 29 + .../migrator/migrations/migration-23.ts | 2 +- .../migrator/migrations/migration-24.ts | 2 +- .../migrator/migrations/migration-48.ts | 33 - .../migrator/migrations/migration-52.ts | 5 +- .../migrator/migrations/migration-53.ts | 10 - .../migrator/migrations/migration-55.ts | 2 +- .../migrator/migrations/migration-56.ts | 5 - .../migrator/migrations/migration-58.ts | 5 - .../migrator/migrations/migration-61.ts | 24 + .../migrator/migrations/migration-62.ts | 81 + .../migrator/migrations/migration-63.ts | 43 + .../migrator/migrations/migration-64.ts | 80 + .../migrator/migrations/migration-65.ts | 27 + .../migrator/migrations/migration-66.ts | 110 + .../migrator/migrations/migration-67.ts | 62 + .../migrator/migrations/migration-68.ts | 23 + .../migrator/migrations/migration-69.ts | 26 + .../migrator/migrations/migration-70.ts | 22 + .../migrator/migrations/migration-71.ts | 60 + .../migrator/migrations/migration-72.ts | 102 + .../migrator/migrations/migration-73.ts | 66 + .../migrator/migrations/migration-74.ts | 29 + .../stores/migrator/migrator.ts | 14 +- .../background/src/typings/worker-loader.d.ts | 7 - packages/background/src/utils/account.ts | 10 +- .../utils/balance-checker/balanceChecker.ts | 2 +- packages/background/src/utils/bnUtils.ts | 2 +- packages/background/src/utils/bridgeApi.ts | 78 +- .../src/utils/checksummedAddress.ts | 2 +- .../src/utils/constants/currencies.json | 454 +- .../src/utils/constants/initialState.ts | 22 +- .../src/utils/constants/networks.ts | 299 +- .../background/src/utils/contractsInfo.ts | 2 +- packages/background/src/utils/currency.ts | 1 + packages/background/src/utils/env.ts | 7 + .../background/src/utils/ethereumChain.ts | 37 +- packages/background/src/utils/hasher.ts | 14 +- packages/background/src/utils/http.ts | 92 +- packages/background/src/utils/manifest.ts | 9 +- .../background/src/utils/notifications.ts | 217 - packages/background/src/utils/onrampApi.ts | 92 + packages/background/src/utils/popup.ts | 10 +- packages/background/src/utils/rateService.ts | 93 +- packages/background/src/utils/releaseNotes.ts | 4 +- .../src/utils/scanner/EtherscanFetcher.ts | 13 +- packages/background/src/utils/signature.ts | 2 +- .../src/utils/{types => swaps}/1inch.ts | 156 +- .../src/utils/{ => swaps}/1inchError.ts | 2 +- .../background/src/utils/swaps/openOcean.ts | 273 + packages/background/src/utils/token.ts | 29 +- .../src/utils/types/communication.ts | 147 +- .../background/src/utils/types/ethereum.ts | 10 + .../background/src/utils/types/hardware.ts | 6 + packages/background/src/utils/types/lifi.ts | 34 +- .../background/src/utils/userPreferences.ts | 4 +- packages/background/src/utils/window.ts | 111 +- .../AccountTrackerController.test.ts | 44 +- .../controllers/AddressBookController.test.ts | 3 +- .../controllers/AppStateController.test.ts | 15 +- .../BlankProviderController.test.ts | 12 +- .../test/controllers/BridgeController.test.ts | 115 +- .../test/controllers/EnsController.test.ts | 4 +- .../controllers/GasPricesController.test.ts | 17 +- .../KeyringControllerDerivated.test.ts | 6 +- .../controllers/NetworkController.test.ts | 63 +- .../controllers/PermissionsController.test.ts | 8 +- .../test/controllers/SwapController.test.ts | 151 +- .../BlockFetchController.test.ts | 30 +- .../erc-20/TokenController.test.ts | 2 +- .../transactions/ApproveTransaction.test.ts | 5 +- .../erc-20/transactions/Transaction.test.ts | 4 +- .../transactions/TransferTransaction.test.ts | 4 +- .../ContractSignatureParser.test.ts | 4 +- .../TransactionController.test.ts | 51 +- .../stores/migrator/migrator.test.ts | 5 +- .../stores/migrator/reconciler.test.ts | 43 +- .../test/mocks/mock-exchangerates.ts | 4 +- .../test/mocks/mock-network-instance.ts | 7 +- .../background/test/mocks/mock-preferences.ts | 9 +- packages/background/test/mocks/sinonChrome.js | 1 + .../test/utils/userPreferences.test.ts | 14 + packages/background/webpack/webpack.shared.js | 15 + packages/background/yarn.lock | 4017 +++-- packages/provider/.nvmrc | 2 +- packages/provider/LICENSE | 2 +- packages/provider/Makefile | 7 + packages/provider/package.json | 48 +- packages/provider/src/content.ts | 77 +- packages/provider/src/index.ts | 21 +- .../provider/src/provider/BlankProvider.ts | 41 +- packages/provider/src/types.ts | 18 + packages/provider/yarn.lock | 2487 ++- packages/ui/.eslintrc | 4 - packages/ui/.eslintrc.json | 26 + packages/ui/.nvmrc | 2 +- packages/ui/LICENSE | 2 +- packages/ui/Makefile | 27 +- packages/ui/package.json | 134 +- packages/ui/src/App.tsx | 2 +- packages/ui/src/assets/fonts/Inter-Medium.ttf | Bin 0 -> 314712 bytes .../ui/src/assets/fonts/Inter-SemiBold.ttf | Bin 0 -> 315756 bytes packages/ui/src/assets/images/icons/about.svg | 13 +- .../src/assets/images/icons/account_add.svg | 12 +- .../ui/src/assets/images/icons/accounts.svg | 13 +- .../assets/images/icons/accounts_order.svg | 14 + .../src/assets/images/icons/assets_order.svg | 17 + packages/ui/src/assets/images/icons/bell.svg | 16 +- .../images/icons/bluecircle_skeleton.json | 136 +- packages/ui/src/assets/images/icons/book.svg | 17 +- .../ui/src/assets/images/icons/bridge.json | 6 +- packages/ui/src/assets/images/icons/cash.svg | 3 + .../assets/images/icons/connected_sites.svg | 11 +- packages/ui/src/assets/images/icons/cross.svg | 8 +- .../ui/src/assets/images/icons/export.svg | 13 +- .../ui/src/assets/images/icons/eye_close.svg | 4 +- .../ui/src/assets/images/icons/eye_open.svg | 2 +- .../ui/src/assets/images/icons/filter.svg | 3 + packages/ui/src/assets/images/icons/gas.svg | 10 +- .../ui/src/assets/images/icons/hotkeys.svg | 14 + .../src/assets/images/icons/import_seed.svg | 13 + packages/ui/src/assets/images/icons/key.svg | 11 + .../ui/src/assets/images/icons/keystone.svg | 5 + .../ui/src/assets/images/icons/logout.svg | 2 +- .../src/assets/images/icons/new_account.svg | 13 + packages/ui/src/assets/images/icons/news.svg | 13 +- .../ui/src/assets/images/icons/no_camera.svg | 1 + .../ui/src/assets/images/icons/onramper.svg | 3 + .../src/assets/images/icons/open_external.svg | 13 +- packages/ui/src/assets/images/icons/order.svg | 7 + packages/ui/src/assets/images/icons/pin.svg | 16 +- packages/ui/src/assets/images/icons/plane.svg | 4 +- .../ui/src/assets/images/icons/refresh.svg | 2 +- packages/ui/src/assets/images/icons/safe.svg | 18 + .../ui/src/assets/images/icons/search.svg | 2 +- .../assets/images/icons/searchnotfound.svg | 6 + .../ui/src/assets/images/icons/shield.svg | 5 +- .../ui/src/assets/images/icons/spanner.svg | 16 +- .../ui/src/assets/images/icons/switch.svg | 4 + packages/ui/src/assets/images/icons/usb.svg | 12 +- .../ui/src/assets/images/icons/wallet.json | 1 + .../ui/src/assets/images/icons/wallet.svg | 11 + packages/ui/src/assets/images/icons/world.svg | 14 +- .../src/assets/images/icons/your_backup.svg | 4 + packages/ui/src/assets/images/keystone.png | Bin 0 -> 4483 bytes packages/ui/src/assets/images/logo.svg | 138 +- .../ui/src/components/ActivityAssetsView.tsx | 56 - packages/ui/src/components/ActivityList.tsx | 5 +- packages/ui/src/components/AnimatedIcon.tsx | 11 +- packages/ui/src/components/AppVersion.tsx | 14 +- packages/ui/src/components/AssetsList.tsx | 187 - .../ui/src/components/CancelSpeedUpCommon.tsx | 48 +- .../ui/src/components/CollapsableMessage.tsx | 86 + .../ui/src/components/CollapsableWarning.tsx | 55 - packages/ui/src/components/FeeDetails.tsx | 6 +- .../ui/src/components/FullCenterContainer.tsx | 5 +- packages/ui/src/components/InfoComponent.tsx | 2 +- packages/ui/src/components/LogoHeader.tsx | 15 +- packages/ui/src/components/PageLayout.tsx | 3 +- .../src/components/account/AccountDisplay.tsx | 78 +- .../components/account/AccountDisplayMenu.tsx | 23 +- .../src/components/account/AccountFilters.tsx | 10 +- .../ui/src/components/account/AccountMenu.tsx | 48 +- .../account/AccountMultipleSelect.tsx | 4 +- .../components/account/AccountSearchBar.tsx | 14 +- .../account/AccountSearchResults.tsx | 78 +- .../src/components/account/AccountSelect.tsx | 96 +- .../account/AccountsDisplayDragDrop.tsx | 211 + .../src/components/account/AccountsList.tsx | 2 +- .../components/addressBook/AddressDisplay.tsx | 71 +- .../components/allowances/AllowanceItem.tsx | 28 +- .../components/allowances/AllowanceList.tsx | 4 +- .../allowances/AllowancesFilterButton.tsx | 6 +- .../allowances/AllowancesRefetchButton.tsx | 2 +- .../assets/ActivityAllowancesView.tsx | 6 +- .../src/components/assets/AssetActivity.tsx | 16 +- .../src/components/assets/AssetAllowances.tsx | 2 +- .../components/assets/AssetAmountDisplay.tsx | 2 +- .../components/assets/AssetDetailsPage.tsx | 25 +- .../assets/AssetDropdownDisplay.tsx | 6 +- .../ui/src/components/assets/AssetList.tsx | 2 +- .../src/components/assets/AssetSelection.tsx | 12 +- .../ui/src/components/assets/AssetsButton.tsx | 41 + .../ui/src/components/assets/AssetsSort.tsx | 87 + .../src/components/bridge/BridgeDetails.tsx | 9 +- .../components/bridge/BridgeDetailsFees.tsx | 4 +- .../components/bridge/BridgeDetailsStatus.tsx | 8 +- .../bridge/BridgeDetailsSummary.tsx | 5 +- .../components/bridge/BridgeNotFoundItem.tsx | 6 +- .../BridgeQuoteNotFoundErrorDetails.tsx | 6 +- .../src/components/bridge/ExpandableItem.tsx | 4 +- packages/ui/src/components/bridge/FeeItem.tsx | 7 +- .../bridge/FeeTokenSummaryDisplay.tsx | 2 +- .../ui/src/components/button/ActionButton.tsx | 7 +- .../src/components/button/ButtonWithIcon.tsx | 4 +- .../ui/src/components/button/OrderButton.tsx | 22 + .../components/button/RoundedIconButton.tsx | 4 +- .../ui/src/components/button/ToggleButton.tsx | 6 +- .../button/ViewOnExplorerButtons.tsx | 4 +- .../ui/src/components/chain/ChainDisplay.tsx | 8 +- .../components/chain/ChainFiltersButton.tsx | 4 +- .../components/chain/NetworkDisplayBadge.tsx | 69 +- .../src/components/chain/ProviderStatus.tsx | 244 + .../chain/RPCValidationEndLabelInfo.tsx | 22 +- .../components/currency/CurrencyDisplay.tsx | 69 + .../currency/CurrencyDropdownDisplay.tsx | 51 + .../src/components/currency/CurrencyList.tsx | 56 + .../components/currency/CurrencySelection.tsx | 134 + .../ui/src/components/dApp/DAppOrigin.tsx | 13 +- .../src/components/dApp/DAppPopupHeader.tsx | 7 +- .../src/components/dialog/CheckboxDialog.tsx | 26 +- .../src/components/dialog/ConfirmDialog.tsx | 27 +- .../src/components/dialog/DetailsDialog.tsx | 9 +- .../ui/src/components/dialog/ErrorDialog.tsx | 20 +- .../dialog/HardwareDeviceNotLinkedDialog.tsx | 4 +- .../src/components/dialog/HotkeysDialog.tsx | 85 + .../src/components/dialog/IsLockedDialog.tsx | 9 +- .../src/components/dialog/MessageDialog.tsx | 26 +- .../components/dialog/ProviderDownDialog.tsx | 79 - .../src/components/dialog/SuccessDialog.tsx | 32 +- .../WaitingAllowanceTransactionDialog.tsx | 2 +- .../src/components/dialog/WaitingDialog.tsx | 4 + .../components/error/ErrorFallbackPage.tsx | 16 +- .../ui/src/components/gas/GasPricesInfo.tsx | 46 +- .../hardwareWallet/AdvancedSettings.tsx | 138 + .../hardwareWallet/HardwareWalletAccount.tsx | 130 + .../components/home/ActivityAssetsView.tsx | 92 + .../ui/src/components/home/AssetsList.tsx | 290 + .../src/components/hotkeys/DisplayHotkey.tsx | 164 + .../hotkeys/HotkeysCollapsedMessage.tsx | 73 + packages/ui/src/components/icons/AppIcon.tsx | 44 +- .../ui/src/components/icons/ArrowUpDown.tsx | 2 +- .../components/icons/CenteredTextCircle.tsx | 2 +- packages/ui/src/components/icons/EditIcon.tsx | 20 + .../src/components/icons/EmptyDrawerIcon.tsx | 41 +- .../icons/ExclamationCircleIconFull.tsx | 4 +- .../ui/src/components/icons/EyeCloseIcon.tsx | 26 + .../ui/src/components/icons/EyeOpenIcon.tsx | 22 + .../ui/src/components/icons/FilterIcon.tsx | 25 + .../ui/src/components/icons/ImportIcon.tsx | 24 +- .../src/components/icons/OpenExplorerIcon.tsx | 3 +- .../ui/src/components/icons/OrderIcon.tsx | 53 + .../ui/src/components/icons/SearchIcon.tsx | 19 +- .../ui/src/components/icons/SwitchIcon.tsx | 10 +- .../ui/src/components/icons/ThreeDotsIcon.tsx | 33 +- .../ui/src/components/icons/TrashBinIcon.tsx | 9 +- packages/ui/src/components/icons/UsbIcon.tsx | 63 +- .../ui/src/components/icons/WalletIcon.tsx | 65 +- packages/ui/src/components/info/Info.tsx | 4 +- .../src/components/info/ReleaseNotesInfo.tsx | 4 +- .../ui/src/components/info/WelcomeInfo.tsx | 5 +- packages/ui/src/components/input/Checkbox.tsx | 2 +- .../src/components/input/DropDownSelector.tsx | 19 +- packages/ui/src/components/input/EndLabel.tsx | 6 +- .../src/components/input/HorizontalSelect.tsx | 4 +- .../ui/src/components/input/NetworkSelect.tsx | 96 +- .../ui/src/components/input/PasswordInput.tsx | 57 +- .../ui/src/components/input/SearchInput.tsx | 27 +- packages/ui/src/components/input/Select.tsx | 13 +- .../ui/src/components/input/TextInput.tsx | 28 +- .../src/components/input/VerticalSelect.tsx | 6 +- .../ui/src/components/label/ClickToReveal.tsx | 57 +- .../ui/src/components/label/FeesTooltip.tsx | 4 +- .../src/components/label/GenericTooltip.tsx | 8 +- packages/ui/src/components/label/InfoTip.tsx | 11 +- packages/ui/src/components/label/Tooltip.tsx | 2 +- .../label/\320\241opyToClipboardTooltip.tsx" | 3 +- .../src/components/loading/LoadingOverlay.tsx | 2 +- .../network/DropdownNetworkDisplay.tsx | 4 +- .../src/components/network/NetworkDisplay.tsx | 4 +- .../network/NetworkDropdownDisplay.tsx | 4 +- .../components/network/NetworkSelector.tsx | 2 +- .../network/NetworkSelectorList.tsx | 2 +- .../components/networks/NetworkDisplay.tsx | 46 +- .../src/components/phishing/AntiPhishing.tsx | 10 +- .../ui/src/components/popup/PopupHeader.tsx | 181 +- .../ui/src/components/popup/PopupLayout.tsx | 46 +- packages/ui/src/components/qr/QRReader.tsx | 101 + .../ui/src/components/qr/TransactionQR.tsx | 39 + .../src/components/qr/TransactionReadQR.tsx | 58 + .../src/components/qr/TransactionShowQR.tsx | 80 + .../ui/src/components/setup/SeedImport.tsx | 12 +- .../skeleton/AssetsLoadingSkeleton.tsx | 4 +- .../skeleton/BalanceLoadingSkeleton.tsx | 2 +- .../skeleton/SendPageLoadingSkeleton.tsx | 47 + .../skeleton/TransactionsLoadingSkeleton.tsx | 2 +- .../ui/src/components/spinner/Spinner.tsx | 4 +- .../ui/src/components/spinner/ThinSpinner.tsx | 6 +- .../components/swaps/PriceImpactDialog.tsx | 10 +- .../src/components/swaps/RateUpdateDialog.tsx | 20 +- .../ui/src/components/swaps/RefreshLabel.tsx | 5 +- .../src/components/token/AddTokenListView.tsx | 13 +- .../components/token/AddTokenManualView.tsx | 6 +- .../ui/src/components/token/TokenDisplay.tsx | 21 +- .../components/token/TokenDisplayDragDrop.tsx | 183 + .../components/token/TokenDropdownDisplay.tsx | 55 + .../ui/src/components/token/TokenList.tsx | 56 + .../ui/src/components/token/TokenLogo.tsx | 44 +- .../src/components/token/TokenSearchView.tsx | 33 +- .../src/components/token/TokenSelection.tsx | 160 + .../ui/src/components/token/TokenSummary.tsx | 24 +- .../transactions/AdvancedSettings.tsx | 24 +- .../transactions/AllowanceInput.tsx | 37 +- .../BridgeNotFoundQuoteDetails.tsx | 44 +- .../transactions/GasPriceComponent.tsx | 72 +- .../transactions/GasPriceSelector.tsx | 61 +- .../ui/src/components/transactions/Price.tsx | 6 +- .../transactions/TransactionDetails.tsx | 12 +- .../TransactionDetailsAdvanced.tsx | 10 +- .../transactions/TransactionDetailsBasic.tsx | 10 +- .../transactions/TransactionDetailsItem.tsx | 6 +- .../transactions/TransactionDetailsList.tsx | 6 +- .../transactions/TransactionItem.tsx | 137 +- .../transactions/TransactionsList.tsx | 189 +- packages/ui/src/components/ui/Alert.tsx | 4 +- packages/ui/src/components/ui/Badge.tsx | 4 +- packages/ui/src/components/ui/CodeBlock.tsx | 2 +- .../components/ui/Dropdown/DropdownButton.tsx | 14 +- .../components/ui/Dropdown/DropdownMenu.tsx | 2 +- packages/ui/src/components/ui/EmptyState.tsx | 2 +- packages/ui/src/components/ui/Icon.tsx | 6 + .../ui/src/components/ui/OutlinedButton.tsx | 7 +- .../components/ui/Pagination/PagesList.tsx | 6 +- .../ui/Pagination/PaginationControls.tsx | 47 +- packages/ui/src/components/ui/Tag.tsx | 8 +- packages/ui/src/components/ui/Typography.tsx | 2 +- packages/ui/src/context/commActions.ts | 182 +- packages/ui/src/context/commTypes.ts | 30 + .../src/context/hooks/useAccountAllowances.ts | 12 +- .../useAwaitAllowanceTransactionDialog.tsx | 4 +- .../context/hooks/useCheckUserIsOnboarded.ts | 4 +- .../ui/src/context/hooks/useDappRequest.ts | 2 + .../ui/src/context/hooks/useHasHWAccounts.ts | 12 +- .../src/context/hooks/useNetWorthBalance.ts | 87 + packages/ui/src/context/hooks/useOnMount.ts | 8 +- ...ectedAccountHasEnoughNativeTokensToSend.ts | 5 +- .../src/context/hooks/useSelectedNetwork.ts | 16 +- .../ui/src/context/hooks/useTokensList.ts | 115 +- .../hooks/useTransactionWaitingDialog.tsx | 81 +- ...useWaitingForSigningInternalTransaction.ts | 23 +- packages/ui/src/context/setup.ts | 130 +- packages/ui/src/context/util/isWindow.ts | 3 +- packages/ui/src/context/util/platform.ts | 55 +- .../src/context/util/requestConnectDevice.ts | 17 +- .../ui/src/context/util/requestMediaAccess.ts | 20 + packages/ui/src/mock/MockBackgroundState.tsx | 37 +- packages/ui/src/router/PopupRouter.tsx | 5 +- packages/ui/src/router/TabRouter.tsx | 6 + packages/ui/src/router/routes.ts | 25 +- packages/ui/src/routes/IntroductionPage.tsx | 22 +- packages/ui/src/routes/PopupPage.tsx | 320 +- packages/ui/src/routes/ReceivePage.tsx | 11 +- packages/ui/src/routes/ReminderPage.tsx | 7 +- packages/ui/src/routes/UnlockPage.tsx | 13 +- .../src/routes/account/AccountsOrderPage.tsx | 110 + .../ui/src/routes/account/AddAccountPage.tsx | 6 +- .../ui/src/routes/account/AllowancesPage.tsx | 41 +- .../src/routes/account/CreateAccountPage.tsx | 20 +- .../ui/src/routes/account/EditAccountPage.tsx | 3 +- .../src/routes/account/ImportAccountPage.tsx | 4 +- .../src/routes/account/ResetAccountPage.tsx | 6 +- .../src/routes/bridge/BridgeConfirmPage.tsx | 97 +- .../ui/src/routes/bridge/BridgeSetupPage.tsx | 30 +- packages/ui/src/routes/buy/BuyPage.tsx | 211 + .../ui/src/routes/connect/ConnectPage.tsx | 27 +- .../ui/src/routes/dApp/AddEthereumChain.tsx | 69 +- packages/ui/src/routes/dApp/ApproveAsset.tsx | 134 +- packages/ui/src/routes/dApp/ApproveNFT.tsx | 82 +- packages/ui/src/routes/dApp/DappRequest.tsx | 3 + packages/ui/src/routes/dApp/SignPage.tsx | 42 +- .../src/routes/dApp/SwitchEthereumChain.tsx | 15 +- .../routes/dApp/TransactionConfirmPage.tsx | 129 +- packages/ui/src/routes/dApp/WatchAsset.tsx | 43 +- .../routes/hardware-wallet/AccountsPage.tsx | 386 +- .../ConnectDeviceStepsLayout.tsx | 11 +- .../KeystoneConnectionPage.tsx | 79 + .../routes/hardware-wallet/RemoveDevice.tsx | 160 +- .../hardware-wallet/RemoveSuccessPage.tsx | 4 +- .../routes/hardware-wallet/SetupLayout.tsx | 32 +- .../routes/hardware-wallet/SuccessPage.tsx | 4 +- .../routes/hardware-wallet/VendorsPage.tsx | 152 +- .../routes/networks/NetworkDetailsPage.tsx | 2 +- .../src/routes/networks/NetworkFormPage.tsx | 120 +- .../ui/src/routes/networks/NetworksPage.tsx | 14 +- .../src/routes/networks/SearchNetworkPage.tsx | 23 +- .../preferences/DefaultGasPreferencesPage.tsx | 17 +- .../DefaultWalletPreferencesPage.tsx | 8 +- .../preferences/LocalePreferencesPage.tsx | 60 +- .../NotificationsAndWarningsPage.tsx | 202 + .../PhishingProtectionPreferencesPage.tsx | 10 +- .../ReleaseNotesPreferencesPage.tsx | 10 +- .../preferences/WarningsPreferencesPage.tsx | 136 - .../ui/src/routes/send/SendConfirmPage.tsx | 288 +- packages/ui/src/routes/send/SendPage.tsx | 25 +- packages/ui/src/routes/settings/AboutPage.tsx | 93 +- .../ui/src/routes/settings/AddContactPage.tsx | 10 +- .../routes/settings/AddTokensConfirmPage.tsx | 2 + .../ui/src/routes/settings/AddTokensPage.tsx | 34 +- .../src/routes/settings/AddressBookPage.tsx | 7 +- .../settings/ConnectedSiteAccountsPage.tsx | 10 +- .../routes/settings/ConnectedSitesPage.tsx | 17 +- .../src/routes/settings/ExportAccountPage.tsx | 2 +- .../ui/src/routes/settings/ExportDonePage.tsx | 28 +- packages/ui/src/routes/settings/Hotkeys.tsx | 178 + .../ui/src/routes/settings/LockTimeout.tsx | 10 +- .../src/routes/settings/PreferencesPage.tsx | 24 +- .../ui/src/routes/settings/SettingsPage.tsx | 118 +- .../ui/src/routes/setup/BackupConfirmPage.tsx | 19 +- .../ui/src/routes/setup/BackupNoticePage.tsx | 73 +- .../ui/src/routes/setup/PasswordSetupPage.tsx | 6 +- .../ui/src/routes/setup/PendingSetupPage.tsx | 2 +- .../ui/src/routes/setup/ResetDonePage.tsx | 56 +- packages/ui/src/routes/setup/ResetPage.tsx | 4 +- .../ui/src/routes/setup/SeedImportPage.tsx | 8 +- .../ui/src/routes/setup/SetupDonePage.tsx | 57 +- packages/ui/src/routes/setup/SetupPage.tsx | 38 +- .../ui/src/routes/swap/SwapConfirmPage.tsx | 103 +- packages/ui/src/routes/swap/SwapPage.tsx | 74 +- packages/ui/src/routes/tokens/TokensPage.tsx | 124 + .../ui/src/routes/transaction/ApprovePage.tsx | 141 +- packages/ui/src/stories/Button.stories.tsx | 10 +- packages/ui/src/stories/Loading.stories.tsx | 2 - .../stories/views/ExtensionViews.stories.tsx | 5 +- packages/ui/src/styles/classes.ts | 79 +- packages/ui/src/styles/fonts.css | 12 +- packages/ui/src/styles/index.css | 18 +- .../ui/src/util/__tests__/account.test.ts | 2 + packages/ui/src/util/account.ts | 6 +- packages/ui/src/util/approval.ts | 12 +- packages/ui/src/util/bridgeUtils.ts | 1 - packages/ui/src/util/connectionStepUtils.tsx | 17 + packages/ui/src/util/constants.ts | 16 +- packages/ui/src/util/exchangeUtils.ts | 9 +- packages/ui/src/util/formatAccount.ts | 18 +- packages/ui/src/util/formatRounded.ts | 19 + packages/ui/src/util/gasPrice.ts | 2 +- packages/ui/src/util/getExplorer.ts | 1 - packages/ui/src/util/hardwareDevice.ts | 15 + .../src/util/hooks/token/useTokenSearch.tsx | 32 + packages/ui/src/util/hooks/useCountdown.ts | 8 +- .../ui/src/util/hooks/useCurrencyFormatter.ts | 2 +- .../util/hooks/useHardwareWalletConnect.tsx | 22 +- packages/ui/src/util/hooks/useHotKey.ts | 100 + .../ui/src/util/hooks/useTokenTransactions.ts | 6 +- packages/ui/src/util/hooks/useTransactions.ts | 2 +- packages/ui/src/util/hotkeys.ts | 454 + packages/ui/src/util/objectUtils.ts | 2 +- packages/ui/src/util/onrampUtils.ts | 1 + packages/ui/src/util/searchUD.ts | 2 + packages/ui/src/util/tokenUtils.ts | 213 + packages/ui/src/util/transactionUtils.ts | 21 + packages/ui/src/util/useEnterKey.hook.ts | 1 + packages/ui/src/util/window.ts | 34 +- packages/ui/tailwind.config.js | 27 +- packages/ui/webpack/config/env.js | 5 +- .../ui/webpack/config/jest/babelTransform.js | 29 + packages/ui/webpack/config/jest/jest.setup.js | 10 +- packages/ui/webpack/config/paths.js | 6 + packages/ui/webpack/config/webpack.config.js | 516 +- .../persistentCache/createEnvironmentHash.js | 9 + packages/ui/webpack/scripts/build.js | 27 +- packages/ui/webpack/scripts/manifest.js | 51 + packages/ui/webpack/scripts/start.js | 26 +- packages/ui/webpack/scripts/test.js | 41 +- packages/ui/yarn.lock | 14280 ++++++++-------- public/hot-reload.js | 43 - public/icons/icon-128.png | Bin 3596 -> 16960 bytes public/icons/icon-16.png | Bin 408 -> 1503 bytes public/icons/icon-48.png | Bin 1098 -> 4237 bytes release-notes.json | 616 + scripts/hot-reload.js | 46 + yarn.lock | 282 + 525 files changed, 27983 insertions(+), 16114 deletions(-) rename public/manifest.json => manifest/base.json (80%) create mode 100644 manifest/chrome.json create mode 100644 manifest/firefox.json create mode 100644 packages/background/src/controllers/CampaignsController.ts create mode 100644 packages/background/src/controllers/NotificationController.ts create mode 100644 packages/background/src/controllers/OnrampController.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-61.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-62.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-63.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-64.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-65.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-66.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-67.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-68.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-69.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-70.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-71.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-72.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-73.ts create mode 100644 packages/background/src/infrastructure/stores/migrator/migrations/migration-74.ts delete mode 100644 packages/background/src/typings/worker-loader.d.ts create mode 100644 packages/background/src/utils/env.ts delete mode 100644 packages/background/src/utils/notifications.ts create mode 100644 packages/background/src/utils/onrampApi.ts rename packages/background/src/utils/{types => swaps}/1inch.ts (51%) rename packages/background/src/utils/{ => swaps}/1inchError.ts (97%) create mode 100644 packages/background/src/utils/swaps/openOcean.ts delete mode 100644 packages/ui/.eslintrc create mode 100644 packages/ui/.eslintrc.json create mode 100644 packages/ui/src/assets/fonts/Inter-Medium.ttf create mode 100644 packages/ui/src/assets/fonts/Inter-SemiBold.ttf create mode 100644 packages/ui/src/assets/images/icons/accounts_order.svg create mode 100644 packages/ui/src/assets/images/icons/assets_order.svg create mode 100644 packages/ui/src/assets/images/icons/cash.svg create mode 100644 packages/ui/src/assets/images/icons/filter.svg create mode 100644 packages/ui/src/assets/images/icons/hotkeys.svg create mode 100644 packages/ui/src/assets/images/icons/import_seed.svg create mode 100644 packages/ui/src/assets/images/icons/key.svg create mode 100644 packages/ui/src/assets/images/icons/keystone.svg create mode 100644 packages/ui/src/assets/images/icons/new_account.svg create mode 100644 packages/ui/src/assets/images/icons/no_camera.svg create mode 100644 packages/ui/src/assets/images/icons/onramper.svg create mode 100644 packages/ui/src/assets/images/icons/order.svg create mode 100644 packages/ui/src/assets/images/icons/safe.svg create mode 100644 packages/ui/src/assets/images/icons/searchnotfound.svg create mode 100644 packages/ui/src/assets/images/icons/switch.svg create mode 100644 packages/ui/src/assets/images/icons/wallet.json create mode 100644 packages/ui/src/assets/images/icons/wallet.svg create mode 100644 packages/ui/src/assets/images/icons/your_backup.svg create mode 100644 packages/ui/src/assets/images/keystone.png delete mode 100644 packages/ui/src/components/ActivityAssetsView.tsx delete mode 100644 packages/ui/src/components/AssetsList.tsx create mode 100644 packages/ui/src/components/CollapsableMessage.tsx delete mode 100644 packages/ui/src/components/CollapsableWarning.tsx create mode 100644 packages/ui/src/components/account/AccountsDisplayDragDrop.tsx create mode 100644 packages/ui/src/components/assets/AssetsButton.tsx create mode 100644 packages/ui/src/components/assets/AssetsSort.tsx create mode 100644 packages/ui/src/components/button/OrderButton.tsx create mode 100644 packages/ui/src/components/chain/ProviderStatus.tsx create mode 100644 packages/ui/src/components/currency/CurrencyDisplay.tsx create mode 100644 packages/ui/src/components/currency/CurrencyDropdownDisplay.tsx create mode 100644 packages/ui/src/components/currency/CurrencyList.tsx create mode 100644 packages/ui/src/components/currency/CurrencySelection.tsx create mode 100644 packages/ui/src/components/dialog/HotkeysDialog.tsx delete mode 100644 packages/ui/src/components/dialog/ProviderDownDialog.tsx create mode 100644 packages/ui/src/components/hardwareWallet/AdvancedSettings.tsx create mode 100644 packages/ui/src/components/hardwareWallet/HardwareWalletAccount.tsx create mode 100644 packages/ui/src/components/home/ActivityAssetsView.tsx create mode 100644 packages/ui/src/components/home/AssetsList.tsx create mode 100644 packages/ui/src/components/hotkeys/DisplayHotkey.tsx create mode 100644 packages/ui/src/components/hotkeys/HotkeysCollapsedMessage.tsx create mode 100644 packages/ui/src/components/icons/EditIcon.tsx create mode 100644 packages/ui/src/components/icons/EyeCloseIcon.tsx create mode 100644 packages/ui/src/components/icons/EyeOpenIcon.tsx create mode 100644 packages/ui/src/components/icons/FilterIcon.tsx create mode 100644 packages/ui/src/components/icons/OrderIcon.tsx create mode 100644 packages/ui/src/components/qr/QRReader.tsx create mode 100644 packages/ui/src/components/qr/TransactionQR.tsx create mode 100644 packages/ui/src/components/qr/TransactionReadQR.tsx create mode 100644 packages/ui/src/components/qr/TransactionShowQR.tsx create mode 100644 packages/ui/src/components/skeleton/SendPageLoadingSkeleton.tsx create mode 100644 packages/ui/src/components/token/TokenDisplayDragDrop.tsx create mode 100644 packages/ui/src/components/token/TokenDropdownDisplay.tsx create mode 100644 packages/ui/src/components/token/TokenList.tsx create mode 100644 packages/ui/src/components/token/TokenSelection.tsx create mode 100644 packages/ui/src/context/hooks/useNetWorthBalance.ts create mode 100644 packages/ui/src/context/util/requestMediaAccess.ts create mode 100644 packages/ui/src/routes/account/AccountsOrderPage.tsx create mode 100644 packages/ui/src/routes/buy/BuyPage.tsx create mode 100644 packages/ui/src/routes/hardware-wallet/KeystoneConnectionPage.tsx create mode 100644 packages/ui/src/routes/preferences/NotificationsAndWarningsPage.tsx delete mode 100644 packages/ui/src/routes/preferences/WarningsPreferencesPage.tsx create mode 100644 packages/ui/src/routes/settings/Hotkeys.tsx create mode 100644 packages/ui/src/routes/tokens/TokensPage.tsx create mode 100644 packages/ui/src/util/hooks/token/useTokenSearch.tsx create mode 100644 packages/ui/src/util/hooks/useHotKey.ts create mode 100644 packages/ui/src/util/hotkeys.ts create mode 100644 packages/ui/src/util/onrampUtils.ts create mode 100644 packages/ui/webpack/config/jest/babelTransform.js create mode 100644 packages/ui/webpack/config/webpack/persistentCache/createEnvironmentHash.js create mode 100644 packages/ui/webpack/scripts/manifest.js delete mode 100644 public/hot-reload.js create mode 100644 scripts/hot-reload.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c6c52bbe..c6bb3e04d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,6 +19,7 @@ env: RELEASE_BRANCH: release/${{ github.event.inputs.extension-version }} GH_ACCESS_TOKEN: ${{ secrets.USER_PAT }}:${{ secrets.PAT }} EXTENSION_NAME: block-wallet-${{ github.event.inputs.extension-version }} + EXTENSION_NAME_FF: block-wallet-firefox-${{ github.event.inputs.extension-version }} ARTIFACTS_FOLDER: artifacts S3_PATH: s3://releases.blockwallet.io/extension TARGET_BRANCH: master @@ -72,7 +73,8 @@ jobs: - name: Compare release version with the latest tag id: tag_comparison run: | - latest_tag=$(git ls-remote --tags --sort='v:refname' origin | cut --delimiter='/' --fields=3 | tail -n 1) + git fetch --all --tags + latest_tag=$(git tag -l | tail -n 1) if [[ -z $latest_tag ]] then exit 0 @@ -126,7 +128,7 @@ jobs: run: | wget https://raw.githubusercontent.com/block-wallet/release-helpers/main/update_file.py -O update_file.py FILE=package.json python3 update_file.py - FILE=public/manifest.json python3 update_file.py + FILE=manifest/base.json python3 update_file.py - name: Update release notes env: @@ -139,7 +141,7 @@ jobs: - name: Commit package.json, manifest.json and release-notes.json id: version_commit run: | - git add package.json release-notes.json public/manifest.json + git add package.json release-notes.json manifest/base.json git commit --message "chore: update files with the new version ${{ github.event.inputs.extension-version }} and also add the new release notes" git push origin $RELEASE_BRANCH @@ -190,84 +192,90 @@ jobs: if-no-files-found: error path: dist - tests: - needs: build - container: cypress/browsers:node16.16.0-chrome105-ff99-edge - runs-on: ubuntu-latest - env: - WIDTH: 357 - HEIGHT: 610 - DBUS_SESSION_BUS_ADDRESS: /dev/null - steps: - - name: Checkout e2e repository - uses: actions/checkout@v2 - with: - repository: block-wallet/e2e-tests - ref: refs/heads/main - token: ${{ secrets.PAT }} - - - name: Install node - uses: actions/setup-node@v3 - with: - node-version-file: .nvmrc - - - name: Install yarn - run: | - npm install --global yarn - - - name: Cache e2e dependencies - id: cache-cypress - uses: actions/cache@v3 - with: - path: | - node_modules - cypress/cache - key: ${{ runner.os }}-cypress-v1-${{ hashFiles('yarn.lock') }} - - - name: Download the extension - uses: actions/download-artifact@v3 - with: - name: blockwallet-extension - path: blockwallet-extension - - - name: Install e2e dependencies - run: | - yarn install --prefer-offline --frozen-lockfile --network-concurrency 1 - - - name: UI Tests - Chrome - run: | - yarn run test:e2e - env: - ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_KEY }} - GITHUB_TOKEN: ${{ secrets.PAT }} - TEST_TAGS: puppeteer,extension - - - name: Take the release notes screenshot - run: | - yarn run test:e2e - env: - EXTENSION_VERSION: ${{ needs.build.outputs.version_without_preffix }} - TEST_TAGS: release-notes - BROWSER_WIDTH: ${{ env.WIDTH }} - BROWSER_HEIGHT: ${{ env.HEIGHT }} - - - name: Install ImageMagick (needed for convert) - run: | - apt update && apt install -y imagemagick --fix-missing - - - name: Cut the screenshot - run: | - convert screenshots/release-notes-${{ needs.build.outputs.version_without_preffix }}.png -crop ${{ env.WIDTH }}x${{ env.HEIGHT }}+0+0 $EXTENSION_NAME.png - - - name: Save screenshot of release notes + - name: Save Firefox extension folder uses: actions/upload-artifact@v3 with: - name: screenshot + name: blockwallet-extension-firefox if-no-files-found: error - path: ${{ env.EXTENSION_NAME }}.png + path: dist-firefox + #tests: + # needs: build + # container: cypress/browsers:node16.16.0-chrome105-ff99-edge + # runs-on: ubuntu-latest + # env: + # WIDTH: 357 + # HEIGHT: 610 + # DBUS_SESSION_BUS_ADDRESS: /dev/null + # steps: + # - name: Checkout e2e repository + # uses: actions/checkout@v3 + # with: + # repository: block-wallet/e2e-tests + # ref: refs/heads/main + # token: ${{ secrets.PAT }} + + # - name: Install node + # uses: actions/setup-node@v3 + # with: + # node-version-file: .nvmrc + + # - name: Install yarn + # run: | + # npm install --global yarn + + # - name: Cache e2e dependencies + # id: cache-cypress + # uses: actions/cache@v3 + # with: + # path: | + # node_modules + # cypress/cache + # key: ${{ runner.os }}-cypress-v1-${{ hashFiles('yarn.lock') }} + + # - name: Download the extension + # uses: actions/download-artifact@v3 + # with: + # name: blockwallet-extension + # path: blockwallet-extension + + # - name: Install e2e dependencies + # run: | + # yarn install --prefer-offline --frozen-lockfile --network-concurrency 1 + + # - name: UI Tests - Chrome + # run: | + # yarn run test:e2e + # env: + # ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_KEY }} + # GITHUB_TOKEN: ${{ secrets.PAT }} + # TEST_TAGS: puppeteer,extension + + # - name: Take the release notes screenshot + # run: | + # yarn run test:e2e + # env: + # EXTENSION_VERSION: ${{ needs.build.outputs.version_without_preffix }} + # TEST_TAGS: release-notes + # BROWSER_WIDTH: ${{ env.WIDTH }} + # BROWSER_HEIGHT: ${{ env.HEIGHT }} + + # - name: Install ImageMagick (needed for convert) + # run: | + # apt update && apt install -y imagemagick --fix-missing + + # - name: Cut the screenshot + # run: | + # convert screenshots/release-notes-${{ needs.build.outputs.version_without_preffix }}.png -crop ${{ env.WIDTH }}x${{ env.HEIGHT }}+0+0 $EXTENSION_NAME.png + + # - name: Save screenshot of release notes + # uses: actions/upload-artifact@v3 + # with: + # name: screenshot + # if-no-files-found: error + # path: ${{ env.EXTENSION_NAME }}.png package: - needs: tests + needs: build runs-on: ubuntu-latest steps: - name: Delete artifacts folders @@ -281,27 +289,39 @@ jobs: name: blockwallet-extension path: blockwallet-extension - - name: Download the extension screenshot (release notes) + - name: Download the extension built folder uses: actions/download-artifact@v3 with: - name: screenshot - path: screenshot + name: blockwallet-extension-firefox + path: blockwallet-extension-firefox + + #- name: Download the extension screenshot (release notes) + # uses: actions/download-artifact@v3 + # with: + # name: screenshot + # path: screenshot - name: Zip extension id: zip + shell: bash run: | mkdir -p $ARTIFACTS_FOLDER mv blockwallet-extension dist - zip -r -D $ARTIFACTS_FOLDER/$EXTENSION_NAME.zip dist/ + mv blockwallet-extension-firefox dist-firefox + zip -r -D $ARTIFACTS_FOLDER/$EXTENSION_NAME.zip dist/* + cd dist-firefox + zip -r -D $EXTENSION_NAME_FF.zip * + mv $EXTENSION_NAME_FF.zip ../$ARTIFACTS_FOLDER - name: Calculate SHA of the extension id: sha run: | cd $ARTIFACTS_FOLDER sha256sum $EXTENSION_NAME.zip > $EXTENSION_NAME.checksum + sha256sum $EXTENSION_NAME_FF.zip > $EXTENSION_NAME_FF.checksum - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 + uses: aws-actions/configure-aws-credentials@v2 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_RELEASE_BUCKET }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_RELEASE_BUCKET }} @@ -310,8 +330,7 @@ jobs: - name: Upload to S3 run: | aws s3 cp --recursive $ARTIFACTS_FOLDER $S3_PATH - aws s3 cp screenshot/$EXTENSION_NAME.png $S3_PATH/$EXTENSION_NAME.png - + # aws s3 cp screenshot/$EXTENSION_NAME.png $S3_PATH/$EXTENSION_NAME.png - name: Upload version and notes to S3 env: DATA: '${{ github.event.inputs.release-notes }}' @@ -352,9 +371,9 @@ jobs: run: | s3_url=$(echo "${S3_PATH/s3/https}") pr_title="[Automated] Extension release ${{ github.event.inputs.extension-version }}" - pr_body=$(echo -e "# PR for release ${{ github.event.inputs.extension-version }}\n\n## Type of change :writing_hand:\n\nClick the correct/s option/s\n\n- [ ] Bug fix (non-breaking change which fixes an issue)\n- [ ] New feature (non-breaking change which adds functionality)\n- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)\n- [ ] This change requires a documentation update\n\n## Manual tests :test_tube:\n\n- [ ] I run the tests and they were succesful\n\n## Artifacts :space_invader:\n\n - [Extension zip]($s3_url/${{ env.EXTENSION_NAME }}.zip)\n - [Release notes screenshot]($s3_url/${{ env.EXTENSION_NAME }}.png)\n - [SHA256 checksum]($s3_url/${{ env.EXTENSION_NAME }}.checksum)\n\n") + pr_body=$(echo -e "# PR for release ${{ github.event.inputs.extension-version }}\n\n## Type of change :writing_hand:\n\nClick the correct/s option/s\n\n- [ ] Bug fix (non-breaking change which fixes an issue)\n- [ ] New feature (non-breaking change which adds functionality)\n- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)\n- [ ] This change requires a documentation update\n\n## Manual tests :test_tube:\n\n- [ ] I run the tests and they were succesful\n\n## Artifacts :space_invader:\n\n - [Extension zip]($s3_url/${{ env.EXTENSION_NAME }}.zip)\n - [SHA256 checksum]($s3_url/${{ env.EXTENSION_NAME }}.checksum)\n - [Firefox Extension zip]($s3_url/${{ env.EXTENSION_NAME_FF }}.zip)\n - [SHA256 checksum]($s3_url/${{ env.EXTENSION_NAME_FF }}.checksum)\n\n") pr_reviewer="block-wallet/releasereviewers" - pr_assignee="leablock" + pr_assignee="sreblock" pr_label="automated,release" gh label create --force "automated" --description "PR made by an automation" --color "bcf5db" @@ -378,7 +397,7 @@ jobs: notify: if: success() - needs: [validation, build, tests, package, pull_request] + needs: [validation, build, package, pull_request] runs-on: ubuntu-latest steps: - name: Slack notification - Ready to release @@ -393,7 +412,7 @@ jobs: "attachments": [ { "color": "good", - "pretext": "The pull request was created. After reviewing it, please merge it to continue the release process that will launch *Release (automated)*. CC: <@U01H6J242BY>, <@U021GBLL00Z>", + "pretext": "The pull request was created. After reviewing it, please merge it to continue the release process that will launch *Release (automated)*.", "author_name": "GitHub Pull Request ${{ github.event.inputs.extension-version }}", "author_link": "${{ needs.pull_request.outputs.pr_url }}", "title": "GitHub Pull Request for the release ${{ github.event.inputs.extension-version }} created as draft" @@ -404,7 +423,7 @@ jobs: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_BLOCKWALLET_RELEASES }} error: if: failure() - needs: [validation, build, tests, package, pull_request, notify] + needs: [validation, build, package, pull_request, notify] runs-on: ubuntu-latest steps: - name: Slack notification - CI status diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc2973308..fbe405373 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,9 +106,10 @@ jobs: test: runs-on: ubuntu-latest + if: startsWith(github.head_ref, 'dependabot/') == false steps: - name: Checkout monorepo - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install node uses: actions/setup-node@v3 @@ -174,7 +175,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout monorepo - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install node uses: actions/setup-node@v3 @@ -226,7 +227,7 @@ jobs: - name: Configure AWS credentials if: startsWith(github.head_ref, 'dependabot/') == false - uses: aws-actions/configure-aws-credentials@v1 + uses: aws-actions/configure-aws-credentials@v2 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_RELEASE_BUCKET }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_RELEASE_BUCKET }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8062e1bc0..11963595d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 + uses: aws-actions/configure-aws-credentials@v2 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_RELEASE_BUCKET }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_RELEASE_BUCKET }} @@ -102,7 +102,7 @@ jobs: "attachments": [ { "color": "good", - "pretext": "New release ${{ needs.release.outputs.extension }}: <${{ needs.release.outputs.s3_url }}/block-wallet-${{ needs.release.outputs.extension }}.zip| extension.zip> and <${{ needs.release.outputs.s3_url }}/block-wallet-${{ needs.release.outputs.extension }}.checksum| checksum>. After uploading it to the store please publish the GitHub Release to finish the release process.\nCC: <@U01H6J242BY>, <@U021GBLL00Z>, <@U01HRUM7XUH>", + "pretext": "New release ${{ needs.release.outputs.extension }}: <${{ needs.release.outputs.s3_url }}/block-wallet-${{ needs.release.outputs.extension }}.zip| extension.zip> and <${{ needs.release.outputs.s3_url }}/block-wallet-${{ needs.release.outputs.extension }}.checksum| checksum>. After uploading it to the store please publish the GitHub Release to finish the release process.\n: <@U01HRUM7XUH>", "author_name": "GitHub Release ${{ needs.release.outputs.extension }}", "author_link": "${{ needs.release.outputs.html_url }}", "title": "GitHub Release ${{ needs.release.outputs.extension }} has been created as a draft." diff --git a/.gitignore b/.gitignore index 37a82bb28..5cb775d6f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ eslint-report.html # production /build /dist +/dist-firefox # misc .DS_Store diff --git a/.nvmrc b/.nvmrc index 9f4189900..b714151ef 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v14.17.6 \ No newline at end of file +v18.18.0 \ No newline at end of file diff --git a/LICENSE b/LICENSE index ef487ce39..bb32854e5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,14 +1,14 @@ -All copyrights (including licenses) are owned by Virtual Privacy AG (“BlockWallet”). +All copyrights (including licenses) are owned by Business Variety Limited (3189974). The BlockWallet owns all legal rights, title, and interest in and to the work, software, application, source code, documentation, and any other documents in this repository (collectively, the “Software”), including any intellectual property rights which subsist in the Software (whether those rights happen to be registered or not, and wherever in the world those rights may exist), whether in source code or any other form. -You are granted a limited license (the “Limited license”). Limited license means that you may not, including transferring any rights related to Software to any third parties, distribute, publish, copy, modify, merge, combine with another software, create derivative works of, reverse engineer, decompile or otherwise attempt to extract the source code of the Software or any part thereof, except that you may contribute to this repository. +You are granted a limited license (the “Limited license”). Limited license means that you may not, including transferring any rights related to Software to any third parties, distribute, publish, copy, modify, merge, combine with another software, create derivative works of, reverse engineer, decompile or otherwise attempt to extract the source code of the Software or any part thereof, except that you may contribute to this repository. You are granted a non-exclusive, non-transferable, non-sublicensable license to distribute, publish, copy, modify, merge, combine with another software/program or create derivative works of the Software (such adjusted software, collectively, the “Adjusted Software”) solely for Non-Commercial Use if you: prominently mark (the "Mark") each copy of the Adjusted Software that the Sofware is used in the Adjusted Software and that the Software is the copyright of BlockWallet; and -subject to the Adjusted Software and any distribution, publication, copy, modification, merger in addition to that, combination with another program, or derivative works thereof to the exact Mark requirement and Non-Commercial Use restriction set forth herein. +subject to the Adjusted Software and any distribution, publication, copy, modification, merger in addition to that, combination with another program, or derivative works thereof to the exact Mark requirement and Non-Commercial Use restriction set forth herein. The Software is exclusively for "Non-Commercial" use. The Non-Commercial is reasonably determined by BlockWallet in its sole discretion and is non-negotiable. The Non-commercial means, including but not limited to, that the Software is only for personal use for research, debugging, personal (private) study, hobby projects, or amateur pursuits, in each case without any anticipated commercial application. Either Software or Adjusted software is used strictly for non-profit purposes, following the condition set hereof, and always properly Marked. diff --git a/Makefile b/Makefile index 437e301f6..d34767395 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ ENVIRONMENT ?= dev +BROWSER ?= chrome depcheck: @cd packages/background && npx depcheck @@ -27,8 +28,11 @@ install: @cd packages/background && rm -rf node_modules @cd packages/ui && rm -rf node_modules @cd packages/provider && rm -rf node_modules + @echo "Installing Background package..." @cd packages/background && yarn install + @echo "Installing UI package" @cd packages/ui && yarn install + @echo "Installing Provider package" @cd packages/provider && yarn install install/ci: @@ -37,35 +41,52 @@ install/ci: @cd packages/provider && yarn install --prefer-offline --frozen-lockfile --network-concurrency 1 build/ui: - @cd packages/ui && $(MAKE) build/ui --no-print-directory + @cd packages/ui && BROWSER=$(BROWSER) $(MAKE) build/ui --no-print-directory build/background: - @cd packages/background && $(MAKE) build/background --no-print-directory + @cd packages/background && BROWSER=$(BROWSER) $(MAKE) build/background --no-print-directory build/provider: - @cd packages/provider && $(MAKE) build/provider --no-print-directory + @cd packages/provider && BROWSER=$(BROWSER) $(MAKE) build/provider --no-print-directory cp/release-notes: +ifeq ($(BROWSER), firefox) + @cp release-notes.json dist-firefox +else @cp release-notes.json dist +endif build: - @rm -Rf dist/ + @rm -Rf dist-firefox + @rm -Rf dist @$(MAKE) ENVIRONMENT=$(ENVIRONMENT) build/background --no-print-directory @$(MAKE) ENVIRONMENT=$(ENVIRONMENT) build/provider --no-print-directory @$(MAKE) build/ui --no-print-directory - @$(MAKE) cp/release-notes --no-print-directory +ifeq ($(BROWSER), firefox) + @mkdir -p dist-firefox && cp -r dist/* dist-firefox/ + @rm -Rf dist +endif + @$(MAKE) BROWSER=$(BROWSER) build/manifest --no-print-directory + @$(MAKE) BROWSER=$(BROWSER) cp/release-notes --no-print-directory build/prod: @rm -Rf dist/ + @rm -Rf dist-firefox/ @$(MAKE) ENVIRONMENT=prod build/background --no-print-directory @$(MAKE) ENVIRONMENT=prod build/provider --no-print-directory @$(MAKE) GENERATE_SOURCEMAP=false build/ui --no-print-directory - @$(MAKE) cp/release-notes --no-print-directory + @mkdir dist-firefox && cp -r dist/* dist-firefox/ + @$(MAKE) BROWSER=chrome build/manifest --no-print-directory + @$(MAKE) BROWSER=firefox build/manifest --no-print-directory + @cp release-notes.json dist + @cp release-notes.json dist-firefox + + +build/manifest: + @cd packages/ui && BROWSER=$(BROWSER) $(MAKE) build/manifest --no-print-directory build/prod-zip: @rm -Rf dist/ - @$(MAKE) ENVIRONMENT=prod build/background --no-print-directory - @$(MAKE) ENVIRONMENT=prod build/provider --no-print-directory - @$(MAKE) build/ui --no-print-directory - @$(MAKE) cp/release-notes --no-print-directory - @zip -r -D block-extension.zip dist/ \ No newline at end of file + @$(MAKE) ENVIRONMENT=prod build/prod + @zip -r -D block-extension-chrome.zip dist/ + @zip -r -D block-extension-firefox.zip dist-firefox/ \ No newline at end of file diff --git a/README.md b/README.md index 9b4ad038e..0b57be74c 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ See the [Acknowledgments](docs/acknowledgments.md) file for details      - + diff --git a/docs/acknowledgments.md b/docs/acknowledgments.md index 40bb9d81a..7dd24f2da 100644 --- a/docs/acknowledgments.md +++ b/docs/acknowledgments.md @@ -2,63 +2,41 @@ Thanks to: Aleksandras: https://github.com/AleksandrasG -Alejandro: https://github.com/a-acer +DEV: https://github.com/chk0912 -Kshamenk: https://github.com/kshamenkgil +SRE: https://github.com/sreblock -Eze: https://github.com/IchBinEze +MetaMask/Transaction-controller MIT license. You may obtain a copy of the here: +https://github.com/MetaMask/transaction-controller/blob/master/LICENSE -Julian: https://github.com/julianariel +MetaMask/controllers MIT license. You may obtain a copy of the here: +https://github.com/MetaMask/transaction-controller/blob/master/LICENSE -Ulysse: https://github.com/Ulydev +MetaMask/contract-metadata ISC license. You may obtain a copy of the here: +https://github.com/MetaMask/contract-metadata/blob/master/LICENSE -Olga: https://github.com/helgonaft +MetaMask/obs-store ISC license. You may obtain a copy of the here: +https://github.com/MetaMask/obs-store/blob/main/LICENSE -Rodrigo: https://github.com/rodr-r +Crypto-browserify/createHash MIT license. You may obtain a copy of the here: +https://github.com/crypto-browserify/createHash/blob/master/LICENSE -Martin: https://github.com/martnLecam +MetaMask/KeyringController ISC license. You may obtain a copy of the here: +https://github.com/MetaMask/KeyringController/blob/main/LICENSE -Facundo: https://github.com/fpezzola +MetaMask/eth-rpc-errors MIT license. You may obtain a copy of the here: +https://github.com/MetaMask/eth-rpc-errors/blob/main/LICENSE -Leandro: https://github.com/leablock +Dumbmatter/fakelndexedDB Apache license 2.0. You may obtain a copy of the here: +https://github.com/dumbmatter/fakeIndexedDB/blob/master/LICENSE -Alexandre: https://github.com/s0wcy +Rstacruz/jsdom-global MIT license. You may obtain a copy of the here: +https://github.com/rstacruz/jsdom-global/blob/master/README.md -Angelina: https://github.com/linagak +Piotrwitek/ts-mocha MIT license. You may obtain a copy of the here: +https://github.com/piotrwitek/ts-mocha/blob/master/LICENSE -Iman: https://github.com/ImanH +Acvetkov/sinon-chrome ISC license. You may obtain a copy of the here: +https://github.com/acvetkov/sinon-chrome/blob/master/LICENSE -MetaMask/Transaction-controller MIT license. You may obtain a copy of the here: -https://github.com/MetaMask/transaction-controller/blob/master/LICENSE - -MetaMask/controllers MIT license. You may obtain a copy of the here: -https://github.com/MetaMask/transaction-controller/blob/master/LICENSE - -MetaMask/contract-metadata ISC license. You may obtain a copy of the here: -https://github.com/MetaMask/contract-metadata/blob/master/LICENSE - -MetaMask/obs-store ISC license. You may obtain a copy of the here: -https://github.com/MetaMask/obs-store/blob/main/LICENSE - -Crypto-browserify/createHash MIT license. You may obtain a copy of the here: -https://github.com/crypto-browserify/createHash/blob/master/LICENSE - -MetaMask/KeyringController ISC license. You may obtain a copy of the here: -https://github.com/MetaMask/KeyringController/blob/main/LICENSE - -MetaMask/eth-rpc-errors MIT license. You may obtain a copy of the here: -https://github.com/MetaMask/eth-rpc-errors/blob/main/LICENSE - -Dumbmatter/fakelndexedDB Apache license 2.0. You may obtain a copy of the here: -https://github.com/dumbmatter/fakeIndexedDB/blob/master/LICENSE - -Rstacruz/jsdom-global MIT license. You may obtain a copy of the here: -https://github.com/rstacruz/jsdom-global/blob/master/README.md - -Piotrwitek/ts-mocha MIT license. You may obtain a copy of the here: -https://github.com/piotrwitek/ts-mocha/blob/master/LICENSE - -Acvetkov/sinon-chrome ISC license. You may obtain a copy of the here: -https://github.com/acvetkov/sinon-chrome/blob/master/LICENSE - -and many more... +and many more... diff --git a/docs/convention.md b/docs/convention.md index 32217e5b3..6faaa22cd 100644 --- a/docs/convention.md +++ b/docs/convention.md @@ -10,7 +10,7 @@ Using a semantic Git commit method can greatly improve the readability of Git lo ## Commit messages -Semantic Git commits start with a semantic tag and use an imperative voice. Git commit messages should be prefixed with one the following tags: +Semantic Git commits start with a semantic tag and use an imperative voice. Git commit messages should be prefixed with one of the following tags: ``` feat: implement new features for endusers @@ -37,7 +37,7 @@ Example ```"feat: add ability to view most popular posts"``` ### fix -The fix: tag should be used to identify any bug fixes to production code. This includes any fix that would effect the enduser, not the build process. +The fix: tag should be used to identify any bug fixes to production code. This includes any fix that would affect the enduser, not the build process. Example @@ -51,9 +51,9 @@ Example ```"docs: add detailed installation instructions for Ubuntu"``` ### style -The style: tag should be used to identify changes made to the code style, which do not effect the enduser. Note that this is separate from the styling of user interfaces, which does effect the enduser. For example, a style change may indicate a change from using tab indentation to spaces. +The style: tag should be used to identify changes made to the code style, which do not affect the enduser. Note that this is separate from the styling of user interfaces, which does affect the enduser. For example, a style change may indicate a change from using tab indentation to spaces. -For style updates that would effect the enduser, such as CSS changes, use the feat: tag instead. +For style updates that would affect the enduser, such as CSS changes, use the feat: tag instead. Example @@ -83,7 +83,7 @@ Example ## Branch names -Based on the previous standard, the branch names must start with the following preffixes: +Based on the previous standard, the branch names must start with the following prefixes: ``` feat/: implement new features for endusers @@ -94,7 +94,7 @@ refactor/: refactoring of code test/: adding or updating tests chore/: updates to build process release/: updates related to a new release. This is only used by our Release workflow. -dependabot/: branch use by the Dependabot +dependabot/: branch used by the Dependabot ``` # Fix diff --git a/docs/guideline.md b/docs/guideline.md index 6e0fbfdc6..6f63eb67d 100644 --- a/docs/guideline.md +++ b/docs/guideline.md @@ -59,4 +59,4 @@ To commit your change(s) just add your file/s and then commit your changes with ### ⤴ Open a Pull Request -Once your changes are ready please open a Pull Request and complete the template. The reviewer team will check your PR and when it's approved you'll be able to merge it. In case you won't to do an external contribution please create an Issue with your proposal. +Once your changes are ready please open a Pull Request and complete the template. The reviewer team will check your PR and when it's approved you'll be able to merge it. In case you want to do an external contribution please create an Issue with your proposal. diff --git a/docs/workflow.md b/docs/workflow.md index b17051442..ee7053824 100644 --- a/docs/workflow.md +++ b/docs/workflow.md @@ -12,7 +12,7 @@ Once the previous pull request is merged this workflow is launched. It creates t ## Convention -This workflow runs when a pull request is opened with master as a targer branch. It checks that the commit messages and branch name follow our [convention](docs/convention.md) +This workflow runs when a pull request is opened with master as a target branch. It checks that the commit messages and branch name follow our [convention](docs/convention.md) ## CI @@ -25,4 +25,4 @@ This workflow runs on every push: ## Dependabot -GitHub's Dependabot helps us to maintain our dependencies updated by opening pull requests when it's necessary. \ No newline at end of file +GitHub's Dependabot helps us to maintain our dependencies updated by opening pull requests when it's necessary. diff --git a/public/manifest.json b/manifest/base.json similarity index 80% rename from public/manifest.json rename to manifest/base.json index 5627d3593..15a00c125 100644 --- a/public/manifest.json +++ b/manifest/base.json @@ -23,7 +23,7 @@ "default_popup": "popup.html", "default_title": "BlockWallet" }, - "description": "The most private, non-custodial cryptocurrency wallet", + "description": "All eyes on your dreams with BlockWallet - your self-custodial Web3 wallet that handles the rest.", "homepage_url": "https://www.blockwallet.io/", "icons": { "16": "icons/icon-16.png", @@ -38,13 +38,22 @@ "permissions": [ "activeTab", "storage", + "unlimitedStorage", "notifications", "alarms", "scripting" ], "host_permissions": ["file://*/*", "http://*/*", "https://*/*"], "short_name": "BlockWallet", - "version": "0.6.0", + "commands": { + "_execute_browser_action": { + "suggested_key": { + "default": "Alt+O" + } + } + }, + "version": "1.2.13", + "version_name": "1.2.13", "web_accessible_resources": [ { "resources": ["blankProvider.js", "keep-alive"], diff --git a/manifest/chrome.json b/manifest/chrome.json new file mode 100644 index 000000000..e28257dfd --- /dev/null +++ b/manifest/chrome.json @@ -0,0 +1,21 @@ +{ + "background": { + "service_worker": "background.js" + }, + "content_scripts": [ + { + "js": ["content.js"], + "matches": ["http://*/*", "https://*/*"], + "exclude_matches": [ + "https://block-wallet.github.io/eth-ledger-bridge-keyring/*", + "https://connect.trezor.io/*" + ], + "run_at": "document_start", + "all_frames": true + }, + { + "js": ["vendor/trezor/trezor-content.js"], + "matches": ["*://connect.trezor.io/*/popup.html"] + } + ] +} diff --git a/manifest/firefox.json b/manifest/firefox.json new file mode 100644 index 000000000..e49092fb6 --- /dev/null +++ b/manifest/firefox.json @@ -0,0 +1,37 @@ +{ + "background": { + "scripts": [ + "browser-polyfill.min.js", + "background.js", + "bw-libs.js" + + ] + }, + "content_scripts": [ + { + "js": [ + "browser-polyfill.min.js", + "content.js" + ], + "matches": [ + "http://*/*", + "https://*/*" + ], + "exclude_matches": [ + "https://block-wallet.github.io/eth-ledger-bridge-keyring/*", + "https://connect.trezor.io/*" + ], + "run_at": "document_start", + "all_frames": true + }, + { + "js": [ + "browser-polyfill.min.js", + "vendor/trezor/trezor-content.js" + ], + "matches": [ + "*://connect.trezor.io/*/popup.html" + ] + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 0ad7ac45e..cf55b5ebe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "extension", - "version": "0.6.0", + "version": "1.2.13", "private": true, "scripts": { "install": "make install/ci", @@ -8,5 +8,8 @@ "build": "make build", "build-prod": "make build/prod", "test": "make test" + }, + "dependencies": { + "build": "^0.1.4" } } \ No newline at end of file diff --git a/packages/background/.nvmrc b/packages/background/.nvmrc index 9f4189900..b714151ef 100644 --- a/packages/background/.nvmrc +++ b/packages/background/.nvmrc @@ -1 +1 @@ -v14.17.6 \ No newline at end of file +v18.18.0 \ No newline at end of file diff --git a/packages/background/LICENSE b/packages/background/LICENSE index ef487ce39..0d52353d8 100644 --- a/packages/background/LICENSE +++ b/packages/background/LICENSE @@ -1,4 +1,4 @@ -All copyrights (including licenses) are owned by Virtual Privacy AG (“BlockWallet”). +All copyrights (including licenses) are owned by Business Variety Limited (3189974). The BlockWallet owns all legal rights, title, and interest in and to the work, software, application, source code, documentation, and any other documents in this repository (collectively, the “Software”), including any intellectual property rights which subsist in the Software (whether those rights happen to be registered or not, and wherever in the world those rights may exist), whether in source code or any other form. diff --git a/packages/background/Makefile b/packages/background/Makefile index 97eb0362e..01403154e 100644 --- a/packages/background/Makefile +++ b/packages/background/Makefile @@ -3,6 +3,7 @@ TS_NODE_COMPILER_OPTIONS := $(shell echo {\"module\": \"commonjs\" }) BRANCH := $(shell git symbolic-ref --short -q HEAD | sed 's/[\.\/]/-/g') TS_CONFIG_PATHS := true ENVIRONMENT ?= dev +BROWSER ?= chrome export #test: @@ -30,14 +31,26 @@ else npx webpack --config ./webpack/webpack.config.js endif @cp ./src/infrastructure/hardware/trezor/trezor-usb-permissions.html ../../dist/trezor-usb-permissions.html +ifeq ($(BROWSER), firefox) + $(MAKE) copy/firefox +endif + + + +copy/firefox: + @mkdir -p ../../dist-firefox + @cp ../../dist/background.js ../../dist-firefox/background.js + @cp ../../dist/trezor-usb-permissions.html ../../dist-firefox/trezor-usb-permissions.html + @cp -r ../../dist/vendor/* ../../dist-firefox/vendor + depcheck: @npx depcheck -version/patch: +version/patch: @yarn version --patch -version/minor: +version/minor: @yarn version --minor version/major: diff --git a/packages/background/contributing.md b/packages/background/contributing.md index 9a962ec87..759b1de39 100644 --- a/packages/background/contributing.md +++ b/packages/background/contributing.md @@ -2,7 +2,7 @@ ## I don't want to read this whole thing I just have a question -We have an official zendesk board with a detailed [FAQ](https://help.blockwallet.io/) with helpful advice if you have questions . +We have an official website board with detailed [articles](https://blockwallet.io/docs) with helpful advices if you have questions . ## Did you find a bug? diff --git a/packages/background/package.json b/packages/background/package.json index f28fd65f0..5111dfe23 100644 --- a/packages/background/package.json +++ b/packages/background/package.json @@ -1,81 +1,89 @@ { "name": "@block-wallet/background", - "version": "1.1.0", + "version": "1.1.24", "private": true, "dependencies": { - "@block-wallet/chains-assets": "https://github.com/block-wallet/chains-assets#v0.0.27", - "@block-wallet/eth-ledger-bridge-keyring": "https://github.com/block-wallet/eth-ledger-bridge-keyring", + "@block-wallet/chains-assets": "https://github.com/block-wallet/chains-assets#v0.0.63", + "@block-wallet/eth-ledger-bridge-keyring": "https://github.com/block-wallet/eth-ledger-bridge-keyring#v0.14.1", + "eth-trezor-keyring": "https://github.com/block-wallet/eth-trezor-keyring#v0.10.2", "@block-wallet/explorer-link": "https://github.com/block-wallet/explorer-link#v2.2.2", "@block-wallet/remote-configs": "https://github.com/block-wallet/remote-configs#v1.1.0", - "@ethereumjs/tx": "^3.5.2", "@metamask/browser-passworder": "https://github.com/block-wallet/browser-passworder#v1.0.1", - "@metamask/eth-keyring-controller": "^10.0.0", + "@ethereumjs/tx": "^4.0.2", + "@ethereumjs/util": "^8.0.3", + "@keystonehq/bc-ur-registry-eth": "^0.19.1", + "@keystonehq/metamask-airgapped-keyring": "^0.13.1", "@metamask/eth-sig-util": "^5.0.2", - "@unstoppabledomains/resolution": "^8.3.3", - "async-mutex": "^0.3.2", - "bip39": "^3.0.3", - "compare-versions": "^3.6.0", - "eslint-webpack-plugin": "^3.2.0", + "@unstoppabledomains/resolution": "^8.5.0", + "async-mutex": "^0.4.0", + "bip39": "^3.0.4", + "browser-passworder": "^2.0.3", + "compare-versions": "^6.0.0-rc.1", + "eslint-webpack-plugin": "^4.0.0", "eth-ens-namehash": "^2.0.8", - "eth-trezor-keyring": "https://github.com/block-wallet/eth-trezor-keyring#v0.10.2", - "ethereumjs-util": "^7.0.7", - "ethereumjs-wallet": "^1.0.1", - "ethers": "^5.4.0", + "@metamask/eth-keyring-controller": "^10.0.0", + "ethereumjs-wallet": "^1.0.2", + "ethers": "^5.7.0", "lodash": "^4.17.21", - "loglevel": "^1.7.1", + "loglevel": "^1.8.1", "schema-validator": "git+https://github.com/block-wallet/schema-validator.git", - "uuid": "^8.3.2" + "uuid": "^9.0.0", + "webextension-polyfill": "^0.10.0" }, "devDependencies": { - "@types/bn.js": "^5.1.0", - "@types/chai": "^4.2.14", - "@types/chrome": "^0.0.197", + "@types/bn.js": "^5.1.1", + "@types/chai": "^4.3.4", + "@types/chrome": "^0.0.206", "@types/create-hash": "^1.2.2", - "@types/lodash": "^4.14.168", - "@types/mocha": "^8.2.0", - "@types/node": "^16.9.1", - "@types/semver": "^7.3.12", - "@types/sinon": "^9.0.10", - "@types/uuid": "^8.3.4", - "@typescript-eslint/eslint-plugin": "^5.36.2", - "@typescript-eslint/parser": "^5.36.2", + "@types/lodash": "^4.14.191", + "@types/mocha": "^10.0.1", + "@types/node": "^20.6.3", + "@types/semver": "^7.3.13", + "@types/sinon": "^10.0.13", + "@types/uuid": "^9.0.0", + "@types/webextension-polyfill": "^0.10.1", + "@typescript-eslint/eslint-plugin": "^5.48.0", + "@typescript-eslint/parser": "^5.48.0", + "browserify-zlib": "^0.2.0", "buffer": "^6.0.3", - "chai": "^4.2.0", + "chai": "^4.3.7", "crypto-browserify": "^3.12.0", - "dotenv": "8.2.0", + "dotenv": "16.0.3", "dotenv-webpack": "^8.0.1", - "eslint": "^8.22.0", - "eslint-config-prettier": "^8.3.0", + "eslint": "^8.31.0", + "eslint-config-prettier": "^8.6.0", "esm": "^3.2.25", "eth-rpc-errors": "^4.0.3", - "fake-indexeddb": "^4.0.0", - "fs-extra": "^9.0.1", + "fs-extra": "^11.1.0", + "https-browserify": "^1.0.0", "isomorphic-fetch": "^3.0.0", - "jsdom": "^20.0.0", + "jsdom": "^20.0.3", "jsdom-global": "^3.0.2", - "loader-utils": "^2.0.0", - "mocha": "^10.0.0", + "loader-utils": "^3.2.1", + "mocha": "^10.2.0", "mock-require": "^3.0.3", "npm-run-all": "^4.1.5", "nyc": "^15.1.0", - "prettier": "^2.7.1", - "semver": "^7.3.7", - "sinon": "^9.2.3", + "prettier": "^2.8.1", + "semver": "^7.3.8", + "sinon": "^15.0.1", "sinon-chrome": "^3.0.1", "stream-browserify": "^3.0.0", - "ts-loader": "^9.3.1", + "stream-http": "^3.2.0", + "ts-loader": "^9.4.2", "ts-mocha": "^10.0.0", "ts-node": "^10.9.1", "ts-pnp": "1.2.0", - "typescript": "^4.0.3", - "webpack": "^5.74.0", - "webpack-bundle-analyzer": "^4.6.1", - "webpack-cli": "^4.10.0", - "worker-loader": "^3.0.8" + "typescript": "^4.9.4", + "url": "^0.11.3", + "webpack": "^5.75.0", + "webpack-bundle-analyzer": "^4.7.0", + "webpack-cli": "^5.0.1" }, "resolutions": { "webpack/**/glob-parent": "^5.1.2", - "webpack/**/set-value": "^4.0.1" + "webpack/**/set-value": "^4.0.1", + "readable-stream": "^3.6.0" }, "scripts": { "test": "make test/background", diff --git a/packages/background/src/controllers/AccountTrackerController.ts b/packages/background/src/controllers/AccountTrackerController.ts index 5fb05b37b..593893241 100644 --- a/packages/background/src/controllers/AccountTrackerController.ts +++ b/packages/background/src/controllers/AccountTrackerController.ts @@ -1,7 +1,10 @@ import NetworkController, { NetworkEvents } from './NetworkController'; import { BaseController } from '../infrastructure/BaseController'; import { BigNumber } from '@ethersproject/bignumber'; -import { StaticJsonRpcProvider } from '@ethersproject/providers/'; +import { + StaticJsonRpcProvider, + TransactionResponse, +} from '@ethersproject/providers'; import { Zero } from '@ethersproject/constants'; import { ImportStrategy, @@ -14,7 +17,7 @@ import { TokenControllerEvents, } from './erc-20/TokenController'; import { Token } from './erc-20/Token'; -import { toChecksumAddress } from 'ethereumjs-util'; +import { toChecksumAddress } from '@ethereumjs/util'; import { TokenOperationsController } from './erc-20/transactions/TokenOperationsController'; import { Mutex } from 'async-mutex'; import initialState from '../utils/constants/initialState'; @@ -54,7 +57,6 @@ import { WatchedTransactionType, } from './transactions/utils/types'; import { retryHandling } from '../utils/retryHandling'; -import { providers } from 'ethers'; import { RPCLogsFetcher } from '../utils/rpc/RPCLogsFetcher'; import { getTokenApprovalLogsTopics } from '../utils/logsQuery'; import { runPromiseSafely } from '../utils/promises'; @@ -126,6 +128,7 @@ export enum AccountType { HD_ACCOUNT = 'HD Account', LEDGER = 'Ledger', TREZOR = 'Trezor', + KEYSTONE = 'Keystone', EXTERNAL = 'External', } @@ -160,11 +163,20 @@ export interface Accounts { [address: string]: AccountInfo; } +export interface AccountTokenOrder { + [tokenAddress: string]: number; +} + export interface AccountTrackerState { accounts: Accounts; hiddenAccounts: Accounts; isAccountTrackerLoading: boolean; isRefreshingAllowances: boolean; + accountTokensOrder: { + [accountAddress: string]: { + [chainId: number]: AccountTokenOrder; + }; + }; } export enum AccountTrackerEvents { @@ -172,6 +184,7 @@ export enum AccountTrackerEvents { ACCOUNT_REMOVED = 'ACCOUNT_REMOVED', CLEARED_ACCOUNTS = 'CLEARED_ACCOUNTS', BALANCE_UPDATED = 'BALANCE_UPDATED', + ACCOUNTS_ORDER_UPDATED = 'ACCOUNTS_ORDER_UPDATED', } export interface UpdateAccountsOptions { @@ -196,6 +209,7 @@ export class AccountTrackerController extends BaseController(() => + await retryHandling(() => provider.getTransaction(lastTxHash) ); if (oldTtransaction.blockNumber) { @@ -1102,6 +1116,27 @@ export class AccountTrackerController extends BaseController { + const chainId = this._networkController.network.chainId; + const accountAddress = this._preferencesController.getSelectedAddress(); + + this.store.updateState({ + accountTokensOrder: { + ...this.store.getState().accountTokensOrder, + [accountAddress]: { + ...this.store.getState().accountTokensOrder[accountAddress], + [chainId]: tokensOrder, + }, + }, + }); + } + + /** + * orderAccounts + * + * @param accounts array with all the accounts ordered by the user + */ + public orderAccounts(accountsInfo: AccountInfo[]): void { + const accounts = this.store.getState().accounts; + const hiddenAccounts = this.store.getState().hiddenAccounts; + + accountsInfo.forEach((account) => { + const address = account.address; + if (accounts[address]) { + accounts[address] = { + ...accounts[address], + index: account.index, + }; + } + if (hiddenAccounts[address]) { + hiddenAccounts[address] = { + ...hiddenAccounts[address], + index: account.index, + }; + } + }); + + // save accounts state + this.store.updateState({ + accounts: accounts, + hiddenAccounts: hiddenAccounts, + }); + } } diff --git a/packages/background/src/controllers/AddressBookController.ts b/packages/background/src/controllers/AddressBookController.ts index 6ced679cd..3e075d083 100644 --- a/packages/background/src/controllers/AddressBookController.ts +++ b/packages/background/src/controllers/AddressBookController.ts @@ -1,4 +1,4 @@ -import { isValidAddress, toChecksumAddress } from 'ethereumjs-util'; +import { isValidAddress, toChecksumAddress } from '@ethereumjs/util'; import { BaseController } from '../infrastructure/BaseController'; import { compareAddresses, normalizeEnsName } from './transactions/utils/utils'; import NetworkController from './NetworkController'; @@ -9,6 +9,7 @@ import { } from './transactions/utils/types'; import { PreferencesController } from './PreferencesController'; import { isNativeTokenAddress } from '../utils/token'; +import { formatName } from '../utils/account'; /** * @type AddressBookControllerProps @@ -240,6 +241,22 @@ export class AddressBookController extends BaseController { return new Promise((resolve) => { // @ts-ignore - if (chrome.storage.session) { + if (browser.storage.session) { // @ts-ignore - chrome.storage.session.get( - ['sessionToken'], - async ({ sessionToken }: { [key: string]: any }) => { + browser.storage.session + .get(['sessionToken']) + .then(async ({ sessionToken }: { [key: string]: any }) => { if (!sessionToken) { resolve(undefined); } resolve(sessionToken as SessionToken); - } - ); + }); } else { resolve(undefined); } @@ -163,9 +163,9 @@ export default class AppStateController extends BaseController { // @ts-ignore - if (chrome.storage.session) { + if (browser.storage.session) { // @ts-ignore - await chrome.storage.session + await browser.storage.session .set({ sessionToken: { encryptionKey: sessionToken.encryptionKey, diff --git a/packages/background/src/controllers/BlankController.ts b/packages/background/src/controllers/BlankController.ts index 650133f6a..096e87a51 100644 --- a/packages/background/src/controllers/BlankController.ts +++ b/packages/background/src/controllers/BlankController.ts @@ -107,10 +107,22 @@ import { RequestAccountReset, RequestSetDefaultGas, RequestCalculateApproveTransactionGasLimit, + RequestCalculateSwapTransactionGasLimit, RequestApproveAllowance, RequestAddAsNewApproveTransaction, RequestGetExchangeSpender, Origin, + SubmitQRHardwareCryptoHDKeyOrAccountMessage, + SubmitQRHardwareSignatureMessage, + CancelQRHardwareSignRequestMessage, + RequestUpdateTransactionStatus, + AddressType, + RequestSwitchProvider, + RequestIsEnrolled, + RequestSetHotkeys, + RequestTokensOrder, + RequestOrderAccounts, + RequestSetHideSmallBalances, } from '../utils/types/communication'; import EventEmitter from 'events'; @@ -148,7 +160,6 @@ import { ExchangeRatesController } from './ExchangeRatesController'; import { AccountInfo, AccountTrackerController, - AccountType, DeviceAccountInfo, } from './AccountTrackerController'; @@ -158,7 +169,10 @@ import { TokenControllerProps, NATIVE_TOKEN_ADDRESS, } from './erc-20/TokenController'; -import SwapController, { SwapParameters, SwapQuote } from './SwapController'; +import SwapController, { + SwapParameters, + SwapQuoteResponse, +} from './SwapController'; import { FetchTokenResponse, IToken, @@ -182,10 +196,8 @@ import { AddressBookEntry, NetworkAddressBook, } from './AddressBookController'; -import { Devices } from '../utils/types/hardware'; import KeyringControllerDerivated from './KeyringControllerDerivated'; -import { showSetUpCompleteNotification } from '../utils/notifications'; import { extensionInstances } from '../infrastructure/connection'; import { focusWindow, @@ -231,6 +243,10 @@ import RemoteConfigsController, { RemoteConfigsControllerState, } from './RemoteConfigsController'; import { ApproveTransaction } from './erc-20/transactions/ApproveTransaction'; +import CampaignsController from './CampaignsController'; +import { NotificationController } from './NotificationController'; +import browser from 'webextension-polyfill'; +import OnrampController from './OnrampController'; export interface BlankControllerProps { initState: BlankAppState; @@ -271,6 +287,9 @@ export default class BlankController extends EventEmitter { private readonly transactionWatcherController: TransactionWatcherController; private readonly tokenAllowanceController: TokenAllowanceController; private readonly remoteConfigsController: RemoteConfigsController; + private readonly campaignsController: CampaignsController; + private readonly notificationController: NotificationController; + private readonly onrampController: OnrampController; // Stores private readonly store: ComposedStore; @@ -278,10 +297,11 @@ export default class BlankController extends EventEmitter { private readonly _devTools: any; - private subscriptions: Record; + private subscriptions: Record; private isSetupComplete: boolean; constructor(props: BlankControllerProps) { + console.log('blank controller constructor'); super(); const initState = props.initState; @@ -364,8 +384,11 @@ export default class BlankController extends EventEmitter { this.gasPricesController, this.tokenController, this.blockUpdatesController, + this.keyringController, initState.TransactionController, - this.keyringController.signTransaction.bind(this.keyringController) + this.keyringController.signEthTransaction.bind( + this.keyringController + ) ); this.privacyController = new PrivacyAsyncController({ @@ -400,6 +423,11 @@ export default class BlankController extends EventEmitter { initState.AccountTrackerController ); + this.campaignsController = new CampaignsController( + this.accountTrackerController, + initState.CampaignsController + ); + this.exchangeRatesController = new ExchangeRatesController( initState.ExchangeRatesController, this.preferencesController, @@ -430,7 +458,8 @@ export default class BlankController extends EventEmitter { this.networkController, this.transactionController, this.tokenController, - this.tokenAllowanceController + this.tokenAllowanceController, + this.gasPricesController ); this.bridgeController = new BridgeController( @@ -458,6 +487,17 @@ export default class BlankController extends EventEmitter { preferencesController: this.preferencesController, }); + this.onrampController = new OnrampController(this.networkController); + + this.notificationController = new NotificationController( + this.preferencesController, + this.transactionWatcherController, + this.transactionController, + this.accountTrackerController, + this.addressBookController, + this.ensController + ); + this.store = new ComposedStore({ NetworkController: this.networkController.store, AppStateController: this.appStateController.store, @@ -478,6 +518,7 @@ export default class BlankController extends EventEmitter { TransactionWatcherControllerState: this.transactionWatcherController.store, BridgeController: this.bridgeController.store, + CampaignsController: this.campaignsController.store, }); this.UIStore = new ComposedStore({ @@ -498,6 +539,7 @@ export default class BlankController extends EventEmitter { BlankProviderController: this.blankProviderController.store, SwapController: this.swapController.UIStore, BridgeController: this.bridgeController.UIStore, + OnrampController: this.onrampController.store, }); // Check controllers on app lock/unlock @@ -529,6 +571,8 @@ export default class BlankController extends EventEmitter { // mv3 auto unlock this.appStateController.autoUnlock(); + + console.log('blank controller constructor finished'); } /** @@ -560,6 +604,16 @@ export default class BlankController extends EventEmitter { isAppUnlocked, activeSubscription ); + + this.networkController.setActiveSubscriptions( + isAppUnlocked, + activeSubscription + ); + + this.gasPricesController.manageActiveSubscriptions( + isAppUnlocked, + activeSubscription + ); } /** @@ -605,7 +659,7 @@ export default class BlankController extends EventEmitter { */ private createSubscription( id: string, - port: chrome.runtime.Port + port: browser.Runtime.Port ): (data: SubscriptionMessageTypes[TMessageType]) => void { this.subscriptions[id] = port; @@ -613,8 +667,33 @@ export default class BlankController extends EventEmitter { this.manageControllers(); return (subscription: unknown): void => { - if (this.subscriptions[id]) { - port.postMessage({ id, subscription }); + try { + if (this.subscriptions[id]) { + // fixing 'DataCloneError' error + // https://stackoverflow.com/questions/68467946/datacloneerror-the-object-could-not-be-cloned-firefox-browser + const message = { + id, + subscription: + subscription && typeof subscription !== undefined + ? JSON.parse(JSON.stringify(subscription)) + : subscription, + }; + + port.postMessage(message); + } + } catch (err) { + const safeError = toError(err); + log.error('[err]', safeError.message); + if ( + safeError.message + .toLowerCase() + .includes( + 'attempting to use a disconnected port object' + ) + ) { + port.disconnect(); + this.unsubscribe(id); + } } }; } @@ -643,7 +722,7 @@ export default class BlankController extends EventEmitter { */ public handler( { id, message, request }: TransportRequestMessage, - port: chrome.runtime.Port, + port: browser.Runtime.Port, portId: string ): void { let isPortConnected = true; @@ -651,7 +730,8 @@ export default class BlankController extends EventEmitter { const source = `${from}: ${id}: ${message}`; port.onDisconnect.addListener(() => { - const error = chrome.runtime.lastError; + this.unsubscribe(id); + const error = browser.runtime.lastError; isPortConnected = false; if (error) { log.error(error); @@ -670,14 +750,28 @@ export default class BlankController extends EventEmitter { throw new Error('Port has been disconnected'); } - port.postMessage({ id, response }); + // fixing 'DataCloneError' error + // https://stackoverflow.com/questions/68467946/datacloneerror-the-object-could-not-be-cloned-firefox-browser + const message = { + id, + response: + response && typeof response !== undefined + ? JSON.parse(JSON.stringify(response)) + : response, + }; + try { + port.postMessage(message); + } catch (error: any) { + log.warn(message, error); + throw error; + } }) .catch((error: unknown): void => { // Always pass an error object to the client const safeError = toError(error); log.error('[err]', source, safeError.message); - + this.blankProviderController.cancelPendingDAppRequests(); // only send message back to port if it's still connected if (isPortConnected) { port.postMessage({ @@ -703,10 +797,12 @@ export default class BlankController extends EventEmitter { id: string, type: MessageTypes, request: RequestTypes[MessageTypes], - port: chrome.runtime.Port, + port: browser.Runtime.Port, portId: string ): Promise> { switch (type) { + case Messages.ADDRESS.GET_TYPE: + return this.getAddressType(request as string); case Messages.ACCOUNT.CREATE: return this.accountCreate(request as RequestAccountCreate); case Messages.ACCOUNT.EXPORT_JSON: @@ -745,6 +841,8 @@ export default class BlankController extends EventEmitter { ); case Messages.ACCOUNT.REFRESH_TOKEN_ALLOWANCES: return this.refreshAccountTokenAllowances(); + case Messages.ACCOUNT.ORDER_ACCOUNTS: + return this.orderAccounts(request as RequestOrderAccounts); case Messages.APP.GET_IDLE_TIMEOUT: return this.getIdleTimeout(); case Messages.APP.SET_IDLE_TIMEOUT: @@ -818,6 +916,8 @@ export default class BlankController extends EventEmitter { return this.setProviderIcon(request as RequestSetIcon, portId); case Messages.EXTERNAL.GET_PROVIDER_CONFIG: return this.getProviderRemoteConfig(); + case Messages.EXTERNAL.IS_ENROLLED: + return this.isEnrolledInCampaign(request as RequestIsEnrolled); case Messages.NETWORK.CHANGE: return this.networkChange(request as RequestNetworkChange); case Messages.NETWORK.SET_SHOW_TEST_NETWORKS: @@ -834,6 +934,8 @@ export default class BlankController extends EventEmitter { ); case Messages.NETWORK.REMOVE_NETWORK: return this.removeNetwork(request as RequestRemoveNetwork); + case Messages.NETWORK.SWITCH_PROVIDER: + return this.switchProvider(request as RequestSwitchProvider); case Messages.NETWORK.GET_SPECIFIC_CHAIN_DETAILS: return this.getChainData(request as RequestGetChainData); case Messages.NETWORK.GET_DEFAULT_RPC: @@ -878,6 +980,10 @@ export default class BlankController extends EventEmitter { return this.rejectTransaction( request as RequestRejectTransaction ); + case Messages.TRANSACTION.UPDATE_STATUS: + return this.updateTransactionStatus( + request as RequestUpdateTransactionStatus + ); case Messages.TRANSACTION.REJECT_REPLACEMENT_TRANSACTION: return this.rejectReplacementTransaction( request as RequestRejectTransaction @@ -890,6 +996,8 @@ export default class BlankController extends EventEmitter { return this.udResolve(request as RequestUDResolve); case Messages.TRANSACTION.GET_LATEST_GAS_PRICE: return this.getLatestGasPrice(); + case Messages.TRANSACTION.UPDATE_GAS_PRICE: + return this.updateGasPrices(); case Messages.TRANSACTION.FETCH_LATEST_GAS_PRICE: return this.fetchLatestGasPriceForChain(request as number); case Messages.TRANSACTION.SEND_ETHER: @@ -922,6 +1030,10 @@ export default class BlankController extends EventEmitter { return this.calculateSendTransactionGasLimit( request as RequestCalculateSendTransactionGasLimit ); + case Messages.TRANSACTION.CALCULATE_SWAP_TRANSACTION_GAS_LIMIT: + return this.calculateSwapTransactionGasLimit( + request as RequestCalculateSwapTransactionGasLimit + ); case Messages.TRANSACTION.CANCEL_TRANSACTION: return this.cancelTransaction( request as RequestCancelTransaction @@ -1050,6 +1162,18 @@ export default class BlankController extends EventEmitter { return this.removeHardwareWallet( request as RequestRemoveHardwareWallet ); + case Messages.WALLET.HARDWARE_QR_SUBMIT_CRYPTO_HD_KEY_OR_ACCOUNT: + return this.hardwareQrSubmitCryptoHdKeyOrAccount( + request as SubmitQRHardwareCryptoHDKeyOrAccountMessage + ); + case Messages.WALLET.HARDWARE_QR_SUBMIT_SIGNATURE: + return this.hardwareQrSubmitSignature( + request as SubmitQRHardwareSignatureMessage + ); + case Messages.WALLET.HARDWARE_QR_CANCEL_SIGN_REQUEST: + return this.hardwareQrCancelSignRequest( + request as CancelQRHardwareSignRequestMessage + ); case Messages.APP.OPEN_HW_CONNECT: return this.openHardwareConnect(); case Messages.APP.OPEN_HW_REMOVE: @@ -1084,6 +1208,20 @@ export default class BlankController extends EventEmitter { ); case Messages.BROWSER.GET_WINDOW_ID: return getCurrentWindowId(); + case Messages.WALLET.SET_HOTKEYS_ENABLED: + return this.setHotkeysStatus(request as RequestSetHotkeys); + case Messages.WALLET.GET_ONRAMP_CURRENCIES: + return this.getOnrampCurrencies(); + case Messages.ACCOUNT.EDIT_ACCOUNT_TOKENS_ORDER: + return this.editAccountTokensOrder( + request as RequestTokensOrder + ); + case Messages.ACCOUNT.SET_ACCOUNT_SORT_VALUE: + return this.setAccountTokensSortValue(request as string); + case Messages.WALLET.SET_HIDESMALLBALANCES: + return this.setHideSmallBalances( + request as RequestSetHideSmallBalances + ); default: throw new Error(`Unable to handle message of type ${type}`); } @@ -1552,7 +1690,6 @@ export default class BlankController extends EventEmitter { * Method to reject transaction proposed by external source * * @param transactionMeta - transaction data - * @param tabId - id of the tab where the extension is opened (needed to close the window) */ private rejectTransaction = async ({ transactionId, @@ -1560,6 +1697,22 @@ export default class BlankController extends EventEmitter { return this.transactionController.rejectTransaction(transactionId); }; + /** + * Method to update transaction status + * + * @param transactionId - transaction id + * @param tabId - id of the tab where the extension is opened (needed to close the window) + */ + private updateTransactionStatus = async ({ + transactionId, + status, + }: RequestUpdateTransactionStatus) => { + return this.transactionController.updateTransactionStatus( + transactionId, + status + ); + }; + /** * Method to reject a speedUp/cancel transaction * @@ -1662,7 +1815,7 @@ export default class BlankController extends EventEmitter { private async getExchangeQuote({ exchangeType, quoteParams, - }: RequestGetExchangeQuote): Promise { + }: RequestGetExchangeQuote): Promise { return this.swapController.getExchangeQuote(exchangeType, quoteParams); } @@ -1896,17 +2049,21 @@ export default class BlankController extends EventEmitter { blockExplorerUrl, currencySymbol, test, + switchToNetwork, }: RequestAddNetwork): Promise { - return this.networkController.addNetwork({ - chainId: Number(chainId), - chainName: name, - rpcUrls: [rpcUrl], - blockExplorerUrls: [blockExplorerUrl], - nativeCurrency: { - symbol: currencySymbol, + return this.networkController.addNetwork( + { + chainId: Number(chainId), + chainName: name, + rpcUrls: [rpcUrl], + blockExplorerUrls: [blockExplorerUrl], + nativeCurrency: { + symbol: currencySymbol, + }, + test: test, }, - test: test, - }); + switchToNetwork + ); } /** @@ -1967,6 +2124,26 @@ export default class BlankController extends EventEmitter { )?.defaultRpcUrl; } + /** + * switchProvider + * + * @param chainId chain identifier of the network + * @param providerType provider type {default, backup, custom} + * @param customRpcUrl custom rpc url of the network + * + */ + private async switchProvider({ + chainId, + providerType, + customRpcUrl, + }: RequestSwitchProvider): Promise { + return this.networkController.switchProvider( + chainId, + providerType, + customRpcUrl + ); + } + /** * getRpcChainId * @@ -2049,6 +2226,10 @@ export default class BlankController extends EventEmitter { return this.remoteConfigsController.providerConfig; } + private async isEnrolledInCampaign(r: RequestIsEnrolled): Promise { + return this.campaignsController.isEnrolled(r.campaignId); + } + /** * Resolve ENS name * @@ -2285,6 +2466,14 @@ export default class BlankController extends EventEmitter { return BigNumber.from(this.gasPricesController.getFeeData().gasPrice!); } + /** + * Updates the gas price levels + */ + private async updateGasPrices() { + const currentBlockNumber = this.blockUpdatesController.getBlockNumber(); + this.gasPricesController.updateGasPrices(currentBlockNumber); + } + /** * It returns the current network latest gas price by fetching it from the Fee service or network */ @@ -2313,6 +2502,15 @@ export default class BlankController extends EventEmitter { }); } + /** + * Calculate the gas limit for a Swap transaction + */ + private async calculateSwapTransactionGasLimit({ + tx, + }: RequestCalculateSwapTransactionGasLimit): Promise { + return this.swapController.estimateSwapGas(tx); + } + private cancelTransaction({ transactionId, gasValues, @@ -2402,10 +2600,14 @@ export default class BlankController extends EventEmitter { if (isNativeToken) { // Native Token and Not a custom network, returns SEND_GAS_COST const. if (hasFixedGasCost) { - return { - gasLimit: BigNumber.from(SEND_GAS_COST), - estimationSucceeded: true, - }; + const isContract = + await this.networkController.isAddressContract(to); + if (!isContract) { + return { + gasLimit: BigNumber.from(SEND_GAS_COST), + estimationSucceeded: true, + }; + } } // Native token of a custom network, estimets gas with fallback price. @@ -2647,7 +2849,7 @@ export default class BlankController extends EventEmitter { }: RequestCompleteSetup): Promise { if (!this.isSetupComplete) { if (sendNotification) { - showSetUpCompleteNotification(); + this.notificationController.showSetUpCompleteNotification(); } this.isSetupComplete = true; } @@ -2657,7 +2859,7 @@ export default class BlankController extends EventEmitter { * State subscription method * */ - private stateSubscribe(id: string, port: chrome.runtime.Port): boolean { + private stateSubscribe(id: string, port: browser.Runtime.Port): boolean { const cb = this.createSubscription( id, port @@ -2684,7 +2886,7 @@ export default class BlankController extends EventEmitter { */ private blankProviderEventSubscribe( id: string, - port: chrome.runtime.Port, + port: browser.Runtime.Port, portId: string ): boolean { const cb = this.createSubscription< @@ -3222,7 +3424,7 @@ export default class BlankController extends EventEmitter { device, }: RequestRemoveHardwareWallet): Promise { const accountType = - device === Devices.LEDGER ? AccountType.LEDGER : AccountType.TREZOR; + this.accountTrackerController.getAccountTypeFromDevice(device); const removeAccountPromises: Promise[] = []; const accounts = @@ -3248,6 +3450,47 @@ export default class BlankController extends EventEmitter { return true; } + private async hardwareQrSubmitCryptoHdKeyOrAccount({ + ur, + }: SubmitQRHardwareCryptoHDKeyOrAccountMessage): Promise { + try { + if (ur.type === 'crypto-hdkey') { + await this.keyringController.submitQRHardwareCryptoHDKey( + ur.cbor + ); + } else { + await this.keyringController.submitQRHardwareCryptoAccount( + ur.cbor + ); + } + return true; + } catch (err) { + log.error(err); + return false; + } + } + + private async hardwareQrSubmitSignature({ + requestId, + ur, + }: SubmitQRHardwareSignatureMessage): Promise { + try { + this.keyringController.submitQRHardwareSignature( + requestId, + Buffer.from(ur.cbor, 'hex') + ); + return true; + } catch (err) { + log.error(err); + return false; + } + } + + private async hardwareQrCancelSignRequest({}: CancelQRHardwareSignRequestMessage): Promise { + this.keyringController.cancelQRHardwareSignRequest(); + return true; + } + /* * Get the specificrelease note of a version * @returns the notes (if exists) of the version @@ -3258,4 +3501,81 @@ export default class BlankController extends EventEmitter { }: RequestGenerateOnDemandReleaseNotes): Promise { return generateOnDemandReleaseNotes(version); } + + /** + * Get Address type (normal, native, smart contract, erc20) + * @param address - hex address + * @returns AddressType + */ + private async getAddressType(address: string): Promise { + if (isNativeTokenAddress(address)) return AddressType.NULL; + + const isContract = await this.networkController.isAddressContract( + address + ); + if (isContract) { + const tokenSearch = await this.tokenController.search(address); + if (tokenSearch.tokens.length > 0 && tokenSearch.tokens[0].symbol) + return AddressType.ERC20; + return AddressType.SMART_CONTRACT; + } + + return AddressType.NORMAL; + } + + /** Set hotkeys enabled/disabled + * + * @param enabled indicates if the extension can use hotkeys + */ + private setHotkeysStatus({ enabled }: RequestSetHotkeys) { + this.preferencesController.hotkeysStatus = enabled; + } + + /** Get onramp currencies + * + */ + private getOnrampCurrencies() { + return this.onrampController.getCurrencies(); + } + + /** + * editAccountTokensOrder + * + * @param address The address identifier of the token contract + * @param order Order of token + */ + private async editAccountTokensOrder( + tokensOrder: RequestTokensOrder + ): Promise { + return this.accountTrackerController.editAccountTokensOrder( + tokensOrder + ); + } + + /** Set tokens list default sort value + * + * @param tokensSortValue indicates which sort value we will use + */ + private setAccountTokensSortValue(tokensSortValue: string) { + this.preferencesController.tokensSortValue = tokensSortValue; + } + + /** + * orderAccounts + * + * @param accounts array with all the accounts ordered by the user + */ + private async orderAccounts({ + accountsInfo, + }: RequestOrderAccounts): Promise { + this.accountTrackerController.orderAccounts(accountsInfo); + } + + /** Set hideSmallBalances enabled/disabled + * + * @param enabled indicates if the extension show token with balance < 0.01 USD + */ + private setHideSmallBalances({ enabled }: RequestSetHideSmallBalances) { + this.preferencesController.hideSmallBalances = enabled; + } } diff --git a/packages/background/src/controllers/BlankProviderController.ts b/packages/background/src/controllers/BlankProviderController.ts index a0828aad7..c43944ff4 100644 --- a/packages/background/src/controllers/BlankProviderController.ts +++ b/packages/background/src/controllers/BlankProviderController.ts @@ -77,7 +77,9 @@ import { validateChainId, } from '../utils/ethereumChain'; import log from 'loglevel'; -import KeyringControllerDerivated from './KeyringControllerDerivated'; +import KeyringControllerDerivated, { + KeyringControllerEvents, +} from './KeyringControllerDerivated'; import { randomBytes } from '../utils/randomBytes'; import BlockUpdatesController, { BlockUpdatesEvents, @@ -105,7 +107,6 @@ import { SignTimeoutError, } from '../utils/hardware'; import { GasPricesController } from './GasPricesController'; -import { bnGreaterThanZero } from '../utils/bnUtils'; import { recoverPersonalSignature } from '@metamask/eth-sig-util'; import checksummedAddress from '../utils/checksummedAddress'; @@ -300,13 +301,20 @@ export default class BlankProviderController extends BaseController { + this.removeAllListeners( + KeyringControllerEvents.QR_MESSAGE_SIGNATURE_REQUEST_GENERATED + ); + }; try { + __clearListeners(); + if (!isAccepted) { throw new Error(ProviderError.USER_REJECTED_REQUEST); } @@ -1096,6 +1105,18 @@ export default class BlankProviderController extends BaseController { + this.updateDappRequest(reqId, { + qrParams: { + requestId, + qrSignRequest, + }, + }); + } + ); + signedMessage = await new Promise((resolve, reject) => { intervalRef = setInterval(() => { if (this._isDappRequestRejected(reqId)) { @@ -1130,6 +1151,7 @@ export default class BlankProviderController extends BaseController { __clearTimeouts(); + __clearListeners(); const { dappRequests } = this.store.getState(); if (reqId in dappRequests) { // At this point the promise has been already rejected by the setInterval function. @@ -1144,6 +1166,7 @@ export default class BlankProviderController extends BaseController { __clearTimeouts(); + __clearListeners(); const { dappRequests } = this.store.getState(); if (reqId in dappRequests) { reject(e); @@ -1174,6 +1197,7 @@ export default class BlankProviderController extends BaseController { - const isConnected = isProviderNetworkOnline && isUserNetworkOnline; + const isConnected = isCurrentProviderOnline && isUserNetworkOnline; if (isConnected !== this._isConnected) { this._isConnected = isConnected; diff --git a/packages/background/src/controllers/BridgeController.ts b/packages/background/src/controllers/BridgeController.ts index 55188a24d..7a176d60e 100644 --- a/packages/background/src/controllers/BridgeController.ts +++ b/packages/background/src/controllers/BridgeController.ts @@ -42,7 +42,7 @@ import { TransactionByHash } from './TransactionWatcherController'; import { sleep } from '../utils/sleep'; import { HOUR, MILISECOND, MINUTE, SECOND } from '../utils/constants/time'; import { TransactionReceipt } from '@ethersproject/providers'; -import { toChecksumAddress } from 'ethereumjs-util'; +import { toChecksumAddress } from '@ethereumjs/util'; import { fetchBlockWithRetries } from '../utils/blockFetch'; import { isNil } from 'lodash'; import { BaseController } from '../infrastructure/BaseController'; @@ -363,7 +363,9 @@ export default class BridgeController extends BaseController< }; } - const logoUrl = targetNetwork.iconUrls + const logoUrl = targetNetwork.nativeCurrency.logo + ? targetNetwork.nativeCurrency.logo + : targetNetwork.iconUrls ? targetNetwork.iconUrls[0] : token.logo; diff --git a/packages/background/src/controllers/CampaignsController.ts b/packages/background/src/controllers/CampaignsController.ts new file mode 100644 index 000000000..2601803db --- /dev/null +++ b/packages/background/src/controllers/CampaignsController.ts @@ -0,0 +1,70 @@ +import { BaseController } from '../infrastructure/BaseController'; +import { AccountTrackerController } from './AccountTrackerController'; +import httpClient from '../utils/http'; +import { retryHandling } from '../utils/retryHandling'; + +const CAMPAIGNS_SERVICE_URL = 'https://campaigns.blockwallet.io/v1'; + +export interface CampaignsControllerState { + enrollments: { + [campaingId: string]: { + isEnrolled: boolean; + accountAddress: string; + }; + }; +} + +const toLower = (a: string) => a.toLowerCase(); + +export default class CampaignsController extends BaseController< + CampaignsControllerState, + undefined +> { + constructor( + private _accountTrackerController: AccountTrackerController, + initialState?: CampaignsControllerState + ) { + super(initialState || { enrollments: {} }); + } + + public async isEnrolled(campaignId: string): Promise { + const { enrollments } = this.store.getState(); + const campaignEnrollments = enrollments[campaignId]; + if (campaignEnrollments && campaignEnrollments.isEnrolled) { + return true; + } + + const accountAddresses = this._accountTrackerController + .getAllAccountAddresses() + .map(toLower); + + type CampaignAccountsResponse = { + accounts: string[]; + }; + + const enrolledAccounts = await retryHandling( + () => + httpClient.request( + `${CAMPAIGNS_SERVICE_URL}/api/campaigns/${campaignId}/accounts` + ) + ); + + const enrolledAccount = enrolledAccounts.accounts.find((acc) => + accountAddresses.includes(acc.toLowerCase()) + ); + + if (enrolledAccount) { + this.store.updateState({ + enrollments: { + ...this.store.getState().enrollments, + [campaignId]: { + isEnrolled: true, + accountAddress: enrolledAccount, + }, + }, + }); + } + + return !!enrolledAccount; + } +} diff --git a/packages/background/src/controllers/ExchangeRatesController.ts b/packages/background/src/controllers/ExchangeRatesController.ts index 8ec51e022..06c8614c3 100644 --- a/packages/background/src/controllers/ExchangeRatesController.ts +++ b/packages/background/src/controllers/ExchangeRatesController.ts @@ -18,20 +18,17 @@ import { ActionIntervalController } from './block-updates/ActionIntervalControll import BlockUpdatesController, { BlockUpdatesEvents, } from './block-updates/BlockUpdatesController'; -import httpClient from '../utils/http'; import { getRateService, RateService, - BaseApiEndpoint, chainLinkService, - coingekoService, + getCoingeckoService, } from '../utils/rateService'; import { AccountTrackerController, AccountTrackerEvents, } from './AccountTrackerController'; import { isNativeTokenAddress } from '../utils/token'; - export interface ExchangeRatesControllerState { exchangeRates: Rates; networkNativeCurrency: { @@ -93,7 +90,14 @@ export class ExchangeRatesController extends BaseController { + await this.updateExchangeRates(); + } + ); } catch (error) { log.error(error); } finally { @@ -218,12 +222,12 @@ export class ExchangeRatesController extends BaseController => { const tokens = { ...this.staticTokens, ...this.getTokens() }; - const tokenContracts = Object.keys(tokens).join(','); - - const query = `${BaseApiEndpoint}token_price/${this.networkNativeCurrency.coingeckoPlatformId}`; - - return httpClient.get(query, { - contract_addresses: tokenContracts, - vs_currencies: this._preferencesController.nativeCurrency, - }); + const service = getCoingeckoService(); + return service.getTokensRates( + this.networkNativeCurrency.coingeckoPlatformId, + Object.keys(tokens), + this._preferencesController.nativeCurrency + ); }; /** diff --git a/packages/background/src/controllers/GasPricesController.ts b/packages/background/src/controllers/GasPricesController.ts index fd458b434..67c43e8c6 100644 --- a/packages/background/src/controllers/GasPricesController.ts +++ b/packages/background/src/controllers/GasPricesController.ts @@ -17,9 +17,11 @@ import BlockUpdatesController, { import httpClient from '../utils/http'; import { MILISECOND } from '../utils/constants/time'; import { retryHandling } from '../utils/retryHandling'; +import { formatUnits } from 'ethers/lib/utils'; const CHAIN_FEE_DATA_SERVICE_URL = 'https://chain-fee.blockwallet.io/v1'; const BLOCKS_TO_WAIT_BEFORE_CHECKING_FOR_CHAIN_SUPPORT = 100; +const CHAIN_FEE_CUSTOM_HEADER = { wallet: 'BlockWallet' }; const API_CALLS_DELAY = 100 * MILISECOND; const API_CALLS_RETRIES = 5; @@ -123,7 +125,11 @@ export class GasPricesController extends BaseController { - this.updateGasPrices(network.chainId); + const currentBlockNumber = + this._blockUpdatesController.getBlockNumber( + network.chainId + ); + this.updateGasPrices(currentBlockNumber, network.chainId); } ); @@ -147,6 +153,25 @@ export class GasPricesController extends BaseController => { try { const oldGasPriceLevels = this.getGasPricesLevels(chainId); + const isEIP1559Compatible = - await this._networkController.getEIP1559Compatibility(chainId); + await this._networkController.getEIP1559Compatibility( + chainId, + false, + this.getState().baseFee + ); const newGasPriceLevels = await this._fetchFeeData( isEIP1559Compatible, @@ -293,10 +343,10 @@ export class GasPricesController extends BaseController { - let gasPriceData: GasPriceData = {} as GasPriceData; - - // Fetch the service to detect if the chain has support. - try { - // If the chain has support request the service - const feeDataResponse = await retryHandling( - () => - httpClient.get( - `${CHAIN_FEE_DATA_SERVICE_URL}/fee_data`, - { - c: chainId, - } - ), - API_CALLS_DELAY, - API_CALLS_RETRIES - ); - - if (feeDataResponse) { - // Parsing the gas result considering the EIP1559 status - // for the case of fantom(250) we will detect that the network is EIP1559 but the service - // won't return gas with that format because eth_feeHistory is not available. - if ( - isEIP1559Compatible && - 'baseFee' in feeDataResponse && - feeDataResponse.baseFee - ) { - gasPriceData = { - blockGasLimit: BigNumber.from( - feeDataResponse.blockGasLimit - ), - baseFee: BigNumber.from(feeDataResponse.baseFee), - estimatedBaseFee: BigNumber.from( - feeDataResponse.estimatedBaseFee - ), - gasPricesLevels: { - slow: { - gasPrice: null, - maxFeePerGas: BigNumber.from( - feeDataResponse.gasPricesLevels.slow - .maxFeePerGas - ), - maxPriorityFeePerGas: BigNumber.from( - feeDataResponse.gasPricesLevels.slow - .maxPriorityFeePerGas - ), - lastBaseFeePerGas: BigNumber.from( - feeDataResponse.baseFee - ), - }, - average: { - gasPrice: null, - maxFeePerGas: BigNumber.from( - feeDataResponse.gasPricesLevels.average - .maxFeePerGas - ), - maxPriorityFeePerGas: BigNumber.from( - feeDataResponse.gasPricesLevels.average - .maxPriorityFeePerGas - ), - lastBaseFeePerGas: BigNumber.from( - feeDataResponse.baseFee - ), - }, - fast: { - gasPrice: null, - maxFeePerGas: BigNumber.from( - feeDataResponse.gasPricesLevels.fast - .maxFeePerGas - ), - maxPriorityFeePerGas: BigNumber.from( - feeDataResponse.gasPricesLevels.fast - .maxPriorityFeePerGas - ), - lastBaseFeePerGas: BigNumber.from( - feeDataResponse.baseFee - ), - }, - }, - }; - } else { - gasPriceData = { - blockGasLimit: BigNumber.from( - feeDataResponse.blockGasLimit - ), - gasPricesLevels: { - slow: { - gasPrice: BigNumber.from( - feeDataResponse.gasPricesLevels.slow - .gasPrice - ), - maxFeePerGas: null, - maxPriorityFeePerGas: null, - lastBaseFeePerGas: null, - }, - average: { - gasPrice: BigNumber.from( - feeDataResponse.gasPricesLevels.average - .gasPrice - ), - maxFeePerGas: null, - maxPriorityFeePerGas: null, - lastBaseFeePerGas: null, - }, - fast: { - gasPrice: BigNumber.from( - feeDataResponse.gasPricesLevels.fast - .gasPrice - ), - maxFeePerGas: null, - maxPriorityFeePerGas: null, - lastBaseFeePerGas: null, - }, - }, - }; - } - - return gasPriceData; - } - } catch (error) { - log.error('error calling chain fees service', error); - } - return undefined; - } - /** * Fetches the fee's service to get the current gas prices. * If the service is not available or the chain is not supported then @@ -597,10 +512,7 @@ export class GasPricesController extends BaseController => { + let gasPriceData: GasPriceData = {} as GasPriceData; + + // Fetch the service to detect if the chain has support. + try { + // If the chain has support request the service + const feeDataResponse = await retryHandling( + () => + httpClient.request( + `${CHAIN_FEE_DATA_SERVICE_URL}/fee_data`, + { + params: { + c: chainId, + }, + headers: CHAIN_FEE_CUSTOM_HEADER, + } + ), + apiCallsDelay, + apiCallsRetries + ); + + if (feeDataResponse) { + // Parsing the gas result considering the EIP1559 status + // for the case of fantom(250) we will detect that the network is EIP1559 but the service + // won't return gas with that format because eth_feeHistory is not available. + if ('baseFee' in feeDataResponse && feeDataResponse.baseFee) { + gasPriceData = { + blockGasLimit: BigNumber.from( + feeDataResponse.blockGasLimit + ), + baseFee: BigNumber.from(feeDataResponse.baseFee), + estimatedBaseFee: BigNumber.from( + feeDataResponse.estimatedBaseFee + ), + gasPricesLevels: { + slow: { + gasPrice: null, + maxFeePerGas: BigNumber.from( + feeDataResponse.gasPricesLevels.slow + .maxFeePerGas + ), + maxPriorityFeePerGas: BigNumber.from( + feeDataResponse.gasPricesLevels.slow + .maxPriorityFeePerGas + ), + lastBaseFeePerGas: BigNumber.from( + feeDataResponse.baseFee + ), + }, + average: { + gasPrice: null, + maxFeePerGas: BigNumber.from( + feeDataResponse.gasPricesLevels.average + .maxFeePerGas + ), + maxPriorityFeePerGas: BigNumber.from( + feeDataResponse.gasPricesLevels.average + .maxPriorityFeePerGas + ), + lastBaseFeePerGas: BigNumber.from( + feeDataResponse.baseFee + ), + }, + fast: { + gasPrice: null, + maxFeePerGas: BigNumber.from( + feeDataResponse.gasPricesLevels.fast + .maxFeePerGas + ), + maxPriorityFeePerGas: BigNumber.from( + feeDataResponse.gasPricesLevels.fast + .maxPriorityFeePerGas + ), + lastBaseFeePerGas: BigNumber.from( + feeDataResponse.baseFee + ), + }, + }, + }; + } else { + gasPriceData = { + blockGasLimit: BigNumber.from( + feeDataResponse.blockGasLimit + ), + gasPricesLevels: { + slow: { + gasPrice: BigNumber.from( + feeDataResponse.gasPricesLevels.slow.gasPrice + ), + maxFeePerGas: null, + maxPriorityFeePerGas: null, + lastBaseFeePerGas: null, + }, + average: { + gasPrice: BigNumber.from( + feeDataResponse.gasPricesLevels.average.gasPrice + ), + maxFeePerGas: null, + maxPriorityFeePerGas: null, + lastBaseFeePerGas: null, + }, + fast: { + gasPrice: BigNumber.from( + feeDataResponse.gasPricesLevels.fast.gasPrice + ), + maxFeePerGas: null, + maxPriorityFeePerGas: null, + lastBaseFeePerGas: null, + }, + }, + }; + } + + return gasPriceData; + } + } catch (error) { + log.error('error calling chain fees service', error); + } + return undefined; +}; diff --git a/packages/background/src/controllers/KeyringControllerDerivated.ts b/packages/background/src/controllers/KeyringControllerDerivated.ts index a0e22cd84..260a6cd46 100644 --- a/packages/background/src/controllers/KeyringControllerDerivated.ts +++ b/packages/background/src/controllers/KeyringControllerDerivated.ts @@ -12,10 +12,42 @@ import TrezorKeyring from 'eth-trezor-keyring'; import { Devices } from '../utils/types/hardware'; import log from 'loglevel'; import { HDPaths, BIP44_PATH } from '../utils/types/hardware'; -import { TypedTransaction } from '@ethereumjs/tx'; import { isManifestV3 } from '../utils/manifest'; -import { bufferToHex } from 'ethereumjs-util'; +import { + AccessListEIP2930Transaction, + Transaction, + TypedTransaction, + FeeMarketEIP1559Transaction, +} from '@ethereumjs/tx'; +import { concatSig, SignTypedDataVersion } from '@metamask/eth-sig-util'; +import { + MetaMaskKeyring as QRKeyring, + MetaMaskKeyring as QRHardwareKeyring, +} from '@keystonehq/metamask-airgapped-keyring'; +import { + DataType, + ETHSignature, + EthSignRequest, +} from '@keystonehq/bc-ur-registry-eth'; + +import rlp from 'rlp'; +import { v4 } from 'uuid'; +import { SignatureData } from './transactions/utils/types'; +import { + arrToBufArr, + bigIntToBuffer, + bufferToBigInt, + bufferToHex, + stripHexPrefix, +} from '@ethereumjs/util'; import { hexToString } from '../utils/signature'; + +export enum KeyringControllerEvents { + QR_TRANSACTION_SIGNATURE_REQUEST_GENERATED = 'QR_TRANSACTION_SIGNATURE_REQUEST_GENERATED', + QR_MESSAGE_SIGNATURE_REQUEST_GENERATED = 'QR_MESSAGE_SIGNATURE_REQUEST_GENERATED', + QR_SIGNATURE_SUBMIT = 'QR_SIGNATURE_SUBMIT', +} + /** * Available keyring types */ @@ -24,21 +56,30 @@ export enum KeyringTypes { HD_KEY_TREE = 'HD Key Tree', TREZOR = 'Trezor Hardware', LEDGER = 'Ledger Hardware', + QR = 'QR Hardware Wallet Device', +} + +interface QRSignatureRequest { + requestId: string; + qrSignRequest: string[]; } export default class KeyringControllerDerivated extends KeyringController { private readonly _mutex: Mutex; + private readonly _qrHardwareKeyring: QRHardwareKeyring; constructor(opts: KeyringControllerProps) { opts.keyringBuilders = [ keyringBuilderFactory(LedgerBridgeKeyring), keyringBuilderFactory(TrezorKeyring), + keyringBuilderFactory(QRKeyring), ]; opts.cacheEncryptionKey = isManifestV3(); opts.encryptor = customEncryptor; super(opts); this._mutex = new Mutex(); + this._qrHardwareKeyring = new QRHardwareKeyring(); } /** @@ -323,7 +364,10 @@ export default class KeyringControllerDerivated extends KeyringController { ); } keyring.setHdPath(hdPath); - if (!keyring.isUnlocked()) await keyring.unlock(); + + if (device !== Devices.KEYSTONE) { + if (!keyring.isUnlocked()) await keyring.unlock(); + } } } @@ -337,7 +381,9 @@ export default class KeyringControllerDerivated extends KeyringController { */ public async getHDPathForDevice(device: Devices): Promise { const keyring = await this.getKeyringFromDevice(device); - return keyring ? keyring.hdPath : this._HDPathForDevice(device); + return keyring && keyring.hdPath !== '' + ? keyring.hdPath + : this._HDPathForDevice(device); } /** @@ -354,9 +400,13 @@ export default class KeyringControllerDerivated extends KeyringController { // If the keyring doesn't exist, create it if (!keyring) { - keyring = await this.addNewKeyring(keyringType, { - hdPath: this._HDPathForDevice(device), - }); + if (device === Devices.KEYSTONE) { + keyring = await this.addNewKeyring(keyringType); + } else { + keyring = await this.addNewKeyring(keyringType, { + hdPath: this._HDPathForDevice(device), + }); + } // Prevents manifest error, research if we can avoid this device === Devices.TREZOR && (await new Promise((resolve) => setTimeout(resolve, 5000))); @@ -371,10 +421,16 @@ export default class KeyringControllerDerivated extends KeyringController { // Unlock the keyring. If it's already unlocked it will resolve. // For Trezor devices, we force the unlock to prevent displaying // old accounts or accounts from a different device - await keyring.unlock(device === Devices.TREZOR); + if (keyring.unlock) { + await keyring.unlock(device === Devices.TREZOR); + } // Return whether we connected and unlocked the keyring successfully - return keyring.isUnlocked(); + if (device === Devices.KEYSTONE) { + return keyring.initialized; + } else { + return keyring.isUnlocked(); + } } /** @@ -417,12 +473,14 @@ export default class KeyringControllerDerivated extends KeyringController { */ private _getKeyringTypeFromDevice( device: Devices - ): KeyringTypes.LEDGER | KeyringTypes.TREZOR { + ): KeyringTypes.LEDGER | KeyringTypes.TREZOR | KeyringTypes.QR { switch (device) { case Devices.LEDGER: return KeyringTypes.LEDGER; case Devices.TREZOR: return KeyringTypes.TREZOR; + case Devices.KEYSTONE: + return KeyringTypes.QR; default: throw new Error('Invalid device'); } @@ -506,6 +564,8 @@ export default class KeyringControllerDerivated extends KeyringController { return Devices.LEDGER; case KeyringTypes.TREZOR: return Devices.TREZOR; + case KeyringTypes.QR: + return Devices.KEYSTONE; default: return undefined; } @@ -548,14 +608,63 @@ export default class KeyringControllerDerivated extends KeyringController { * @returns {Promise} The signed transactio object. */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ - public async signTransaction( + public async signEthTransaction( + transactionId: string, ethTx: TypedTransaction, _fromAddress: string, opts?: any ): Promise { - return this._mutex.runExclusive(async () => - super.signTransaction(ethTx, _fromAddress, opts) - ); + const keyringType = await this.getKeyringTypeFromAccount(_fromAddress); + if (keyringType === KeyringTypes.QR) { + // cancels any previous signature request + this.cancelQRHardwareSignRequest(); + this.removeAllListeners( + KeyringControllerEvents.QR_SIGNATURE_SUBMIT + ); + + const signRequest = await this.getQRETHSignRequest( + ethTx, + _fromAddress + ); + + this.emit( + KeyringControllerEvents.QR_TRANSACTION_SIGNATURE_REQUEST_GENERATED, + transactionId, + signRequest.requestId, + signRequest.qrSignRequest + ); + + try { + const { v, r, s } = await this.QRsignatureSubmission( + signRequest + ); + + // 0 means legacy + if (ethTx.type === 0) { + return (ethTx as Transaction)['_processSignature'](v, r, s); + } else if (ethTx.type === 1) { + return ( + ethTx as AccessListEIP2930Transaction + )._processSignature(v, r, s); + } else { + return ( + ethTx as FeeMarketEIP1559Transaction + )._processSignature(v + BigInt(27), r, s); + } + } catch (error) { + log.error('signature request error', error); + return ethTx; + } + } else { + return this._mutex.runExclusive( + async (): Promise => { + if (keyringType === KeyringTypes.TREZOR) { + await this.connectHardwareKeyring(Devices.TREZOR); + } + return super.signTransaction(ethTx, _fromAddress, opts); + } + ); + } } /** @@ -574,9 +683,19 @@ export default class KeyringControllerDerivated extends KeyringController { }, opts?: { withAppKeyOrigin: boolean } ): Promise { - return this._mutex.runExclusive(async () => - super.signMessage(msgParams, opts) + const keyringType = await this.getKeyringTypeFromAccount( + msgParams.from ); + if (keyringType === KeyringTypes.QR) { + return await this._signQRMessage(msgParams, opts); + } else { + return this._mutex.runExclusive(async () => { + if (keyringType === KeyringTypes.TREZOR) { + await this.connectHardwareKeyring(Devices.TREZOR); + } + return super.signMessage(msgParams, opts); + }); + } } /** @@ -596,9 +715,19 @@ export default class KeyringControllerDerivated extends KeyringController { }, opts?: any ): Promise { - return this._mutex.runExclusive(async () => - super.signPersonalMessage(msgParams, opts) + const keyringType = await this.getKeyringTypeFromAccount( + msgParams.from ); + if (keyringType === KeyringTypes.QR) { + return await this._signQRMessage(msgParams, opts); + } else { + return this._mutex.runExclusive(async () => { + if (keyringType === KeyringTypes.TREZOR) { + await this.connectHardwareKeyring(Devices.TREZOR); + } + return super.signPersonalMessage(msgParams, opts); + }); + } } /** @@ -617,9 +746,52 @@ export default class KeyringControllerDerivated extends KeyringController { version: 'V1' | 'V3' | 'V4'; } ): Promise { - return this._mutex.runExclusive(async () => - super.signTypedMessage(msgParams, opts) + const keyringType = await this.getKeyringTypeFromAccount( + msgParams.from ); + if (keyringType === KeyringTypes.QR) { + return await this._signQRMessage(msgParams, opts, true); + } else { + return this._mutex.runExclusive(async () => { + if (keyringType === KeyringTypes.TREZOR) { + await this.connectHardwareKeyring(Devices.TREZOR); + } + return super.signTypedMessage(msgParams, opts); + }); + } + } + + private async _signQRMessage( + msgParams: { + from: string; + data: string; + }, + opts?: any, + typedData?: boolean + ): Promise { + // cancels any previous signature request + this.cancelQRHardwareSignRequest(); + this.removeAllListeners(KeyringControllerEvents.QR_SIGNATURE_SUBMIT); + + let signRequest: QRSignatureRequest; + + if (typedData) { + signRequest = await this.getQRTypedMessageSignRequest( + msgParams, + opts + ); + } else { + signRequest = await this.getQRMessageSignRequest(msgParams); + } + + this.emit( + KeyringControllerEvents.QR_MESSAGE_SIGNATURE_REQUEST_GENERATED, + signRequest.requestId, + signRequest.qrSignRequest + ); + + const { v, r, s } = await this.QRsignatureSubmission(signRequest); + return concatSig(bigIntToBuffer(v), r, s); } /** @@ -636,4 +808,331 @@ export default class KeyringControllerDerivated extends KeyringController { }); } } + + // QR Hardware related methods + + /** + * Get qr hardware keyring. + * + * @returns The added keyring + */ + async getOrAddQRKeyring(): Promise { + let keyring = await this.getKeyringFromDevice(Devices.KEYSTONE); + if (!keyring) { + await this.connectHardwareKeyring(Devices.KEYSTONE); + keyring = await this.getKeyringFromDevice(Devices.KEYSTONE); + } + return keyring; + } + + /** + * Returns accounts from the QR device by page + * + * @param page + * @returns + */ + async getQRPage( + page: number + ): Promise<{ balance: string; address: string; index: number }[]> { + try { + const keyring = await this.getOrAddQRKeyring(); + const currentPage = (await keyring.serialize()).page; + + let accounts; + if (page > currentPage) { + // increments + for (let i = 1; i < page - currentPage; i++) { + await keyring.getNextPage(); + } + accounts = await keyring.getNextPage(); + } else if (page < currentPage) { + // decrements + for (let i = 1; i < currentPage - page; i++) { + await keyring.getPreviousPage(); + } + accounts = await keyring.getPreviousPage(); + } else { + await keyring.getNextPage(); + accounts = await keyring.getPreviousPage(); + } + + return accounts.map((account: any) => { + return { + ...account, + balance: '0x0', + }; + }); + } catch (e) { + throw new Error(`Unspecified error when connect QR Hardware, ${e}`); + } + } + + /** + * Submites the HDKey of the QR device + * + * @param cbor + */ + async submitQRHardwareCryptoHDKey(cbor: string) { + return this._mutex.runExclusive(async () => { + const read = this._qrHardwareKeyring.readKeyring(); + this._qrHardwareKeyring.submitCryptoHDKey(cbor); + await read; + this.fullUpdate(); + }); + } + + /** + * Submites the account of the QR device + * + * @param cbor + */ + async submitQRHardwareCryptoAccount(cbor: string) { + return this._mutex.runExclusive(async () => { + const r = this._qrHardwareKeyring.readKeyring(); + this._qrHardwareKeyring.submitCryptoAccount(cbor); + await r; + this.fullUpdate(); + }); + } + + /** + * Generates a ETH Sign request to be signed with a QR device + * + * + * @param {TypedTransaction} ethTx - The transaction to sign. + * @param {string} _fromAddress - The transaction 'from' address. + * @returns {Promise} The transaction sign request object QR as string. + */ + /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + public async getQRETHSignRequest( + ethTx: TypedTransaction, + _fromAddress: string + ): Promise { + return this._mutex.runExclusive(async () => { + const dataType = + ethTx.type === 0 + ? DataType.transaction + : DataType.typedTransaction; + + let messageToSign; + if (ethTx.type === 0) { + messageToSign = rlp.encode(ethTx.getMessageToSign(false)); + } else { + messageToSign = ethTx.getMessageToSign(false); + } + + const hdPath = await this._qrHardwareKeyring._pathFromAddress( + _fromAddress + ); + const chainId = ethTx.common.chainId(); + const requestId = v4(); + const xfp = (this._qrHardwareKeyring as any)['xfp']; + + const ethSignRequest = EthSignRequest.constructETHRequest( + messageToSign as Buffer, + dataType, + hdPath, + xfp, + requestId, + Number(chainId), + _fromAddress + ); + + return { + requestId, + qrSignRequest: ethSignRequest.toUREncoder(200).encodeWhole(), + }; + }); + } + + /** + * Generates a message sign request to be signed with a QR device + * + * + * @param {Object} msgParams - The message parameters to sign. + * @returns {Promise} The message sign request object QR as string. + */ + /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + public async getQRMessageSignRequest(msgParams: { + from: string; + data: string; + }): Promise { + return this._mutex.runExclusive(async () => { + const usignedHex = stripHexPrefix(msgParams.data); + const dataHex = Buffer.from(usignedHex, 'hex'); + const requestId = v4(); + const xfp = (this._qrHardwareKeyring as any)['xfp']; + const hdPath = await this._qrHardwareKeyring._pathFromAddress( + msgParams.from + ); + + const ethSignRequest = EthSignRequest.constructETHRequest( + dataHex, + DataType.personalMessage, + hdPath, + xfp, + requestId, + undefined, + msgParams.from + ); + + return { + requestId, + qrSignRequest: ethSignRequest.toUREncoder(200).encodeWhole(), + }; + }); + } + + /** + * Generates a typed message sign request to be signed with a QR device + * + * + * @param {Object} msgParams - The message parameters to sign. + * @returns {Promise} The typed message sign request object QR as string. + */ + /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + public async getQRTypedMessageSignRequest( + msgParams: { + from: string; + data: any; + }, + opts: { + version: 'V1' | 'V3' | 'V4'; + } + ): Promise { + return this._mutex.runExclusive(async () => { + if ( + opts.version !== SignTypedDataVersion.V1 && + typeof msgParams.data === 'string' + ) { + msgParams.data = JSON.parse(msgParams.data); + } + const dataHex = Buffer.from( + JSON.stringify(msgParams.data), + 'utf-8' + ); + const requestId = v4(); + const xfp = (this._qrHardwareKeyring as any)['xfp']; + const hdPath = await this._qrHardwareKeyring._pathFromAddress( + msgParams.from + ); + + const ethSignRequest = EthSignRequest.constructETHRequest( + dataHex, + DataType.typedData, + hdPath, + xfp, + requestId, + undefined, + msgParams.from + ); + + return { + requestId, + qrSignRequest: ethSignRequest.toUREncoder(200).encodeWhole(), + }; + }); + } + + /** + * After requesting a QR sign this function waits for the signed message + * @param signRequest + * @returns + */ + private async QRsignatureSubmission( + signRequest: QRSignatureRequest + ): Promise { + return new Promise((resolve, reject) => { + this.on( + KeyringControllerEvents.QR_SIGNATURE_SUBMIT, + (_requestId: string, signatureData: SignatureData) => { + if (_requestId === signRequest.requestId) { + this.removeAllListeners( + KeyringControllerEvents.QR_SIGNATURE_SUBMIT + ); + resolve(signatureData); + } else { + reject( + `got a signature of another request. current request requestId: ${signRequest.requestId}, received requestId: ${_requestId}` + ); + } + } + ); + }); + } + + /** + * Submits the signature generate by the QR device + * + * @param requestId + * @param cbor + */ + public submitQRHardwareSignature(requestId: string, cbor: Buffer) { + const ethSignature = ETHSignature.fromCBOR(cbor); + const signature = ethSignature.getSignature(); // it will return the signature r,s,v + const slice = Uint8Array.prototype.slice.call(signature); + const v = bufferToBigInt(arrToBufArr(slice.slice(64, 65))); + const r = arrToBufArr(slice.slice(0, 32)); + const s = arrToBufArr(slice.slice(32, 64)); + + const signatureData: SignatureData = { v, r, s }; + + this.emit( + KeyringControllerEvents.QR_SIGNATURE_SUBMIT, + requestId, + signatureData + ); + } + + /** + * Cancels an ongoing sign request + */ + public cancelQRHardwareSignRequest() { + this.emit(KeyringControllerEvents.QR_SIGNATURE_SUBMIT); + } + + /** + * Returns accounts from the device by page + * + * @param device + * @param keyring + * @param page + * @returns + */ + async getPage( + device: Devices, + keyring: any, + pageIndex: number + ): Promise<[]> { + try { + if (device === Devices.KEYSTONE) { + return (await this.getQRPage(pageIndex)) as []; + } else if (device === Devices.TREZOR) { + const currentPage = (await keyring.serialize()).page; + + let accounts; + if (pageIndex > currentPage) { + // increments + for (let i = 1; i < pageIndex - currentPage; i++) { + await keyring.getNextPage(); + } + accounts = await keyring.getNextPage(); + } else if (pageIndex < currentPage) { + // decrements + for (let i = 1; i < currentPage - pageIndex; i++) { + await keyring.getPreviousPage(); + } + accounts = await keyring.getPreviousPage(); + } else { + await keyring.getNextPage(); + accounts = await keyring.getPreviousPage(); + } + return accounts; + } else { + return await keyring.getPage(pageIndex); + } + } catch (e) { + throw new Error(`Unspecified error retrieving page, ${e}`); + } + } } diff --git a/packages/background/src/controllers/NetworkController.ts b/packages/background/src/controllers/NetworkController.ts index aec7d6a9c..50734393d 100644 --- a/packages/background/src/controllers/NetworkController.ts +++ b/packages/background/src/controllers/NetworkController.ts @@ -1,10 +1,9 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import Common from '@ethereumjs/common'; +import { Common, Hardfork } from '@ethereumjs/common'; import { BaseController } from '../infrastructure/BaseController'; import { Network, Networks, - HARDFORKS, AddNetworkType, EditNetworkUpdatesType, EditNetworkOrderType, @@ -27,6 +26,7 @@ import { getChainListItem } from '../utils/chainlist'; import { FEATURES } from '../utils/constants/features'; import { formatAndValidateRpcURL, + getCustomRpcChainId, getUrlWithoutTrailingSlash, validateNetworkChainId, } from '../utils/ethereumChain'; @@ -36,6 +36,12 @@ import { isABlockWalletNode, customHeadersForBlockWalletNode, } from '../utils/nodes'; +import { toChecksumAddress } from 'ethereumjs-util'; +import { MILISECOND, SECOND } from '../utils/constants/time'; +import { ProviderType } from '../utils/types/communication'; +import { _fetchFeeDataFromService } from './GasPricesController'; +import { isHttpsURL } from '../utils/http'; +import { BigNumber } from '@ethersproject/bignumber'; export enum NetworkEvents { NETWORK_CHANGE = 'NETWORK_CHANGE', @@ -48,19 +54,37 @@ export interface NetworkControllerState { availableNetworks: Networks; isNetworkChanging: boolean; isUserNetworkOnline: boolean; - isProviderNetworkOnline: boolean; + providerStatus: { + isCurrentProviderOnline: boolean; + isDefaultProviderOnline: boolean; + isBackupProviderOnline: boolean; + isUsingBackupProvider: boolean; + }; isEIP1559Compatible: { [chainId in number]: boolean }; } +const CHECK_PROVIDER_SCHEDULED_TIME = 5 * SECOND; +const CHECK_PROVIDER_REQUEST_TIMEOUT = 10 * SECOND; + +export const NO_EIP_1559_NETWORKS = [280, 324, 30, 31, 42220, 534351, 534352]; + export default class NetworkController extends BaseController { public static readonly CURRENT_HARDFORK: string = 'london'; private provider: StaticJsonRpcProvider; + private activeSubscriptions = false; + private checkProviderTimeout: ReturnType | undefined = + undefined; constructor(initialState: NetworkControllerState) { super({ ...initialState, isNetworkChanging: false, - isProviderNetworkOnline: true, + providerStatus: { + isCurrentProviderOnline: true, + isDefaultProviderOnline: true, + isBackupProviderOnline: true, + isUsingBackupProvider: false, + }, isUserNetworkOnline: true, }); @@ -72,18 +96,81 @@ export default class NetworkController extends BaseController { - if (!this.getState().isProviderNetworkOnline) { - this._updateProviderNetworkStatus(); + this.provider.on('debug', async ({ error }) => { + if (error) { + if (this.network.currentRpcUrl !== this.network.defaultRpcUrl) { + await this._updateProviderNetworkStatus( + ProviderType.DEFAULT, + error + ); + } else { + await this._updateProviderNetworkStatus( + ProviderType.BACKUP, + error + ); + } + this._updateProviderNetworkStatus(undefined, error); } - }, 3000); + }); this.setMaxListeners(30); // currently, we need 16 } + /** + * Checks the provider's network status when there is an active subscription to the bakcground. + * If the provider is online, we don't check its stauts and schedule a future check. + * If the provider is offline, we check the status and schedule a future check. + * If there is no active subscription, the scheduled check is cleared. + */ + private async _checkProviderNetworkStatus() { + if (!this.activeSubscriptions) { + if (this.checkProviderTimeout) { + clearTimeout(this.checkProviderTimeout); + } + return; + } + const { isCurrentProviderOnline, isUsingBackupProvider } = + this.getState().providerStatus; + + if (!isCurrentProviderOnline) { + await this._updateProviderNetworkStatus(); + await this._updateProviderNetworkStatus(ProviderType.DEFAULT); + + if (this.network.currentRpcUrl === this.network.defaultRpcUrl) { + await this._updateProviderNetworkStatus(ProviderType.BACKUP); + } + } + + // If the provider is online and we are using the backup provider, check the default provider status + if (isCurrentProviderOnline && isUsingBackupProvider) { + await this._updateProviderNetworkStatus(ProviderType.DEFAULT); + } + + this.checkProviderTimeout = setTimeout( + this._checkProviderNetworkStatus.bind(this), + CHECK_PROVIDER_SCHEDULED_TIME + ); + } + + /** + * setActiveSubscriptions + * + * It sets if there is at least one active subscription to the background + * + * @param isUnlocked Whether the extension is unlocked or not + * @param activeSubscription If there is any block wallet instance active. + */ + public setActiveSubscriptions( + isUnlocked: boolean, + activeSubscription: boolean + ): void { + const prevActiveSubscriptions = this.activeSubscriptions; + this.activeSubscriptions = isUnlocked && activeSubscription; + if (this.activeSubscriptions != prevActiveSubscriptions) { + this._checkProviderNetworkStatus(); + } + } + /** * Gets user selected native currency */ @@ -298,7 +385,7 @@ export default class NetworkController extends BaseController { + public async addNetwork( + network: AddNetworkType, + switchToNetwork = false + ): Promise { if (!network.chainId || Number.isNaN(network.chainId)) { throw new Error('ChainId is required and must be numeric.'); } @@ -361,20 +460,22 @@ export default class NetworkController extends BaseController url) + chainDataFromList?.explorers?.map( + ({ url }: { url: string }) => url + ) ) || ''; - if (explorerUrl && explorerUrl.indexOf('https://') === -1) { + if (explorerUrl && !isHttpsURL(explorerUrl)) { throw new Error('Block explorer endpoint must be https'); } // Check if we have the explorer name const blockExplorerName = chainDataFromList?.explorers - ?.map((t) => ({ + ?.map((t: any) => ({ ...t, url: t.url.endsWith('/') ? t.url.slice(0, -1) : t.url, })) - .find((e) => e.url.includes(explorerUrl))?.name; + .find((e: any) => e.url.includes(explorerUrl))?.name; const nativeCurrencySymbol = network.nativeCurrency?.symbol || @@ -383,12 +484,14 @@ export default class NetworkController extends BaseController n.test === network.test) .map((c) => c.order) .sort((a, b) => b - a)[0] + 1, - iconUrls: nativeCurrencyIcon ? [nativeCurrencyIcon] : undefined, + iconUrls: networkIcon ? [networkIcon] : undefined, nativelySupported: false, hasFixedGasCost: false, etherscanApiUrl: chainDataFromList?.scanApi, }; } + + if (switchToNetwork) { + this.setNetwork(key); + } } /** @@ -499,7 +613,10 @@ export default class NetworkController extends BaseController { const network = this.searchNetworkByName(networkName); - return this._getProviderForNetwork(network.chainId, network.rpcUrls[0]); + return this._getProviderForNetwork( + network.chainId, + network.currentRpcUrl + ); }; /** @@ -526,7 +643,7 @@ export default class NetworkController extends BaseController { let shouldFetchTheCurrentState = false; - if (!(chainId in this.getState().isEIP1559Compatible)) { - shouldFetchTheCurrentState = true; - } else { - if (this.getState().isEIP1559Compatible[chainId] === undefined) { - shouldFetchTheCurrentState = true; - } else { - if (forceUpdate) { - shouldFetchTheCurrentState = true; - } - } + const isEIP1559Compatible: boolean | null = + this.getState().isEIP1559Compatible[chainId]; + + // Integrity check. We had cases where old states had a false value but the network was upgraded to EIP-1559. + // So, if we detect that the baseFee is inconsistent with the value we had, we force the update of the flag. + if (isEIP1559Compatible != null && !forceUpdate) { + forceUpdate = + (isEIP1559Compatible && baseFee === undefined) || + (!isEIP1559Compatible && baseFee !== undefined); } - if (shouldFetchTheCurrentState) { - let baseFeePerGas = (await this.getLatestBlock(provider)) - .baseFeePerGas; + shouldFetchTheCurrentState = isEIP1559Compatible == null || forceUpdate; - // detection for the fantom case, - // the network seems to be eip1559 but eth_feeHistory is not available. - if (baseFeePerGas) { - try { - const feeHistory = await provider.send('eth_feeHistory', [ - '0x1', - 'latest', - [50], - ]); - if ( - !feeHistory || - (!feeHistory.baseFeePerGas && !feeHistory.reward) - ) { - throw new Error( - `eth_feeHistory is not fully supported by chain ${chainId}` - ); + if (shouldFetchTheCurrentState) { + // check: https://github.com/block-wallet/chain-fee-data-service/blob/main/domain/eth/service/service_impl.go#L425 + if (NO_EIP_1559_NETWORKS.includes(chainId)) { + this.store.updateState({ + isEIP1559Compatible: { + ...this.getState().isEIP1559Compatible, + [chainId]: false, + }, + }); + } else { + // check the response of the chain fee service + const feeDataResponse = await _fetchFeeDataFromService( + chainId, + 10 * MILISECOND, + 1 + ); + if ( + feeDataResponse && + 'baseFee' in feeDataResponse && + feeDataResponse.baseFee + ) { + this.store.updateState({ + isEIP1559Compatible: { + ...this.getState().isEIP1559Compatible, + [chainId]: true, + }, + }); + } else { + let baseFeePerGas = (await this.getLatestBlock(provider)) + .baseFeePerGas; + + // detection for the fantom case, + // the network seems to be eip1559 but eth_feeHistory is not available. + if (baseFeePerGas) { + try { + const feeHistory = await provider.send( + 'eth_feeHistory', + ['0x1', 'latest', [50]] + ); + if ( + !feeHistory || + (!feeHistory.baseFeePerGas && + !feeHistory.reward) + ) { + throw new Error( + `eth_feeHistory is not fully supported by chain ${chainId}` + ); + } + } catch { + baseFeePerGas = undefined; + } } - } catch { - baseFeePerGas = undefined; + + this.store.updateState({ + isEIP1559Compatible: { + ...this.getState().isEIP1559Compatible, + [chainId]: !!baseFeePerGas, + }, + }); } } - - this.store.updateState({ - isEIP1559Compatible: { - ...this.getState().isEIP1559Compatible, - [chainId]: !!baseFeePerGas, - }, - }); } return this.getState().isEIP1559Compatible[chainId]; @@ -728,6 +876,97 @@ export default class NetworkController extends BaseController { + const network = this.getNetworkFromChainId(chainId); + if (!network) throw new Error('Network not found for given chainId'); + + let rpcUrl; + switch (providerType) { + case ProviderType.DEFAULT: + rpcUrl = network.defaultRpcUrl; + break; + case ProviderType.BACKUP: { + rpcUrl = await this._getWorkingBackupRpcUrl(chainId); + break; + } + case ProviderType.CUSTOM: + rpcUrl = customRpcUrl; + break; + default: + throw new Error('Invalid provider type'); + } + if (!rpcUrl) throw new Error('Invalid rpc url'); + + await this.editNetwork(chainId, { + rpcUrls: [rpcUrl], + blockExplorerUrls: network.blockExplorerUrls, + name: network.desc, + test: network.test, + }); + + this.store.updateState({ + providerStatus: { + ...this.getState().providerStatus, + isUsingBackupProvider: providerType === ProviderType.BACKUP, + }, + }); + } + + /** + * _isRpcValid + * + * Checks if the rpc url is working + * @param chainId chain identifier of the network + * @param rpcUrl rpc url of the network + * @returns true if the rpc url is valid, false otherwise + */ + private async _isRpcValid( + chainId: number, + rpcUrl: string + ): Promise { + try { + const returnedChainId = await getCustomRpcChainId(rpcUrl); + return returnedChainId === chainId; + } catch (error) { + return false; + } + } + + /** + * _getWorkingBackupRpcUrl + * + * Returns the first working backup rpc url + * @param chainId chain identifier of the network + * @returns rpc url of the network + */ + private async _getWorkingBackupRpcUrl(chainId: number) { + const network = this.getNetworkFromChainId(chainId); + const backupRpcUrls = network?.backupRpcUrls ?? []; + + let rpcUrl; + + for (const url of backupRpcUrls) { + if (await this._isRpcValid(chainId, url)) { + rpcUrl = url; + break; + } + } + + return rpcUrl; + } + /** * waitForTransaction */ @@ -755,10 +994,13 @@ export default class NetworkController extends BaseController { + private _updateProviderNetworkStatus = async ( + providerType?: ProviderType, + err?: { + code: ErrorCode; + body?: string; + } + ) => { const errorCodes = [ErrorCode.SERVER_ERROR, ErrorCode.TIMEOUT]; + const { defaultRpcUrl, currentRpcUrl, chainId } = this.network; + const checkingChainId = this.provider.network.chainId; + + let rpcUrl = currentRpcUrl; + + switch (providerType) { + case ProviderType.DEFAULT: + if (!defaultRpcUrl) { + this.store.updateState({ + providerStatus: { + ...this.getState().providerStatus, + isDefaultProviderOnline: false, + }, + }); + return Promise.resolve(true); + } + rpcUrl = defaultRpcUrl; + break; + case ProviderType.BACKUP: { + const backupRpcUrl = await this._getWorkingBackupRpcUrl( + chainId + ); + if (!backupRpcUrl) { + this.store.updateState({ + providerStatus: { + ...this.getState().providerStatus, + isBackupProviderOnline: false, + }, + }); + return Promise.resolve(true); + } + rpcUrl = backupRpcUrl; + break; + } + } + + const providerInstance = this._getProviderForNetwork(chainId, rpcUrl); + if (!err || errorCodes.includes(err.code)) { - this._isProviderReady() + return this._isProviderReady(providerInstance) .then(() => { return Promise.resolve(true); }) @@ -785,16 +1068,40 @@ export default class NetworkController extends BaseController { - if (this.getState().isProviderNetworkOnline === newStatus) { - return; + const currentChainId = this.provider.network.chainId; + const providerStatus = this.getState().providerStatus; + const currentProviderStatus = + providerType === ProviderType.BACKUP + ? providerStatus.isBackupProviderOnline + : providerType === ProviderType.DEFAULT + ? providerStatus.isDefaultProviderOnline + : providerStatus.isCurrentProviderOnline; + + if ( + currentProviderStatus === newStatus || + // Discard response if the user switched the network + currentChainId !== checkingChainId + ) { + return false; } + const updatedStatus = + providerType === ProviderType.BACKUP + ? { isBackupProviderOnline: newStatus } + : providerType === ProviderType.DEFAULT + ? { isDefaultProviderOnline: newStatus } + : { isCurrentProviderOnline: newStatus }; + this.store.updateState({ - isProviderNetworkOnline: newStatus, + providerStatus: { + ...providerStatus, + ...updatedStatus, + }, }); - this.emit(NetworkEvents.PROVIDER_NETWORK_CHANGE, newStatus); + return true; }); } + return Promise.resolve(true); }; private _isProviderReady( @@ -819,7 +1126,7 @@ export default class NetworkController extends BaseController { + try { + const code = await provider.getCode(toChecksumAddress(address)); + if (code !== '0x') return true; + } catch (error) { + log.error("error executing 'getCode'", error); + } + return false; + } } diff --git a/packages/background/src/controllers/NotificationController.ts b/packages/background/src/controllers/NotificationController.ts new file mode 100644 index 000000000..24a288ebc --- /dev/null +++ b/packages/background/src/controllers/NotificationController.ts @@ -0,0 +1,446 @@ +import { BigNumber } from '@ethersproject/bignumber'; +import { createCustomExplorerLink } from '@block-wallet/explorer-link'; + +import { PreferencesController } from './PreferencesController'; +import { + TransactionWatcherController, + TransactionWatcherControllerEvents, +} from './TransactionWatcherController'; +import TransactionController from './transactions/TransactionController'; +import { AccountTrackerController } from './AccountTrackerController'; +import { AddressBookController } from './AddressBookController'; +import { EnsController } from './EnsController'; + +import { ChainListItem, getChainListItem } from '../utils/chainlist'; +import { + MetaType, + TransactionCategories, + TransactionEvents, + TransactionMeta, + TransactionStatus, + WatchedTransactionType, +} from '../controllers/transactions/utils/types'; +import { formatTokenAmount } from '../utils/token'; +import { fetchContractDetails } from '../utils/contractsInfo'; +import { formatName } from '../utils/account'; +import browser from 'webextension-polyfill'; +import { isHttpsURL } from '../utils/http'; + +interface ChainListItemWithExplorerUrl extends ChainListItem { + explorerUrl: string; +} + +export class NotificationController { + constructor( + private readonly _preferencesController: PreferencesController, + private readonly _transactionWatcherController: TransactionWatcherController, + private readonly _transactionController: TransactionController, + private readonly _accountTrackerController: AccountTrackerController, + private readonly _addressBookController: AddressBookController, + private readonly _ensController: EnsController + ) { + // Subscribe to Incoming transactions + this._transactionWatcherController.on( + TransactionWatcherControllerEvents.INCOMING_TRANSACTION, + async ( + _chainId: number, + _address: string, + _transactionType: WatchedTransactionType, + txMeta: TransactionMeta + ) => { + this.showTransactionNotification(txMeta); + } + ); + + // Subscribe to transactions status updates + this._transactionController.on( + TransactionEvents.STATUS_UPDATE, + (transactionMeta: TransactionMeta) => { + const notificationTxStatuses = [ + TransactionStatus.CONFIRMED, + TransactionStatus.FAILED, + TransactionStatus.CANCELLED, + ]; + + if (notificationTxStatuses.includes(transactionMeta.status)) { + this.showTransactionNotification(transactionMeta); + } + } + ); + } + + /** + * Shows a notification when the user has completed the set-up process. + */ + async showSetUpCompleteNotification() { + const url = ''; + const title = 'BlockWallet is ready!'; + const message = + "You've completed the set-up process. Check the extension in the upper right corner of your browser."; + + this.showNotification(title, message, url); + } + + /** + * Shows a notification including the transaction info and open link to explorer on click. + * @param txMeta - The transaction meta object. + */ + async showTransactionNotification(txMeta: TransactionMeta) { + const network = this.getNetworkData(txMeta.chainId); + if (!network) return; + + // the cancel transaction is not shown in the notification only the cancelation + if (txMeta.metaType === MetaType.CANCEL) return; + + const { title, message, url } = await this.getTxNotificationData( + txMeta, + network + ); + + if (!txMeta.transactionCategory) return; + + const isIncomingTransaction = [ + TransactionCategories.INCOMING, + TransactionCategories.TOKEN_METHOD_INCOMING_TRANSFER, + ].includes(txMeta.transactionCategory); + + const accountAddress = isIncomingTransaction + ? txMeta.transactionParams.to + : txMeta.transactionParams.from; + + if (!accountAddress) return; + + const accountName = + this._accountTrackerController.getAccountName(accountAddress); + + if (!accountName) return; + + const accountInfo = `${formatName( + accountName + )} (...${accountAddress?.slice(accountAddress.length - 4)})`; + + this.showNotification(title, message, url, accountInfo); + } + + /** + * Shows a browser notification and open link on click. + * + * @param title - The notification title. + * @param message - The notification message. + * @param url - The url to open on click. + * @param contextMessage - The context message. + * + */ + public showNotification( + title: string, + message: string, + url: string, + contextMessage?: string + ) { + if (!this._preferencesController.settings.subscribedToNotifications) + return; + let notificationUrl = url; + if (url) { + this.addOnClickListener(); + + // To prevent duplicate notifications id which causes the notification to not show (overrides the old one) + const urlObject = new URL(url); + urlObject.searchParams.set('timestamp', Date.now().toString()); + notificationUrl = urlObject.toString(); + } + browser.notifications.create(notificationUrl, { + title: title, + message: message, + iconUrl: browser.runtime.getURL('icons/icon-48.png'), + type: 'basic', + isClickable: url ? true : false, + contextMessage: contextMessage, + }); + } + + private addOnClickListener() { + const onClickListener = browser.notifications.onClicked; + + if (!onClickListener.hasListener(this.linkToExplorer)) { + onClickListener.addListener(this.linkToExplorer); + } + } + + private linkToExplorer(url: string) { + if (isHttpsURL(url)) { + browser.tabs.create({ url: url }); + } + } + + /** + * Gets the network data from the chain list. + * @param chainId - The chain id. + * @returns The network data. + * + */ + private getNetworkData(chainId: number | undefined) { + if (!chainId || isNaN(chainId)) return undefined; + + const networkData = getChainListItem(chainId); + + if ( + !networkData || + !networkData.explorers || + !networkData.explorers.length + ) + return undefined; + + const explorerUrl = networkData.explorers.find((e) => e.url)?.url; + if (!explorerUrl) return undefined; + + return { ...networkData, explorerUrl } as ChainListItemWithExplorerUrl; + } + + private async getTxNotificationData( + txMeta: TransactionMeta, + network: ChainListItemWithExplorerUrl + ) { + const { + transactionParams: txParams, + transactionCategory, + status, + exchangeParams, + bridgeParams, + approveAllowanceParams, + transferType, + chainId, + } = txMeta; + + const { + name: txNetworkName, + nativeCurrency: txNetworkNativeToken, + explorerUrl, + } = network; + + let title = ''; + let message = ''; + let url = ''; + + if (txParams.hash) { + url = createCustomExplorerLink(txParams.hash, explorerUrl); + } + + const isTxFailed = status === TransactionStatus.FAILED; + const isTxCancelled = status === TransactionStatus.CANCELLED; + + // Used when the transaction category is not supported or there is no data for the transaction category. + let showDefaultNotification = false; + + switch (transactionCategory) { + case TransactionCategories.EXCHANGE: + if (!exchangeParams) { + showDefaultNotification = true; + break; + } + title = `Swap`; + message = `${exchangeParams.fromToken.symbol} to ${exchangeParams.toToken.symbol} swap on ${txNetworkName}`; + break; + case TransactionCategories.BRIDGE: + if (!bridgeParams) { + showDefaultNotification = true; + break; + } + title = `Bridge`; + message = `${ + this.getNetworkData(bridgeParams.fromChainId)?.name + } to ${ + this.getNetworkData(bridgeParams.toChainId)?.name + } bridge`; + break; + case TransactionCategories.TOKEN_METHOD_APPROVE: { + if (!approveAllowanceParams) { + showDefaultNotification = true; + break; + } + const { + spenderAddress, + spenderInfo, + token, + allowanceValue, + isUnlimited, + } = approveAllowanceParams; + const isRevoke = BigNumber.from(allowanceValue).eq(0); + const spenderName = + spenderInfo?.name ?? + `Spender (${spenderAddress?.slice( + 0, + 6 + )}...${spenderAddress?.slice(spenderAddress.length - 4)})`; + if (!isRevoke) { + title = `Token Approval`; + message = `Approval of ${ + isUnlimited + ? `unlimited ${token.symbol}` + : formatTokenAmount( + allowanceValue, + token.decimals, + token.symbol + ) + } for use with ${spenderName} on ${txNetworkName}`; + } else { + title = `Approval Revoke`; + message = `${token.symbol} approval revoke for ${spenderName} on ${txNetworkName}`; + } + break; + } + case TransactionCategories.SENT_ETHER: { + const recipientName = await this._getAddressName(txParams.to); + + title = `Transaction`; + message = `${txNetworkNativeToken.symbol} transfer${ + recipientName ? ` to ${recipientName}` : '' + } on ${txNetworkName}`; + break; + } + case TransactionCategories.TOKEN_METHOD_TRANSFER: { + const recipientName = await this._getAddressName( + transferType?.to + ); + if (!transferType) { + showDefaultNotification = true; + break; + } + title = `Transaction`; + message = `${transferType.currency} transfer${ + recipientName ? ` to ${recipientName}` : '' + } on ${txNetworkName}`; + break; + } + case TransactionCategories.INCOMING: { + const senderName = await this._getAddressName(txParams.from); + + if (!txParams || !txParams.value) { + showDefaultNotification = true; + break; + } + title = `Incoming Transaction`; + message = `Received ${formatTokenAmount( + txParams.value, + txNetworkNativeToken.decimals, + txNetworkNativeToken.symbol + )}${ + senderName ? ` from ${senderName}` : '' + } on ${txNetworkName}.`; + break; + } + case TransactionCategories.TOKEN_METHOD_INCOMING_TRANSFER: { + const senderName = await this._getAddressName(txParams.from); + if (!transferType) { + showDefaultNotification = true; + break; + } + + title = 'Incoming Transaction'; + message = `Received ${formatTokenAmount( + transferType.amount, + transferType.decimals, + transferType.currency + )}${ + senderName ? ` from ${senderName}` : '' + } on ${txNetworkName}.`; + break; + } + case TransactionCategories.CONTRACT_INTERACTION: { + if (!txParams || !txParams.to || !chainId) { + showDefaultNotification = true; + break; + } + const contractAddress = txParams.to; + const contractDetails = await fetchContractDetails( + chainId, + contractAddress + ); + const contractName = + contractDetails?.name ?? + `Contract (${contractAddress?.slice( + 0, + 6 + )}...${contractAddress?.slice( + contractAddress.length - 4 + )})`; + + title = `Contract Interaction`; + message = `Transaction with ${contractName} on ${txNetworkName}`; + break; + } + case TransactionCategories.CONTRACT_DEPLOYMENT: + title = `Contract Deploy`; + message = `Contract deployment on ${txNetworkName}`; + break; + default: + showDefaultNotification = true; + break; + } + + if (showDefaultNotification) { + title = 'Transaction'; + message = `Transaction on ${txNetworkName}`; + } + + const incomingTxCategories = [ + TransactionCategories.INCOMING, + TransactionCategories.TOKEN_METHOD_INCOMING_TRANSFER, + ]; + + if ( + transactionCategory && + !incomingTxCategories.includes(transactionCategory) + ) { + title = + title + + (!isTxFailed && !isTxCancelled + ? ' Completed' + : isTxCancelled + ? ' Cancelled' + : ' Failed'); + message = + message + + (!isTxFailed && !isTxCancelled + ? ' completed.' + : isTxCancelled + ? ' cancelled.' + : ' failed.'); + } + + return { + title, + message, + url, + }; + } + + /** + * Returns the name of the address if it is an account or in address book or an ENS name + * @param address - Hex address of the account + * @returns - Name of the address + * + */ + private async _getAddressName(address?: string) { + if (!address) return undefined; + + let name: string | undefined | null; + + name = this._accountTrackerController.getAccountName(address); + + if (!name) { + name = await this._addressBookController.getFormattedContactName( + address + ); + } + + if (!name) { + name = await this._ensController + .lookupAddress(address) + .catch(() => { + return undefined; + }); + } + + return name; + } +} diff --git a/packages/background/src/controllers/OnrampController.ts b/packages/background/src/controllers/OnrampController.ts new file mode 100644 index 000000000..dfe7d6fb8 --- /dev/null +++ b/packages/background/src/controllers/OnrampController.ts @@ -0,0 +1,142 @@ +import log from 'loglevel'; +import NetworkController from './NetworkController'; +import { IToken } from './erc-20/Token'; +import { IChain } from '../utils/types/chain'; +import { BaseController } from '../infrastructure/BaseController'; +import { Currency } from '../utils/currency'; +import OnrampAPI, { IOnramp, OnrampImplementation } from '../utils/onrampApi'; +import { isValidAddress, toChecksumAddress } from '@ethereumjs/util'; +import { isNativeTokenAddress } from '../utils/token'; + +export interface OnrampControllerMemState { + availableOnrampChains: IChain[]; +} + +export interface GetOnRampCurrencies { + crypto: IToken[]; + fiat: Currency[]; +} + +/** + * Onramp Controller + * + * This class handles BlockWallet Onramp. + * + * Allow to retrieve onramp currencies and availablechains from OnrampApi + * + */ + +export default class OnrampController extends BaseController { + constructor(private readonly _networkController: NetworkController) { + super(undefined, { availableOnrampChains: [] }); + + this.getAvailableChains(); + } + + /** + * It returns the onramp available chains + * + */ + public getOnrampAvailableChains(): IChain[] { + return this.store.getState().availableOnrampChains; + } + + private _getAPIImplementation( + implementation: OnrampImplementation + ): IOnramp { + return OnrampAPI[implementation]; + } + + public async getCurrencies( + aggregator: OnrampImplementation = OnrampImplementation.ONRAMPER_BUY + ): Promise { + const implementor = this._getAPIImplementation(aggregator); + const { name, chainId } = this._networkController.network; + const networkName = + name === 'mainnet' ? 'ethereum' : name.toLowerCase(); + const availableChains = this.getOnrampAvailableChains(); + + try { + const response: GetOnRampCurrencies = { crypto: [], fiat: [] }; + const allCurrenciesResponse = + await implementor.getSupportedCurrencies(); + const currentOnrampChain = availableChains.filter( + (chain) => chain.id === chainId + ); + + if (!currentOnrampChain.length) return response; + + const currentNetworkCryptos = + allCurrenciesResponse.message.crypto.filter( + (currency) => + currency.network === currentOnrampChain[0].name + ); + + currentNetworkCryptos.map((token) => { + const tokenAddress = + token.address && isValidAddress(token.address) + ? toChecksumAddress(token.address) + : ''; + const nativeTokenAddress = isNativeTokenAddress(tokenAddress); + const logoURL = !nativeTokenAddress + ? 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/' + + networkName + + '/assets/' + + tokenAddress + + '/logo.png' + : 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/ethereum/info/logo.png'; + + return response.crypto.push({ + name: token.name, + symbol: token.code, + logo: tokenAddress !== '' ? logoURL : '', + type: token.id, + address: token.address, + decimals: token.decimals, + }); + }); + + response.crypto.sort((tokenA, tokenB) => + tokenA.name > tokenB.name + ? 1 + : tokenB.name > tokenA.name + ? -1 + : 0 + ); + response.fiat.push(...allCurrenciesResponse.message.fiat); + + return response; + } catch (e) { + throw new Error('Unable to fetch onramper currencies.'); + } + } + + public async getAvailableChains( + aggregator: OnrampImplementation = OnrampImplementation.ONRAMPER_BUY + ) { + const implementor = this._getAPIImplementation(aggregator); + try { + const onrampSupportedNetworks = + await implementor.getSupportedNetworks(); + + const availableChains: IChain[] = []; + onrampSupportedNetworks.map((network) => { + return availableChains.push({ + name: network.networkName, + id: Number(network.chainId), + logo: '', + test: false, + }); + }); + + this.store.setState({ + availableOnrampChains: availableChains, + }); + } catch (e) { + log.error('Error fetching onramper chains', e); + this.store.setState({ + availableOnrampChains: [], + }); + } + } +} diff --git a/packages/background/src/controllers/PermissionsController.ts b/packages/background/src/controllers/PermissionsController.ts index bb1a36610..ef65d9478 100644 --- a/packages/background/src/controllers/PermissionsController.ts +++ b/packages/background/src/controllers/PermissionsController.ts @@ -1,4 +1,4 @@ -import { toChecksumAddress } from 'ethereumjs-util'; +import { toChecksumAddress } from '@ethereumjs/util'; import { BaseController } from '../infrastructure/BaseController'; import { PreferencesController, diff --git a/packages/background/src/controllers/PreferencesController.ts b/packages/background/src/controllers/PreferencesController.ts index d5c974558..84d2b67d0 100644 --- a/packages/background/src/controllers/PreferencesController.ts +++ b/packages/background/src/controllers/PreferencesController.ts @@ -1,16 +1,24 @@ -import { toChecksumAddress } from 'ethereumjs-util'; +import { toChecksumAddress } from '@ethereumjs/util'; import { BaseController } from '../infrastructure/BaseController'; export interface UserSettings { // Setting that indicates if a warning is shown when receiving a transaction from an address different from the selected one. hideAddressWarning: boolean; + // Setting that indicates if a warning is shown when sending a transaction to a contract address. + hideSendToContractWarning: boolean; + // Setting that indicates if a warning is shown when sending a transaction to null address. + hideSendToNullWarning: boolean; subscribedToReleaseaNotes: boolean; + subscribedToNotifications: boolean; useAntiPhishingProtection: boolean; defaultBrowserWallet: boolean; hideEstimatedGasExceedsThresholdWarning: boolean; //whether we should display the warning while making a deposit with an external/hardware account or not. hideDepositsExternalAccountsWarning: boolean; hideBridgeInsufficientNativeTokenWarning: boolean; + + // Indicates if the wallet displays net worth in native currency value or native token balance. + displayNetWorth: boolean; } export interface Note { @@ -51,6 +59,9 @@ export interface PreferencesControllerState { releaseNotesSettings: ReleaseNotesSettings; filters: FilterPreferences; defaultGasOption: DefaultGasOptions; + hotkeysEnabled: boolean; + tokensSortValue: string; + hideSmallBalances: boolean; } export interface PreferencesControllerProps { @@ -324,4 +335,50 @@ export class PreferencesController extends BaseController => { - if (exchangeType === ExchangeType.SWAP_1INCH) { - return this._get1InchSwapQuote(quoteParams); - } else { - throw new Error('Exchange type not supported'); + quoteParams: SwapQuoteParams + ): Promise => { + const { chainId } = this._networkController.network; + + switch (exchangeType) { + case ExchangeType.SWAP_1INCH: + return await OneInchService.getSwapQuote(chainId, quoteParams); + case ExchangeType.SWAP_OPENOCEAN: { + const gasPrice = + this._gasPricesController.getHighGasPriceInGwei(); + + return await OpenOceanService.getSwapQuote(chainId, { + ...quoteParams, + gasPrice, + }); + } + default: + throw new Error('Exchange type not supported.'); } }; @@ -169,12 +214,31 @@ export default class SwapController extends BaseController< */ public getExchangeParameters = async ( exchangeType: ExchangeType, - exchangeParams: OneInchSwapRequestParams + exchangeParams: SwapRequestParams ): Promise => { - if (exchangeType === ExchangeType.SWAP_1INCH) { - return this._get1InchSwapParameters(exchangeParams); - } else { - throw new Error('Exchange type not supported'); + const { chainId } = this._networkController.network; + const contractSignatureParser = new ContractSignatureParser( + this._networkController + ); + + switch (exchangeType) { + case ExchangeType.SWAP_1INCH: + return await OneInchService.getSwapParameters( + chainId, + contractSignatureParser, + exchangeParams + ); + case ExchangeType.SWAP_OPENOCEAN: { + const gasPrice = + this._gasPricesController.getHighGasPriceInGwei(); + return await OpenOceanService.getSwapParameters( + chainId, + contractSignatureParser, + { ...exchangeParams, gasPrice } + ); + } + default: + throw new Error('Exchange type not supported.'); } }; @@ -188,15 +252,16 @@ export default class SwapController extends BaseController< exchangeType: ExchangeType, exchangeParams: SwapTransaction ): Promise => { - if (exchangeType === ExchangeType.SWAP_1INCH) { - const swapPromise = this._execute1InchSwap(exchangeParams); - this._tokenController.attemptAddToken( - exchangeParams.toToken.address - ); - return swapPromise; - } else { - throw new Error('Exchange type not supported'); - } + if ( + ![ExchangeType.SWAP_1INCH, ExchangeType.SWAP_OPENOCEAN].includes( + exchangeType + ) + ) + throw new Error('Exchange type not supported.'); + + const swapPromise = this._executeSwap(exchangeParams); + this._tokenController.attemptAddToken(exchangeParams.toToken.address); + return swapPromise; }; /** @@ -209,129 +274,57 @@ export default class SwapController extends BaseController< const { chainId } = this._networkController.network; try { - if (exchangeType === ExchangeType.SWAP_1INCH) { - // 1Inch router contract address - const res = await retryHandling(() => - httpClient.get( - `${ONEINCH_SWAPS_ENDPOINT}${chainId}/approve/spender` - ) - ); - - return res.address; - } else { - throw new Error('Exchange type not supported'); + switch (exchangeType) { + case ExchangeType.SWAP_1INCH: + return OneInchService.getSpender(chainId); + case ExchangeType.SWAP_OPENOCEAN: + return OpenOceanService.getSpender(chainId); + default: + throw new Error('Exchange type not supported.'); } } catch (error) { throw new Error('Unable to fetch exchange spender'); } }; - /** - * Fetch quote details for a 1Inch Swap - * - * @param OneInchSwapQuoteParams 1Inch quote parameters - */ - private _get1InchSwapQuote = async ({ - fromTokenAddress, - toTokenAddress, - amount, - }: OneInchSwapQuoteParams): Promise => { + public estimateSwapGas = async ( + tx: SwapTxMeta + ): Promise => { try { - const res = await retryHandling(() => - httpClient.get< - OneInchSwapQuoteResponse, - OneInchSwapQuoteParams - >( - `${ONEINCH_SWAPS_ENDPOINT}${this._networkController.network.chainId}/quote`, - { - fromTokenAddress: - fromTokenAddress === '0x0' - ? ONEINCH_NATIVE_ADDRESS - : fromTokenAddress, - toTokenAddress: - toTokenAddress === '0x0' - ? ONEINCH_NATIVE_ADDRESS - : toTokenAddress, - amount, - fee: BASE_SWAP_FEE, - } - ) - ); - - return { - ...res, - blockWalletFee: BigNumber.from(res.fromTokenAmount) - .mul(BASE_SWAP_FEE * 10) - .div(1000), - estimatedGas: Math.round(res.estimatedGas * GAS_LIMIT_INCREASE), + const transactionMeta: TransactionMeta = { + id: uuid(), + chainId: this._networkController.network.chainId, + origin: 'blank', + status: TransactionStatus.UNAPPROVED, + time: Date.now(), + verifiedOnBlockchain: false, + loadingGasValues: true, + blocksDropCount: 0, + transactionParams: normalizeTransaction({ + from: tx.from, + to: tx.to, + data: tx.data, + value: BigNumber.from(tx.value), + }), + metaType: MetaType.REGULAR, }; - } catch (error) { - const errMessage = map1InchErrorMessage( - get1InchErrorMessageFromResponse(error) // Error should be of type RequestError - ); - throw new Error(errMessage || 'Error getting 1Inch swap quote'); - } - }; - /** - * Fetch transaction parameters for a 1Inch Swap - * - * @param swapParams 1Inch swap parameters - */ - private _get1InchSwapParameters = async ( - swapParams: OneInchSwapRequestParams - ): Promise => { - const { chainId } = this._networkController.network; - const contractSignatureParser = new ContractSignatureParser( - this._networkController - ); - - try { - const res = await retryHandling(() => - httpClient.get< - OneInchSwapRequestResponse, - OneInchSwapRequestParams - >(`${ONEINCH_SWAPS_ENDPOINT}${chainId}/swap`, { - ...swapParams, - fee: BASE_SWAP_FEE, - referrerAddress: REFERRER_ADDRESS, - allowPartialFill: false, - }) + transactionMeta.origin = 'blank'; + return await this._transactionController.estimateGas( + transactionMeta, + BigNumber.from(tx.gas) ); - - const methodSignature = - await contractSignatureParser.getMethodSignature( - res.tx.data, - res.tx.to - ); - - return { - ...res, - methodSignature, - blockWalletFee: BigNumber.from(res.fromTokenAmount) - .mul(BASE_SWAP_FEE * 10) - .div(1000), - tx: { - ...res.tx, - gas: Math.round(res.tx.gas * GAS_LIMIT_INCREASE), - }, - }; } catch (error) { - const errMessage = map1InchErrorMessage( - get1InchErrorMessageFromResponse(error) // Error should be of type RequestError - ); - throw new Error( - errMessage || 'Error getting 1Inch swap parameters' - ); + throw new Error('Unable to estimate gas'); } }; /** - * Submits a 1Inch Swap transaction + * Submits a Swap transaction * - * @param SwapTransaction 1Inch swap transaction + * @param SwapTransaction swap transaction */ - private _execute1InchSwap = async ({ + private _executeSwap = async ({ tx, flashbots, customNonce, @@ -371,7 +364,7 @@ export default class SwapController extends BaseController< transactionMeta.flashbots = flashbots; transactionMeta.exchangeParams = { - exchangeType: ExchangeType.SWAP_1INCH, + exchangeType: ExchangeType.SWAP_OPENOCEAN, fromToken, toToken, fromTokenAmount, @@ -385,7 +378,7 @@ export default class SwapController extends BaseController< return result; } catch (error) { - throw new Error('Error executing 1Inch Swap'); + throw new Error('Error executing Swap'); } }; } diff --git a/packages/background/src/controllers/TransactionWatcherController.ts b/packages/background/src/controllers/TransactionWatcherController.ts index d1f461015..5c8278dbb 100644 --- a/packages/background/src/controllers/TransactionWatcherController.ts +++ b/packages/background/src/controllers/TransactionWatcherController.ts @@ -1,5 +1,5 @@ import { Mutex } from 'async-mutex'; -import { isValidAddress, toChecksumAddress } from 'ethereumjs-util'; +import { isValidAddress, toChecksumAddress } from '@ethereumjs/util'; import { BigNumber } from '@ethersproject/bignumber'; import { TransactionResponse } from '@ethersproject/providers'; import { LogDescription, ParamType } from '@ethersproject/abi'; @@ -30,7 +30,6 @@ import { import { Block, Log } from '@ethersproject/abstract-provider'; import { SignedTransaction } from './erc-20/transactions/SignedTransaction'; import { TransactionArgument } from './transactions/ContractSignatureParser'; -import { showIncomingTransactionNotification } from '../utils/notifications'; import TransactionController from './transactions/TransactionController'; import { fetchBlockWithRetries } from '../utils/blockFetch'; import { isNil } from 'lodash'; @@ -95,7 +94,7 @@ export interface TransactionWatcherControllerState { export const TRANSACTION_TYPE_STATUS: { [type in WatchedTransactionType]: boolean; } = { - txlist: false, + txlist: true, tokentx: true, tokennfttx: false, token1155tx: false, @@ -202,34 +201,6 @@ export class TransactionWatcherController extends BaseController { - let section: - | '' - | 'tokentxns' - | 'tokentxnsErc721' - | 'tokentxnsErc1155' = ''; - switch (transactionType) { - case WatchedTransactionType.ERC20: - section = 'tokentxns'; - break; - case WatchedTransactionType.ERC721: - section = 'tokentxnsErc721'; - break; - case WatchedTransactionType.ERC1155: - section = 'tokentxnsErc1155'; - break; - } - showIncomingTransactionNotification(address, chainId, section); - } - ); - this._fetchPendingTimestampsFromChain(); } @@ -1470,19 +1441,29 @@ export class TransactionWatcherController extends BaseController { + const allTransactions = this._transactionController.getTransactions(); + if (Object.keys(currentTransactions).length) { for (const transactionHash in newTransactions) { if ( this._sameAddress( newTransactions[transactionHash].transactionParams.to, address + ) && + // Discard incoming transaction if it's from a BlockWallet swap + !allTransactions.find( + (tx) => + tx.transactionParams.hash === transactionHash && + tx.transactionCategory === + TransactionCategories.EXCHANGE ) ) { this.emit( TransactionWatcherControllerEvents.INCOMING_TRANSACTION, chainId, address, - transactionType + transactionType, + newTransactions[transactionHash] ); break; } diff --git a/packages/background/src/controllers/UDController.ts b/packages/background/src/controllers/UDController.ts index ecf0533b2..b5374cc14 100644 --- a/packages/background/src/controllers/UDController.ts +++ b/packages/background/src/controllers/UDController.ts @@ -20,13 +20,13 @@ export class UDController { Layer1: { url: this._networkController.searchNetworkByName( 'mainnet' - ).rpcUrls[0], + ).currentRpcUrl, network: 'mainnet', }, Layer2: { url: this._networkController.searchNetworkByName( 'polygon' - ).rpcUrls[0], + ).currentRpcUrl, network: 'polygon-mainnet', }, }, diff --git a/packages/background/src/controllers/block-updates/BlockFetchController.ts b/packages/background/src/controllers/block-updates/BlockFetchController.ts index 843a9d52d..e5e3e6f7c 100644 --- a/packages/background/src/controllers/block-updates/BlockFetchController.ts +++ b/packages/background/src/controllers/block-updates/BlockFetchController.ts @@ -10,6 +10,7 @@ import { MILISECOND } from '../../utils/constants/time'; export const BLOCKS_TO_WAIT_BEFORE_CHECHKING_FOR_CHAIN_SUPPORT = 100; const OFF_CHAIN_BLOCK_FETCH_SERVICE_URL = 'https://chain-fee.blockwallet.io/v1'; const OFF_CHAIN_BLOCK_FETCH_SERVICE_MAX_REPEATED_BLOCKS_TOLERANCE = 100; +const OFF_CHAIN_BLOCK_CUSTOM_HEADER = { wallet: 'BlockWallet' }; const API_CALLS_DELAY = 100 * MILISECOND; const API_CALLS_RETRIES = 5; @@ -288,7 +289,7 @@ export default class BlockFetchController extends BaseController - httpClient.get<{ + httpClient.request<{ bn: string; }>(`${OFF_CHAIN_BLOCK_FETCH_SERVICE_URL}/bn`, { - c: chainId, + params: { + c: chainId, + }, + headers: OFF_CHAIN_BLOCK_CUSTOM_HEADER, }), API_CALLS_DELAY, API_CALLS_RETRIES diff --git a/packages/background/src/controllers/erc-20/Token.ts b/packages/background/src/controllers/erc-20/Token.ts index 0705e229b..1413ead52 100644 --- a/packages/background/src/controllers/erc-20/Token.ts +++ b/packages/background/src/controllers/erc-20/Token.ts @@ -1,4 +1,4 @@ -import { BigNumber } from 'ethers'; +import { BigNumber } from '@ethersproject/bignumber'; export interface IL1Bridge { tokenAddress: string; @@ -36,6 +36,7 @@ export class Token implements IToken { decimals: number; l1Bridge?: IL1Bridge; totalSupply?: BigNumber; + order?: number; constructor( address: string, diff --git a/packages/background/src/controllers/erc-20/TokenController.ts b/packages/background/src/controllers/erc-20/TokenController.ts index a63523378..8c5da1d9f 100644 --- a/packages/background/src/controllers/erc-20/TokenController.ts +++ b/packages/background/src/controllers/erc-20/TokenController.ts @@ -17,7 +17,7 @@ import { isHexPrefixed, isValidAddress, toChecksumAddress, -} from 'ethereumjs-util'; +} from '@ethereumjs/util'; import { PreferencesController } from '../PreferencesController'; import { Mutex } from 'async-mutex'; import initialState from '../../utils/constants/initialState'; @@ -677,7 +677,8 @@ export class TokenController extends BaseController { public async searchTokenWithTotalSupply( tokenAddress: string - ): Promise { + ): Promise<{ token: Token; fetchFailed: boolean }> { + let fetchFailed = false; const { tokens } = await this.search(tokenAddress); let token = tokens[0]; @@ -691,15 +692,15 @@ export class TokenController extends BaseController { this._networkController.network.chainId, true ); + fetchFailed = updatedToken.fetchFailed; if (updatedToken.token.totalSupply) { token = mergeTokens(updatedToken.token, token); } } - - if (!token.totalSupply) { - throw new Error('Unable to get token total supply'); - } - return token; + return { + fetchFailed, + token, + }; } /** diff --git a/packages/background/src/controllers/erc-20/TokenList.ts b/packages/background/src/controllers/erc-20/TokenList.ts index c43ee3f27..0e5702902 100644 --- a/packages/background/src/controllers/erc-20/TokenList.ts +++ b/packages/background/src/controllers/erc-20/TokenList.ts @@ -1,6 +1,10 @@ /* eslint-disable @typescript-eslint/no-var-requires */ import { INetworkTokens, IToken } from './Token'; -import { TOKENS_LIST } from '@block-wallet/chains-assets'; +import { + TOKENS_LIST, + ASSETS_BLOCKCHAINS_CHAIN_ID, +} from '@block-wallet/chains-assets'; +import browser from 'webextension-polyfill'; export const GOBLANK_TOKEN_DATA: { addresses: { [chainId in number]: string }; @@ -18,7 +22,7 @@ export const GOBLANK_TOKEN_DATA: { symbol: 'BLANK', type: 'ERC20', decimals: 18, - logo: chrome.runtime.getURL('icons/icon-48.png'), + logo: browser.runtime.getURL('icons/icon-48.png'), }; export const getBlankTokenDataByChainId = ( @@ -120,37 +124,6 @@ const NETWORK_TOKENS_LIST: INetworkTokens = { 1337: {}, // localhost }; -const NETWORKS_NAMES: { [key in number]: string } = { - 42161: 'arbitrum', - 43114: 'avalanchec', - 820: 'callisto', - 42220: 'celo', - 61: 'classic', - 64: 'ellaism', - 59: 'eos', - 1313114: 'ether-1', - 1: 'ethereum', - 250: 'fantom', - 60: 'gochain', - 1666600000: 'harmony', - 128: 'heco', - 4689: 'iotex', - 71393: 'nervos', - 58: 'ontology', - 10: 'optimism', - 77: 'poa', - 137: 'polygon', - 10000: 'smartbch', - 56: 'smartchain', - 361: 'theta', - 108: 'thundertoken:', - 88: 'tomochain', - 888: 'wanchain', - 100: 'xdai', - 50: 'xdc', - 280: 'zksync', -}; - export const NETWORK_TOKENS_LIST_ARRAY: { [chainId in number]: string[] } = {}; for (const chainId in TOKENS_LIST) { @@ -165,7 +138,7 @@ for (const chainId in TOKENS_LIST) { logo = 'https://' + token['l']; } else { logo = `https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/${ - NETWORKS_NAMES[parseInt(chainId)] + ASSETS_BLOCKCHAINS_CHAIN_ID[parseInt(chainId)] }/assets/${address}/logo.png`; } @@ -235,5 +208,4 @@ for (const chainId in NETWORK_TOKENS_LIST) { NETWORK_TOKENS_LIST[parseInt(chainId)] ); } - export default NETWORK_TOKENS_LIST; diff --git a/packages/background/src/controllers/erc-20/transactions/ApproveTransaction.ts b/packages/background/src/controllers/erc-20/transactions/ApproveTransaction.ts index 7f1839199..36181d256 100644 --- a/packages/background/src/controllers/erc-20/transactions/ApproveTransaction.ts +++ b/packages/background/src/controllers/erc-20/transactions/ApproveTransaction.ts @@ -1,7 +1,7 @@ import { BigNumber } from '@ethersproject/bignumber'; import { MaxUint256 } from '@ethersproject/constants'; import { PopulatedTransaction } from '@ethersproject/contracts'; -import { isHexString, isValidAddress, stripHexPrefix } from 'ethereumjs-util'; +import { isHexString, isValidAddress, stripHexPrefix } from '@ethereumjs/util'; import { TransactionGasEstimation } from '../../transactions/TransactionController'; import { TransactionCategories, diff --git a/packages/background/src/controllers/transactions/ContractSignatureParser.ts b/packages/background/src/controllers/transactions/ContractSignatureParser.ts index a53f74804..6cf20bda2 100644 --- a/packages/background/src/controllers/transactions/ContractSignatureParser.ts +++ b/packages/background/src/controllers/transactions/ContractSignatureParser.ts @@ -236,19 +236,18 @@ export class ContractSignatureParser { // this retry is for network/http errors const result = await retryHandling( () => - httpClient.get<{ + httpClient.request<{ status: string; result: string; message?: string; - }>( - `${etherscanAPI}/api`, - { + }>(`${etherscanAPI}/api`, { + params: { module: 'contract', action: 'getabi', address, }, - 30000 - ), + timeout: 30000, + }), API_CALLS_DELAY ); @@ -342,10 +341,12 @@ export class ContractSignatureParser { try { fourByteResponse = await retryHandling( () => - httpClient.get( + httpClient.request( `https://www.4byte.directory/api/v1/signatures/`, { - hex_signature: bytes, + params: { + hex_signature: bytes, + }, } ), API_CALLS_DELAY diff --git a/packages/background/src/controllers/transactions/TransactionController.ts b/packages/background/src/controllers/transactions/TransactionController.ts index 55226be8a..180e8dcf1 100644 --- a/packages/background/src/controllers/transactions/TransactionController.ts +++ b/packages/background/src/controllers/transactions/TransactionController.ts @@ -10,16 +10,19 @@ import { import log from 'loglevel'; import { addHexPrefix, - bnToHex, + bigIntToBuffer, + bigIntToHex, bufferToHex, isValidAddress, + isValidSignature, toChecksumAddress, -} from 'ethereumjs-util'; +} from '@ethereumjs/util'; import { TransactionFactory, TypedTransaction } from '@ethereumjs/tx'; import { v4 as uuid } from 'uuid'; import { Mutex } from 'async-mutex'; import { MetaType, + QRTransactionParams, TransactionCategories, TransactionEvents, TransactionMeta, @@ -47,7 +50,6 @@ import PermissionsController from '../PermissionsController'; import { GasPricesController } from '../GasPricesController'; import { ContractSignatureParser } from './ContractSignatureParser'; import { BaseController } from '../../infrastructure/BaseController'; -import { showTransactionNotification } from '../../utils/notifications'; import { reverse } from '../../utils/array'; import { TokenController } from '../erc-20/TokenController'; import { ApproveTransaction } from '../erc-20/transactions/ApproveTransaction'; @@ -67,9 +69,12 @@ import { NFTContract } from '../erc-721/NFTContract'; import httpClient from '../../utils/http'; import { fetchBlockWithRetries } from '../../utils/blockFetch'; import { unixTimestampToJSTimestamp } from '../../utils/timestamp'; -import { cloneDeep } from 'lodash'; +import { cloneDeep, isNil } from 'lodash'; import { isUnlimitedAllowance } from '../../utils/token'; import { fetchContractDetails } from '../../utils/contractsInfo'; +import KeyringControllerDerivated, { + KeyringControllerEvents, +} from '../KeyringControllerDerivated'; /** * It indicates the amount of blocks to wait after marking @@ -276,14 +281,17 @@ export class TransactionController extends BaseController< private readonly _gasPricesController: GasPricesController, private readonly _tokenController: TokenController, private readonly _blockUpdatesController: BlockUpdatesController, + private readonly _keyringController: KeyringControllerDerivated, initialState: TransactionControllerState, /** * Method used to sign transactions */ public sign: ( + transactionId: string, transaction: TypedTransaction, from: string ) => Promise, + public config: { txHistoryLimit: number; } = { @@ -352,10 +360,30 @@ export class TransactionController extends BaseController< this._blockUpdatesCallback ); - // Show browser notification on transaction status update - this.subscribeNotifications(); + // Subscription to QR signature requests + this._keyringController.on( + KeyringControllerEvents.QR_TRANSACTION_SIGNATURE_REQUEST_GENERATED, + this.updateTransactionQRSignatureRequest + ); } + // Sets a qrSignRequest in a transaction meta object + private updateTransactionQRSignatureRequest = async ( + transactionId: string, + requestId: string, + qrSignRequest: string[] + ) => { + const transactionMeta = this.getTransaction(transactionId); + if (transactionMeta) { + transactionMeta.qrParams = { + requestId, + qrSignRequest, + } as QRTransactionParams; + + this.updateTransaction(transactionMeta); + } + }; + /** * _blockUpdatesCallback * @@ -423,20 +451,6 @@ export class TransactionController extends BaseController< }, {} as { [key: string]: TransactionMeta }); }; - private subscribeNotifications() { - this.on( - TransactionEvents.STATUS_UPDATE, - (transactionMeta: TransactionMeta) => { - if ( - transactionMeta.status === TransactionStatus.CONFIRMED || - transactionMeta.status === TransactionStatus.FAILED - ) { - showTransactionNotification(transactionMeta); - } - } - ); - } - /** * Queries for transaction statuses * @@ -579,6 +593,8 @@ export class TransactionController extends BaseController< transactionMeta.advancedData = advancedData; transactionMeta.approveAllowanceParams = approveAllowanceParams; + // Ensure that approve transactions do not send anything in the value parameter to prevent potential native asset steal. + transaction.value = undefined; } // Push transaction so extension can trigger window without waiting for gas values @@ -654,7 +670,7 @@ export class TransactionController extends BaseController< ); // Get token data - const token = + const { token, fetchFailed } = await this._tokenController.searchTokenWithTotalSupply( transactionMeta.transactionParams.to! ); @@ -676,7 +692,7 @@ export class TransactionController extends BaseController< token, }; - if (!token.decimals) { + if (fetchFailed || !token.decimals || !token.totalSupply) { // Check if it is an NFT const nftContract = new NFTContract({ networkController: this._networkController, @@ -687,13 +703,19 @@ export class TransactionController extends BaseController< _value ); - if (tokenURI) { - advancedData = { - tokenId: _value, - }; - } else { - throw new Error('Failed fetching token data'); + if (isNil(tokenURI)) { + throw new Error('Unable to get token URI from contract.'); } + + //TODO remove advanced data (check where it use and run proper migrations) + advancedData = { + tokenId: _value, + }; + approveAllowanceParams.tokenId = _value; + approveAllowanceParams.token = { + ...approveAllowanceParams.token, + type: 'ERC721', + }; } else { advancedData = { decimals: token.decimals, @@ -731,23 +753,12 @@ export class TransactionController extends BaseController< BigNumber.from(feeData.maxPriorityFeePerGas); } } - } else { - // Gas price - if (!transactionMeta.transactionParams.gasPrice) { - if (feeData.gasPrice) { - transactionMeta.transactionParams.gasPrice = BigNumber.from( - feeData.gasPrice - ); - } - } - } - /** - * Checks if the network is compatible with EIP1559 but the - * the transaction is legacy and then Transforms the gas configuration - * of the legacy transaction to the EIP1559 fee data. - */ - if (chainIsEIP1559Compatible) { + /** + * Checks if the network is compatible with EIP1559 but the + * the transaction is legacy and then Transforms the gas configuration + * of the legacy transaction to the EIP1559 fee data. + */ if ( getTransactionType(transactionMeta.transactionParams) != TransactionType.FEE_MARKET_EIP1559 @@ -759,6 +770,19 @@ export class TransactionController extends BaseController< transactionMeta.transactionParams.gasPrice; transactionMeta.transactionParams.gasPrice = undefined; } + } else { + // Gas price + if (!transactionMeta.transactionParams.gasPrice) { + if (feeData.gasPrice) { + transactionMeta.transactionParams.gasPrice = BigNumber.from( + feeData.gasPrice + ); + } + } + + // If the network is not EIP-1559 compatible, we remove maxPriority and maxFee parameters in case they come with a value, specially from dApps. + transactionMeta.transactionParams.maxPriorityFeePerGas = undefined; + transactionMeta.transactionParams.maxFeePerGas = undefined; } return transactionMeta; @@ -894,6 +918,9 @@ export class TransactionController extends BaseController< let provider: StaticJsonRpcProvider; if (!transactionMeta) { + // Release approve lock + releaseLock(); + throw new Error('The specified transaction does not exist'); } @@ -914,7 +941,8 @@ export class TransactionController extends BaseController< const { APPROVED: status } = TransactionStatus; let txNonce = nonce; - if (!txNonce) { + // this includes 0 as posible value + if (typeof txNonce === 'undefined' || txNonce === undefined) { // Get new nonce nonceLock = await this._nonceTracker.getNonceLock(from!); txNonce = nonceLock.nextNonce; @@ -936,6 +964,9 @@ export class TransactionController extends BaseController< transactionMeta.transactionParams.data! ); transactionMeta.methodSignature = methodSignature; + + // Ensure that approve transactions do not send anything in the value parameter to prevent potential native asset steal. + transactionMeta.transactionParams.value = undefined; } transactionMeta.status = status; @@ -981,6 +1012,9 @@ export class TransactionController extends BaseController< from! ); + // remove QR + transactionMeta.qrParams = undefined; + // Check for HW flow cancellation if (this.isTransactionRejected(transactionID)) { return; @@ -990,10 +1024,10 @@ export class TransactionController extends BaseController< transactionMeta.status = TransactionStatus.SIGNED; // Set r,s,v values - transactionMeta.transactionParams.r = bnToHex(signedTx.r!); - transactionMeta.transactionParams.s = bnToHex(signedTx.s!); + transactionMeta.transactionParams.r = bigIntToHex(signedTx.r!); + transactionMeta.transactionParams.s = bigIntToHex(signedTx.s!); transactionMeta.transactionParams.v = BigNumber.from( - bnToHex(signedTx.v!) + bigIntToHex(signedTx.v!) ).toNumber(); // Serialize transaction & update @@ -1110,7 +1144,7 @@ export class TransactionController extends BaseController< timeoutRef = setTimeout(() => { reject(new SignTimeoutError()); }, txTimeout); - this.sign(unsignedEthTx, from) + this.sign(transactionId, unsignedEthTx, from) .then(resolve) .catch(reject) .finally(__clearTimeouts); @@ -1146,8 +1180,29 @@ export class TransactionController extends BaseController< ); // Add r,s,v values - if (!signedTx.r || !signedTx.s || !signedTx.v) + if ( + !signedTx.r || + !signedTx.s || + // for eip1559 v is 0. + // https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/tx/src/eip1559Transaction.ts#L382 + (!signedTx.v && signedTx.v?.toString() !== '0') + ) + throw new Error('An error while signing the transaction ocurred'); + + if ( + !isValidSignature( + signedTx.v, + bigIntToBuffer(signedTx.r), + bigIntToBuffer(signedTx.s), + undefined, + BigInt(this._networkController.network.chainId) + ) + ) { throw new Error('An error while signing the transaction ocurred'); + } + + // recover and validate the address from the signature + signedTx.getSenderAddress(); return signedTx; } @@ -1168,6 +1223,27 @@ export class TransactionController extends BaseController< this.updateTransaction(transactionMeta); } + /** + * Updates transaction status + * + * @param transactionID - The ID of the transaction to update. + * @param status - the new transaction status + */ + public updateTransactionStatus( + transactionID: string, + status: TransactionStatus + ): void { + const transactionMeta = this.getTransaction(transactionID); + if (!transactionMeta) { + throw new Error('The specified transaction does not exist'); + } + transactionMeta.status = status; + if (status === TransactionStatus.REJECTED) { + this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta); + } + this.updateTransaction(transactionMeta); + } + /** * Rejects a replacement transaction based on its ID by setting its status to `TransactionStatus.REJECTED` * @param transactionID The ID of the replacement transaction to reject. @@ -1457,9 +1533,9 @@ export class TransactionController extends BaseController< newTransactionMeta.status = TransactionStatus.SIGNED; newTransactionMeta.transactionParams = { ...newTransactionMeta.transactionParams, - r: bnToHex(signedTx.r!), - s: bnToHex(signedTx.s!), - v: BigNumber.from(bnToHex(signedTx.v!)).toNumber(), + r: bigIntToHex(signedTx.r!), + s: bigIntToHex(signedTx.s!), + v: BigNumber.from(bigIntToHex(signedTx.v!)).toNumber(), }; this.store.updateState({ transactions: this.trimTransactionsForState(transactions), @@ -1662,9 +1738,9 @@ export class TransactionController extends BaseController< newTransactionMeta.status = TransactionStatus.SIGNED; newTransactionMeta.transactionParams = { ...newTransactionMeta.transactionParams, - r: bnToHex(signedTx.r!), - s: bnToHex(signedTx.s!), - v: BigNumber.from(bnToHex(signedTx.v!)).toNumber(), + r: bigIntToHex(signedTx.r!), + s: bigIntToHex(signedTx.s!), + v: BigNumber.from(bigIntToHex(signedTx.v!)).toNumber(), }; this.store.updateState({ transactions: this.trimTransactionsForState(transactions), @@ -2240,7 +2316,7 @@ export class TransactionController extends BaseController< ): Promise<[TransactionMeta, boolean]> { const baseUrl = 'https://protect.flashbots.net/tx/'; - const response = await httpClient.get( + const response = await httpClient.request( baseUrl + meta.transactionParams.hash ); @@ -2400,6 +2476,18 @@ export class TransactionController extends BaseController< error ); + //Transaction will fail no matter how much gas the user sets + if ( + error.reason && + error.reason.match( + /Transfer amount must be greater than zero/gi + ) + ) { + throw new Error( + 'This token only allows transfers bigger than zero.' + ); + } + const hasFixedGasCost = this._networkController.hasChainFixedGasCost( txOrCurrentChainId diff --git a/packages/background/src/controllers/transactions/utils/types.ts b/packages/background/src/controllers/transactions/utils/types.ts index e80d104bc..0498eb15c 100644 --- a/packages/background/src/controllers/transactions/utils/types.ts +++ b/packages/background/src/controllers/transactions/utils/types.ts @@ -99,6 +99,12 @@ export interface TransactionMeta { exchangeParams?: ExchangeParams; bridgeParams?: BridgeTransactionParams; approveAllowanceParams?: ApproveAllowanceParams; + qrParams?: QRTransactionParams; +} + +export interface QRTransactionParams { + requestId: string; + qrSignRequest: string[]; } export interface BridgeTransactionParams { @@ -134,6 +140,8 @@ export interface ApproveAllowanceParams { isUnlimited: boolean; spenderInfo?: ContractDetails; token: Token; + //In case it is an NFT + tokenId?: BigNumber; } export interface uiTransactionParams extends TransactionParams { @@ -241,3 +249,9 @@ export enum WatchedTransactionType { ERC721 = 'tokennfttx', ERC1155 = 'token1155tx', } + +export interface SignatureData { + v: bigint; + r: Buffer; + s: Buffer; +} diff --git a/packages/background/src/controllers/transactions/utils/utils.ts b/packages/background/src/controllers/transactions/utils/utils.ts index 6439ede47..7f5db53b5 100644 --- a/packages/background/src/controllers/transactions/utils/utils.ts +++ b/packages/background/src/controllers/transactions/utils/utils.ts @@ -1,4 +1,4 @@ -import { addHexPrefix, isHexString, isValidAddress } from 'ethereumjs-util'; +import { addHexPrefix, isHexString, isValidAddress } from '@ethereumjs/util'; import { BigNumber } from '@ethersproject/bignumber'; import { TransactionMeta, TransactionParams, TransactionType } from './types'; import ensNamehash from 'eth-ens-namehash'; @@ -74,7 +74,7 @@ export function normalizeTransaction( /** * Validates that the input is a hex address. This utility method is a thin - * wrapper around ethereumjs-util.isValidAddress, with the exception that it + * wrapper around @ethereumjs/util.isValidAddress, with the exception that it * does not throw an error when provided values that are not hex strings. In * addition, and by default, this method will return true for hex strings that * meet the length requirement of a hex address, but are not prefixed with `0x` diff --git a/packages/background/src/index.ts b/packages/background/src/index.ts index 4adf8d2ce..ad4c5a6f3 100644 --- a/packages/background/src/index.ts +++ b/packages/background/src/index.ts @@ -7,7 +7,7 @@ import BlankController, { import BlankStorageStore from './infrastructure/stores/BlankStorageStore'; import initialState, { BlankAppState } from './utils/constants/initialState'; import reconcileState from './infrastructure/stores/migrator/reconcileState'; -import compareVersions from 'compare-versions'; +import { compareVersions } from 'compare-versions'; import { getVersion, openExtensionInBrowser } from './utils/window'; import { setupConnection } from './infrastructure/connection'; import { migrator } from './infrastructure/stores/migrator/migrator'; @@ -16,8 +16,10 @@ import log, { LogLevelDesc } from 'loglevel'; import { resolvePreferencesAfterWalletUpdate } from './utils/userPreferences'; import { CONTENT } from './utils/types/communication'; import { isManifestV3 } from './utils/manifest'; +import browser from 'webextension-polyfill'; // Initialize Block State Store +console.log('blankStateStore'); const blankStateStore = new BlankStorageStore(); /** @@ -101,11 +103,11 @@ const getDevTools = () => { */ const updateExtensionBadge = (label: string) => { if (isManifestV3()) { - chrome.action.setBadgeText({ text: label }); - chrome.action.setBadgeBackgroundColor({ color: '#1673FF' }); // BlockWallet primary color + browser.action.setBadgeText({ text: label }); + browser.action.setBadgeBackgroundColor({ color: '#1673FF' }); // BlockWallet primary color } else { - chrome.browserAction.setBadgeText({ text: label }); - chrome.browserAction.setBadgeBackgroundColor({ color: '#1673FF' }); // BlockWallet primary color + browser.browserAction.setBadgeText({ text: label }); + browser.browserAction.setBadgeBackgroundColor({ color: '#1673FF' }); // BlockWallet primary color } }; @@ -114,6 +116,7 @@ const updateExtensionBadge = (label: string) => { * */ const initBlockWallet = async () => { + console.log('initBlockWallet'); // Get persisted state const initState = await getPersistedState; @@ -146,16 +149,18 @@ const initBlockWallet = async () => { ); // Setup connection - chrome.runtime.onConnect.addListener((port) => { + browser.runtime.onConnect.addListener((port) => { setupConnection(port, blankController); }); // Set isBlankInitialized response and should inject response - chrome.runtime.onMessage.addListener((request, _, sendResponse) => { + browser.runtime.onMessage.addListener((request, _, sendResponse) => { if (request.message === 'isBlankInitialized') { - sendResponse({ isBlankInitialized: true }); + return Promise.resolve({ isBlankInitialized: true }); } else if (request.message === CONTENT.SHOULD_INJECT) { - sendResponse({ shouldInject: blankController.shouldInject() }); + return Promise.resolve({ + shouldInject: blankController.shouldInject(), + }); } else if (request.message === CONTENT.SW_KEEP_ALIVE) { sendResponse(); } @@ -173,27 +178,30 @@ const initBlockWallet = async () => { log.setLevel((process.env.LOG_LEVEL as LogLevelDesc) || 'error'); }; +console.log('index.ts start'); + // Start block wallet initBlockWallet().catch((error) => { log.error(error.message || error); }); // On install, open onboarding tab -chrome.runtime.onInstalled.addListener(({ reason }) => { +browser.runtime.onInstalled.addListener(({ reason }) => { if (reason === 'install') { - chrome.runtime.setUninstallURL('https://forms.gle/g4RghfndrhwPS6L76'); + browser.runtime.setUninstallURL('https://forms.gle/g4RghfndrhwPS6L76'); openExtensionInBrowser(); } // For existing users, when the extension gets updated we also set the uninstall form. if (reason === 'update') { - chrome.runtime.setUninstallURL('https://forms.gle/g4RghfndrhwPS6L76'); + browser.runtime.setUninstallURL('https://forms.gle/g4RghfndrhwPS6L76'); } }); const registerBlankProviderContentScript = async () => { + console.log('registerBlankProviderContentScript'); try { - await (chrome.scripting as any).registerContentScripts([ + await (browser.scripting as any).registerContentScripts([ { id: 'blankProvider', matches: ['file://*/*', 'http://*/*', 'https://*/*'], @@ -210,11 +218,12 @@ const registerBlankProviderContentScript = async () => { }; if (isManifestV3()) { + console.log('v3'); // this keeps alive the service worker. // when it goes 'inactive' it is restarted. - chrome.alarms.create({ delayInMinutes: 0.5, periodInMinutes: 0.05 }); - chrome.alarms.onAlarm.addListener(() => { - fetch(chrome.runtime.getURL('keep-alive')); + browser.alarms.create({ delayInMinutes: 0.5, periodInMinutes: 0.05 }); + browser.alarms.onAlarm.addListener(() => { + fetch(browser.runtime.getURL('keep-alive')); }); registerBlankProviderContentScript(); } diff --git a/packages/background/src/infrastructure/connection.ts b/packages/background/src/infrastructure/connection.ts index a3f9ff58d..3b9febfb4 100644 --- a/packages/background/src/infrastructure/connection.ts +++ b/packages/background/src/infrastructure/connection.ts @@ -8,6 +8,7 @@ import { isOnboardingTabUrl } from '../utils/window'; import { v4 as uuid } from 'uuid'; import BlankController from '../controllers/BlankController'; import log from 'loglevel'; +import browser from 'webextension-polyfill'; export const extensionInstances: ExtensionInstances = {}; export const providerInstances: ProviderInstances = {}; @@ -19,7 +20,7 @@ export const providerInstances: ProviderInstances = {}; * @param blankController blank controller running instance */ export const setupConnection = ( - port: chrome.runtime.Port, + port: browser.Runtime.Port, blankController: BlankController ): void => { // Ignore Trezor content script messages @@ -77,15 +78,15 @@ export const setupConnection = ( } // Setup listeners - const messageListener = (message: any, port: chrome.runtime.Port) => { + const messageListener = (message: any, port: browser.Runtime.Port) => { blankController.handler(message, port, id); }; port.onMessage.addListener(messageListener); - port.onDisconnect.addListener((port: chrome.runtime.Port) => { + port.onDisconnect.addListener((port: browser.Runtime.Port) => { // Check for error - const error = chrome.runtime.lastError; + const error = browser.runtime.lastError; if (error) { log.error('Error on port disconnection', error.message || error); diff --git a/packages/background/src/infrastructure/hardware/trezor/trezor-content.ts b/packages/background/src/infrastructure/hardware/trezor/trezor-content.ts index 80d8a171f..b32c664a9 100644 --- a/packages/background/src/infrastructure/hardware/trezor/trezor-content.ts +++ b/packages/background/src/infrastructure/hardware/trezor/trezor-content.ts @@ -1,12 +1,13 @@ import { Origin } from '../../../utils/types/communication'; +import browser from 'webextension-polyfill'; // Setup port connection -let port: chrome.runtime.Port | null = chrome.runtime.connect({ +let port: browser.Runtime.Port | null = browser.runtime.connect({ name: Origin.TREZOR_CONNECT, }); // Process any messages from the extension to the trezor page -port.onMessage.addListener((message) => { +port.onMessage.addListener((message: any) => { window.postMessage(message, window.location.origin); }); diff --git a/packages/background/src/infrastructure/hardware/trezor/trezor-usb-permissions.ts b/packages/background/src/infrastructure/hardware/trezor/trezor-usb-permissions.ts index e96ac4401..59023b817 100644 --- a/packages/background/src/infrastructure/hardware/trezor/trezor-usb-permissions.ts +++ b/packages/background/src/infrastructure/hardware/trezor/trezor-usb-permissions.ts @@ -1,4 +1,6 @@ -const VERSION = '8.2.8-beta.4'; +import browser from 'webextension-polyfill'; + +const VERSION = '9.0.6'; const versionN = VERSION.split('.').map((s) => parseInt(s, 10)); // const DIRECTORY = `${ versionN[0] }${ (versionN[1] > 0 ? `.${versionN[1]}` : '') }/`; const DIRECTORY = `${versionN[0]}/`; @@ -11,30 +13,25 @@ const switchToPopupTab = (event?: any) => { if (!event) { // triggered from 'usb-permissions-close' message // close current tab - chrome.tabs.query( - { - currentWindow: true, - active: true, - }, - (current) => { + browser.tabs + .query({ currentWindow: true, active: true }) + .then((current) => { if (current.length <= 0) return; const id = current[0].id; - chrome.tabs.remove(Number(id)); - } - ); + browser.tabs.remove(Number(id)); + }); } // find tab by popup pattern and switch to it - chrome.tabs.query( - { + browser.tabs + .query({ url: `${url}popup.html`, - }, - (tabs) => { + }) + .then((tabs) => { if (tabs.length <= 0) return; const id = tabs[0].id; - chrome.tabs.update(Number(id), { active: true }); - } - ); + browser.tabs.update(Number(id), { active: true }); + }); }; window.addEventListener('message', (event) => { @@ -48,7 +45,7 @@ window.addEventListener('message', (event) => { iframe.contentWindow?.postMessage( { type: 'usb-permissions-init', - extension: chrome.runtime.id, + extension: browser.runtime.id, }, '*' ); diff --git a/packages/background/src/infrastructure/stores/BaseStorageStore.ts b/packages/background/src/infrastructure/stores/BaseStorageStore.ts index 378a31604..c059db1f5 100644 --- a/packages/background/src/infrastructure/stores/BaseStorageStore.ts +++ b/packages/background/src/infrastructure/stores/BaseStorageStore.ts @@ -1,9 +1,10 @@ import log from 'loglevel'; +import browser from 'webextension-polyfill'; type StoreValue = Record; const lastError = (type: string): void => { - const error = chrome.runtime.lastError; + const error = browser.runtime.lastError; if (error) { log.error('Store', type, 'runtime.lastError', error.message || error); @@ -23,12 +24,14 @@ export default abstract class BaseStorageStore { public getVersion(): Promise { const key = `${this.prefix}version`; return new Promise((resolve) => { - chrome.storage.local.get([key], (result: StoreValue): void => { - lastError('getVersion'); - key in result - ? resolve(result[key] as string) - : resolve(undefined); - }); + browser.storage.local + .get([key]) + .then((result: StoreValue): void => { + lastError('getVersion'); + key in result + ? resolve(result[key] as string) + : resolve(undefined); + }); }); } @@ -39,7 +42,7 @@ export default abstract class BaseStorageStore { const key = `${this.prefix}version`; return new Promise((resolve) => { - chrome.storage.local.set({ [key]: value }, (): void => { + browser.storage.local.set({ [key]: value }).then((): void => { lastError('setVersion'); resolve(); }); @@ -47,7 +50,7 @@ export default abstract class BaseStorageStore { } public all(update: (key: string, value: T) => void): void { - chrome.storage.local.get(null, (result: StoreValue): void => { + browser.storage.local.get(null).then((result: StoreValue): void => { lastError('all'); Object.entries(result) @@ -61,7 +64,7 @@ export default abstract class BaseStorageStore { public get(_key: string, update: (value: T) => void): void { const key = `${this.prefix}${_key}`; - chrome.storage.local.get([key], (result: StoreValue): void => { + browser.storage.local.get([key]).then((result: StoreValue): void => { lastError('get'); update(result[key] as T); @@ -71,7 +74,7 @@ export default abstract class BaseStorageStore { public remove(_key: string, update?: () => void): void { const key = `${this.prefix}${_key}`; - chrome.storage.local.remove(key, (): void => { + browser.storage.local.remove(key).then((): void => { lastError('remove'); update && update(); @@ -81,7 +84,7 @@ export default abstract class BaseStorageStore { public set(_key: string, value: T, update?: () => void): void { const key = `${this.prefix}${_key}`; - chrome.storage.local.set({ [key]: value }, (): void => { + browser.storage.local.set({ [key]: value }).then((): void => { lastError('set'); update && update(); diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/index.ts b/packages/background/src/infrastructure/stores/migrator/migrations/index.ts index 6a73648df..0bd2d9537 100644 --- a/packages/background/src/infrastructure/stores/migrator/migrations/index.ts +++ b/packages/background/src/infrastructure/stores/migrator/migrations/index.ts @@ -59,6 +59,20 @@ import migration57 from './migration-57'; import migration58 from './migration-58'; import migration59 from './migration-59'; import migration60 from './migration-60'; +import migration61 from './migration-61'; +import migration62 from './migration-62'; +import migration63 from './migration-63'; +import migration64 from './migration-64'; +import migration65 from './migration-65'; +import migration66 from './migration-66'; +import migration67 from './migration-67'; +import migration68 from './migration-68'; +import migration69 from './migration-69'; +import migration70 from './migration-70'; +import migration71 from './migration-71'; +import migration72 from './migration-72'; +import migration73 from './migration-73'; +import migration74 from './migration-74'; const migrations: IMigration[] = [ migration01, @@ -121,5 +135,20 @@ const migrations: IMigration[] = [ migration58, migration59, migration60, + migration61, + migration62, + migration63, + migration64, + migration65, + migration66, + migration67, + migration68, + migration69, + migration70, + migration71, + migration72, + migration73, + migration74, ]; + export default (): IMigration[] => migrations; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-23.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-23.ts index 683ab3e9b..cad59df30 100644 --- a/packages/background/src/infrastructure/stores/migrator/migrations/migration-23.ts +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-23.ts @@ -1,6 +1,6 @@ import { ITokens } from '@block-wallet/background/controllers/erc-20/Token'; import { BlankAppState } from '@block-wallet/background/utils/constants/initialState'; -import { isValidAddress, toChecksumAddress } from 'ethereumjs-util'; +import { isValidAddress, toChecksumAddress } from '@ethereumjs/util'; import { IMigration } from '../IMigration'; /** * This migration updates user token's addresses from the user's token list diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-24.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-24.ts index ed9377f70..d5147566e 100644 --- a/packages/background/src/infrastructure/stores/migrator/migrations/migration-24.ts +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-24.ts @@ -1,6 +1,6 @@ import { BlankAppState } from '@block-wallet/background/utils/constants/initialState'; import { IMigration } from '../IMigration'; -import { toChecksumAddress } from 'ethereumjs-util'; +import { toChecksumAddress } from '@ethereumjs/util'; /** * This migration removes a spam token from the user's token */ diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-48.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-48.ts index a1fa0e78f..19eb653a3 100644 --- a/packages/background/src/infrastructure/stores/migrator/migrations/migration-48.ts +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-48.ts @@ -15,39 +15,6 @@ export default { const { availableNetworks } = persistedState.NetworkController; const updatedNetworks = { ...availableNetworks }; - updatedNetworks.SCROLL_L1_TESTNET = { - name: 'scroll_l1_testnet', - desc: 'Scroll L1 Testnet', - chainId: 534351, - networkVersion: '534351', - nativeCurrency: { - name: 'Ether', - symbol: 'TSETH', - decimals: 18, - }, - iconUrls: [ - 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/scroll/info/logo.png', - ], - isCustomNetwork: true, - enable: true, - test: true, - order: 8, - features: [FEATURES.SENDS], - ens: false, - showGasLevels: true, - rpcUrls: [`https://prealpha.scroll.io/l1`], - blockExplorerUrls: ['https://l1scan.scroll.io/'], - blockExplorerName: 'Scroll L1 Explorer', - actionsTimeIntervals: { - ...SLOW_TESTNET_TIME_INTERVALS_DEFAULT_VALUES, - }, - tornadoIntervals: { - depositConfirmations: DEFAULT_TORNADO_CONFIRMATION, - derivationsForward: DERIVATIONS_FORWARD, - }, - nativelySupported: true, - } as any; - updatedNetworks.SCROLL_L2_TESTNET = { name: 'scroll_l2_testnet', desc: 'Scroll L2 Testnet', diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-52.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-52.ts index 70896a9bc..bda26ffbb 100644 --- a/packages/background/src/infrastructure/stores/migrator/migrations/migration-52.ts +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-52.ts @@ -34,10 +34,7 @@ export default { ...updatedNetworks.ZKSYNC_ALPHA_TESTNET, showGasLevels: false, }; - updatedNetworks.SCROLL_L1_TESTNET = { - ...updatedNetworks.SCROLL_L1_TESTNET, - showGasLevels: false, - }; + updatedNetworks.SCROLL_L2_TESTNET = { ...updatedNetworks.SCROLL_L2_TESTNET, showGasLevels: false, diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-53.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-53.ts index 268bec622..52e40dc65 100644 --- a/packages/background/src/infrastructure/stores/migrator/migrations/migration-53.ts +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-53.ts @@ -33,16 +33,6 @@ export default { updatedNetworks ); - const rskTestnetNonNativeKey = networkController.getNonNativeNetworkKey( - INITIAL_NETWORKS.RSK_TESTNET.chainId - ); - updatedNetworks = addNetworkUsingValuesDefinedByTheUser( - 'RSK_TESTNET', - rskTestnetNonNativeKey, - INITIAL_NETWORKS.RSK_TESTNET, - updatedNetworks - ); - updatedNetworks.LOCALHOST = { ...updatedNetworks.LOCALHOST, order: updatedNetworks.LOCALHOST.order + 1, diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-55.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-55.ts index 080b21f23..b55ae32ef 100644 --- a/packages/background/src/infrastructure/stores/migrator/migrations/migration-55.ts +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-55.ts @@ -9,7 +9,7 @@ import { import NetworkController from '../../../../controllers/NetworkController'; import { TokenOperationsController } from '../../../../controllers/erc-20/transactions/TokenOperationsController'; import { Accounts } from '@block-wallet/background/controllers/AccountTrackerController'; -import { toChecksumAddress } from 'ethereumjs-util'; +import { toChecksumAddress } from '@ethereumjs/util'; const hasRecords = (records: Record): boolean => { return records && Object.keys(records).length > 0; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-56.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-56.ts index f18299dec..88a2760ee 100644 --- a/packages/background/src/infrastructure/stores/migrator/migrations/migration-56.ts +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-56.ts @@ -16,11 +16,6 @@ export default { desc: INITIAL_NETWORKS.RSK.desc, }; - updatedNetworks.RSK_TESTNET = { - ...updatedNetworks.RSK_TESTNET, - desc: INITIAL_NETWORKS.RSK_TESTNET.desc, - }; - const orderedNetworks = normalizeNetworksOrder(updatedNetworks); return { diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-58.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-58.ts index ef5f21e6f..4de220518 100644 --- a/packages/background/src/infrastructure/stores/migrator/migrations/migration-58.ts +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-58.ts @@ -11,11 +11,6 @@ export default { const { availableNetworks } = persistedState.NetworkController; const updatedNetworks = { ...availableNetworks }; - updatedNetworks.SCROLL_L1_TESTNET = { - ...updatedNetworks.SCROLL_L1_TESTNET, - rpcUrls: INITIAL_NETWORKS.SCROLL_L1_TESTNET.rpcUrls, - }; - updatedNetworks.SCROLL_L2_TESTNET = { ...updatedNetworks.SCROLL_L2_TESTNET, rpcUrls: INITIAL_NETWORKS.SCROLL_L2_TESTNET.rpcUrls, diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-61.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-61.ts new file mode 100644 index 000000000..f88e30f46 --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-61.ts @@ -0,0 +1,24 @@ +import { BlankAppState } from '@block-wallet/background/utils/constants/initialState'; +import { IMigration } from '../IMigration'; + +/** + * This migration adds the subscribedToNotifications flag with true value to the user preferences + */ +export default { + migrate: async (persistedState: BlankAppState) => { + return { + ...persistedState, + PreferencesController: { + ...persistedState.PreferencesController, + settings: { + ...persistedState.PreferencesController.settings, + + subscribedToNotifications: + persistedState.PreferencesController.settings + .subscribedToNotifications ?? true, + }, + }, + }; + }, + version: '1.1.1', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-62.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-62.ts new file mode 100644 index 000000000..012fd4a0e --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-62.ts @@ -0,0 +1,81 @@ +import { FEATURES } from '../../../../utils/constants/features'; +import { BlankAppState } from '../../../../utils/constants/initialState'; +import { ACTIONS_TIME_INTERVALS_DEFAULT_VALUES } from '../../../../utils/constants/networks'; +import { normalizeNetworksOrder } from '../../../../utils/networks'; +import { IMigration } from '../IMigration'; + +/** + * This migration updates Scroll and zkSync networks + */ +export default { + migrate: async (persistedState: BlankAppState) => { + const { availableNetworks } = persistedState.NetworkController; + const updatedNetworks = { ...availableNetworks }; + + // Remove Scroll L1 testnet + delete updatedNetworks['SCROLL_L1_TESTNET']; + + // Update Scroll L2 Alpha + updatedNetworks.SCROLL_L2_TESTNET = { + ...updatedNetworks.SCROLL_L2_TESTNET, + desc: 'Scroll Alpha Testnet', + chainId: 534353, + networkVersion: '534353', + blockExplorerUrls: ['https://blockscout.scroll.io/'], + }; + + // Add new zkSync Era Mainnet + updatedNetworks.ZKSYNC_ERA_MAINNET = { + name: 'zksync_era_mainnet', + desc: 'zkSync Era Mainnet', + chainId: 324, + networkVersion: '324', + nativeCurrency: { + name: 'Ether', + symbol: 'ETH', + decimals: 18, + }, + iconUrls: [ + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/zksync/info/logo.png', + ], + hasFixedGasCost: false, + enable: true, + test: false, + order: 10, + features: [FEATURES.SENDS], + ens: false, + showGasLevels: false, + rpcUrls: [`https://zksync-node.blockwallet.io`], + currentRpcUrl: `https://zksync-node.blockwallet.io`, + defaultRpcUrl: `https://zksync-node.blockwallet.io`, + blockExplorerUrls: ['https://explorer.zksync.io/'], + blockExplorerName: 'zkSync Explorer', + actionsTimeIntervals: { + ...ACTIONS_TIME_INTERVALS_DEFAULT_VALUES, + }, + tornadoIntervals: { + depositConfirmations: 0, + derivationsForward: 0, + }, + nativelySupported: true, + }; + + // Update zkSync Era Testnet + updatedNetworks.ZKSYNC_ALPHA_TESTNET = { + ...updatedNetworks.ZKSYNC_ALPHA_TESTNET, + desc: 'zkSync Era Testnet', + blockExplorerName: 'zkSync Testnet Explorer', + }; + + const orderedNetworks = normalizeNetworksOrder(updatedNetworks); + + return { + ...persistedState, + NetworkController: { + ...persistedState.NetworkController, + availableNetworks: { ...orderedNetworks }, + }, + }; + }, + version: '1.1.2', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-63.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-63.ts new file mode 100644 index 000000000..68ef0ada2 --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-63.ts @@ -0,0 +1,43 @@ +import { BlankAppState } from '@block-wallet/background/utils/constants/initialState'; +import { IMigration } from '../IMigration'; +import { INITIAL_NETWORKS } from '../../../../utils/constants/networks'; +import { normalizeNetworksOrder } from '../../../../utils/networks'; + +/** + * Update main networks RPC urls temporary due to Cloudflare block. + */ +export default { + migrate: async (persistedState: BlankAppState) => { + const { availableNetworks } = persistedState.NetworkController; + const updatedNetworks = { ...availableNetworks }; + + updatedNetworks.MAINNET = { + ...updatedNetworks.MAINNET, + rpcUrls: INITIAL_NETWORKS.MAINNET.rpcUrls, + defaultRpcUrl: INITIAL_NETWORKS.MAINNET.defaultRpcUrl, + }; + + updatedNetworks.POLYGON = { + ...updatedNetworks.POLYGON, + rpcUrls: INITIAL_NETWORKS.POLYGON.rpcUrls, + defaultRpcUrl: INITIAL_NETWORKS.POLYGON.defaultRpcUrl, + }; + + updatedNetworks.BSC = { + ...updatedNetworks.BSC, + rpcUrls: INITIAL_NETWORKS.BSC.rpcUrls, + defaultRpcUrl: INITIAL_NETWORKS.BSC.defaultRpcUrl, + }; + + const orderedNetworks = normalizeNetworksOrder(updatedNetworks); + + return { + ...persistedState, + NetworkController: { + ...persistedState.NetworkController, + availableNetworks: { ...orderedNetworks }, + }, + }; + }, + version: '1.1.3', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-64.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-64.ts new file mode 100644 index 000000000..7868d12f1 --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-64.ts @@ -0,0 +1,80 @@ +import { FEATURES } from '../../../../utils/constants/features'; +import { BlankAppState } from '../../../../utils/constants/initialState'; +import { + ACTIONS_TIME_INTERVALS_DEFAULT_VALUES, + INITIAL_NETWORKS, +} from '../../../../utils/constants/networks'; +import { normalizeNetworksOrder } from '../../../../utils/networks'; +import { IMigration } from '../IMigration'; + +/** + * This migration adds polygon zkEvm network + */ +export default { + migrate: async (persistedState: BlankAppState) => { + const { availableNetworks } = persistedState.NetworkController; + const updatedNetworks = { ...availableNetworks }; + + updatedNetworks.MAINNET = { + ...updatedNetworks.MAINNET, + rpcUrls: INITIAL_NETWORKS.MAINNET.rpcUrls, + defaultRpcUrl: INITIAL_NETWORKS.MAINNET.defaultRpcUrl, + }; + + updatedNetworks.POLYGON = { + ...updatedNetworks.POLYGON, + rpcUrls: INITIAL_NETWORKS.POLYGON.rpcUrls, + defaultRpcUrl: INITIAL_NETWORKS.POLYGON.defaultRpcUrl, + }; + + updatedNetworks.BSC = { + ...updatedNetworks.BSC, + rpcUrls: INITIAL_NETWORKS.BSC.rpcUrls, + defaultRpcUrl: INITIAL_NETWORKS.BSC.defaultRpcUrl, + }; + + // Add new Polygon zkEVM + updatedNetworks.POLYGON_ZKEVM = { + name: 'polygon_zkevm', + desc: 'Polygon zkEVM', + chainId: 1101, + networkVersion: '1101', + nativeCurrency: { + name: 'Ether', + symbol: 'ETH', + decimals: 18, + }, + iconUrls: [ + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/polygonzkevm/info/logo.png', + ], + hasFixedGasCost: false, + enable: true, + test: false, + order: 11, + features: [FEATURES.SENDS], + ens: false, + showGasLevels: false, + rpcUrls: [`https://polygon-zkevm-node.blockwallet.io`], + currentRpcUrl: `https://polygon-zkevm-node.blockwallet.io`, + defaultRpcUrl: `https://polygon-zkevm-node.blockwallet.io`, + blockExplorerUrls: ['https://zkevm.polygonscan.com/'], + blockExplorerName: 'Polygon zkEVM Explorer', + actionsTimeIntervals: { ...ACTIONS_TIME_INTERVALS_DEFAULT_VALUES }, + tornadoIntervals: { + depositConfirmations: 0, + derivationsForward: 0, + }, + nativelySupported: true, + }; + const orderedNetworks = normalizeNetworksOrder(updatedNetworks); + + return { + ...persistedState, + NetworkController: { + ...persistedState.NetworkController, + availableNetworks: { ...orderedNetworks }, + }, + }; + }, + version: '1.1.4', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-65.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-65.ts new file mode 100644 index 000000000..55f50af17 --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-65.ts @@ -0,0 +1,27 @@ +import { BlankAppState } from '@block-wallet/background/utils/constants/initialState'; +import { IMigration } from '../IMigration'; +import { FAST_TIME_INTERVALS_DEFAULT_VALUES } from '../../../../utils/constants/networks'; + +/** + * Update Mainnet with fast intervals. + */ +export default { + migrate: async (persistedState: BlankAppState) => { + const { availableNetworks } = persistedState.NetworkController; + const updatedNetworks = { ...availableNetworks }; + + updatedNetworks.MAINNET = { + ...updatedNetworks.MAINNET, + actionsTimeIntervals: FAST_TIME_INTERVALS_DEFAULT_VALUES, + }; + + return { + ...persistedState, + NetworkController: { + ...persistedState.NetworkController, + availableNetworks: { ...updatedNetworks }, + }, + }; + }, + version: '1.1.5', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-66.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-66.ts new file mode 100644 index 000000000..1eb3f5b01 --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-66.ts @@ -0,0 +1,110 @@ +import { BlankAppState } from '@block-wallet/background/utils/constants/initialState'; +import { IMigration } from '../IMigration'; +import { INITIAL_NETWORKS } from '../../../../utils/constants/networks'; +import { normalizeNetworksOrder } from '../../../../utils/networks'; + +/** + * Update Network and native currency logos + fix polygon mumbai block explorer name + */ +export default { + migrate: async (persistedState: BlankAppState) => { + const { availableNetworks } = persistedState.NetworkController; + const updatedNetworks = { ...availableNetworks }; + + updatedNetworks.MAINNET = { + ...updatedNetworks.MAINNET, + iconUrls: INITIAL_NETWORKS.MAINNET.iconUrls, + }; + + updatedNetworks.ARBITRUM = { + ...updatedNetworks.ARBITRUM, + iconUrls: INITIAL_NETWORKS.ARBITRUM.iconUrls, + nativeCurrency: { + ...updatedNetworks.ARBITRUM.nativeCurrency, + logo: INITIAL_NETWORKS.ARBITRUM.nativeCurrency.logo, + }, + }; + + updatedNetworks.OPTIMISM = { + ...updatedNetworks.OPTIMISM, + iconUrls: INITIAL_NETWORKS.OPTIMISM.iconUrls, + nativeCurrency: { + ...updatedNetworks.OPTIMISM.nativeCurrency, + logo: INITIAL_NETWORKS.OPTIMISM.nativeCurrency.logo, + }, + }; + + updatedNetworks.XDAI = { + ...updatedNetworks.XDAI, + iconUrls: INITIAL_NETWORKS.XDAI.iconUrls, + nativeCurrency: { + ...updatedNetworks.XDAI.nativeCurrency, + logo: INITIAL_NETWORKS.XDAI.nativeCurrency.logo, + }, + }; + + updatedNetworks.ZKSYNC_ERA_MAINNET = { + ...updatedNetworks.ZKSYNC_ERA_MAINNET, + iconUrls: INITIAL_NETWORKS.ZKSYNC_ERA_MAINNET.iconUrls, + nativeCurrency: { + ...updatedNetworks.ZKSYNC_ERA_MAINNET.nativeCurrency, + logo: INITIAL_NETWORKS.ZKSYNC_ERA_MAINNET.nativeCurrency.logo, + }, + }; + + updatedNetworks.RSK = { + ...updatedNetworks.RSK, + iconUrls: INITIAL_NETWORKS.RSK.iconUrls, + nativeCurrency: { + ...updatedNetworks.RSK.nativeCurrency, + logo: INITIAL_NETWORKS.RSK.nativeCurrency.logo, + }, + }; + + updatedNetworks.POLYGON_ZKEVM = { + ...updatedNetworks.POLYGON_ZKEVM, + nativeCurrency: { + ...updatedNetworks.POLYGON_ZKEVM.nativeCurrency, + logo: INITIAL_NETWORKS.POLYGON_ZKEVM.nativeCurrency.logo, + }, + }; + + updatedNetworks.GOERLI = { + ...updatedNetworks.GOERLI, + iconUrls: INITIAL_NETWORKS.GOERLI.iconUrls, + }; + + updatedNetworks.POLYGON_TESTNET_MUMBAI = { + ...updatedNetworks.POLYGON_TESTNET_MUMBAI, + blockExplorerName: + INITIAL_NETWORKS.POLYGON_TESTNET_MUMBAI.blockExplorerName, + }; + + updatedNetworks.ZKSYNC_ALPHA_TESTNET = { + ...updatedNetworks.ZKSYNC_ALPHA_TESTNET, + nativeCurrency: { + ...updatedNetworks.ZKSYNC_ALPHA_TESTNET.nativeCurrency, + logo: INITIAL_NETWORKS.ZKSYNC_ALPHA_TESTNET.nativeCurrency.logo, + }, + }; + + updatedNetworks.LOCALHOST = { + ...updatedNetworks.LOCALHOST, + nativeCurrency: { + ...updatedNetworks.LOCALHOST.nativeCurrency, + logo: INITIAL_NETWORKS.LOCALHOST.nativeCurrency.logo, + }, + }; + + const orderedNetworks = normalizeNetworksOrder(updatedNetworks); + + return { + ...persistedState, + NetworkController: { + ...persistedState.NetworkController, + availableNetworks: { ...orderedNetworks }, + }, + }; + }, + version: '1.1.6', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-67.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-67.ts new file mode 100644 index 000000000..f3df17c76 --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-67.ts @@ -0,0 +1,62 @@ +import { BlankAppState } from '@block-wallet/background/utils/constants/initialState'; +import { IMigration } from '../IMigration'; +import { INITIAL_NETWORKS } from '../../../../utils/constants/networks'; +import { normalizeNetworksOrder } from '../../../../utils/networks'; + +/** + * Add Network currentRpcUrl, backupRpcUrls & defaultRpcUrl Properties and remove rpcUrls + */ +export default { + migrate: async (persistedState: BlankAppState) => { + const { availableNetworks } = persistedState.NetworkController; + const updatedNetworks = { ...availableNetworks }; + + Object.keys(updatedNetworks).forEach((key) => { + const { rpcUrls } = updatedNetworks[key]; + + let defaultRpcUrl: string | undefined; + let currentRpcUrl: string; + let backupRpcUrls: string[] | undefined; + if (key in INITIAL_NETWORKS) { + defaultRpcUrl = INITIAL_NETWORKS[key].defaultRpcUrl; + backupRpcUrls = INITIAL_NETWORKS[key].backupRpcUrls; + currentRpcUrl = + updatedNetworks[key].currentRpcUrl ?? + INITIAL_NETWORKS[key].currentRpcUrl; + } else { + const network = updatedNetworks[key]; + if (network.rpcUrls && network.rpcUrls?.length) { + defaultRpcUrl = network.rpcUrls[0]; + currentRpcUrl = network.rpcUrls[0]; + } else { + defaultRpcUrl = ''; + currentRpcUrl = network.currentRpcUrl ?? ''; + } + backupRpcUrls = []; + } + + if (rpcUrls && rpcUrls.length > 0) { + currentRpcUrl = currentRpcUrl || rpcUrls[0]; + delete updatedNetworks[key].rpcUrls; + } + + updatedNetworks[key] = { + ...updatedNetworks[key], + defaultRpcUrl, + currentRpcUrl, + backupRpcUrls, + }; + }); + + const orderedNetworks = normalizeNetworksOrder(updatedNetworks); + + return { + ...persistedState, + NetworkController: { + ...persistedState.NetworkController, + availableNetworks: { ...orderedNetworks }, + }, + }; + }, + version: '1.1.9', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-68.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-68.ts new file mode 100644 index 000000000..db9c23ef2 --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-68.ts @@ -0,0 +1,23 @@ +import { BlankAppState } from '@block-wallet/background/utils/constants/initialState'; +import { IMigration } from '../IMigration'; + +/** + * This migration adds a default configuration related to net worth + */ +export default { + migrate: async (persistedState: BlankAppState) => { + return { + ...persistedState, + PreferencesController: { + ...persistedState.PreferencesController, + settings: { + ...persistedState.PreferencesController.settings, + displayNetWorth: + persistedState.PreferencesController.settings + .displayNetWorth ?? true, + }, + }, + }; + }, + version: '1.1.10', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-69.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-69.ts new file mode 100644 index 000000000..b795c71d0 --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-69.ts @@ -0,0 +1,26 @@ +import { BlankAppState } from '@block-wallet/background/utils/constants/initialState'; +import { IMigration } from '../IMigration'; +import { NO_EIP_1559_NETWORKS } from '../../../../controllers/NetworkController'; + +/** + * This migration forces the calculation of the EIP1559 compatibility to some networks + */ +export default { + migrate: async (persistedState: BlankAppState) => { + const { isEIP1559Compatible } = persistedState.NetworkController; + const updatedIsEIP1559Compatible = { ...isEIP1559Compatible }; + + NO_EIP_1559_NETWORKS.forEach( + (chainId: number) => delete updatedIsEIP1559Compatible[chainId] + ); + + return { + ...persistedState, + NetworkController: { + ...persistedState.NetworkController, + isEIP1559Compatible: updatedIsEIP1559Compatible, + }, + }; + }, + version: '1.1.11', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-70.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-70.ts new file mode 100644 index 000000000..877ab32e9 --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-70.ts @@ -0,0 +1,22 @@ +import { BlankAppState } from '@block-wallet/background/utils/constants/initialState'; +import { IMigration } from '../IMigration'; + +/** + * This migration forces the calculation of the EIP1559 compatibility to some networks + */ +export default { + migrate: async (persistedState: BlankAppState) => { + return { + ...persistedState, + PreferencesController: { + ...persistedState.PreferencesController, + tokensSortValue: 'CUSTOM', + }, + AccountTrackerController: { + ...persistedState.AccountTrackerController, + accountTokensOrder: {}, + }, + }; + }, + version: '1.1.12', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-71.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-71.ts new file mode 100644 index 000000000..aaa17cbd0 --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-71.ts @@ -0,0 +1,60 @@ +import { BlankAppState } from '@block-wallet/background/utils/constants/initialState'; +import { IMigration } from '../IMigration'; +import { normalizeNetworksOrder } from '../../../../utils/networks'; +import { FEATURES } from '../../../../utils/constants/features'; +import { SLOW_TESTNET_TIME_INTERVALS_DEFAULT_VALUES } from '../../../../utils/constants/networks'; + +/** + * This migration adds ZetaChain Testnet + */ +export default { + migrate: async (persistedState: BlankAppState) => { + const { availableNetworks } = persistedState.NetworkController; + const updatedNetworks = { ...availableNetworks }; + + updatedNetworks.ZETACHAIN_TESTNET = { + name: 'zetachain_testnet', + desc: 'ZetaChain Testnet', + chainId: 7001, + networkVersion: '7001', + nativeCurrency: { + name: 'Testnet ZETA', + symbol: 'aZETA', + decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/zetachaintestnet/info/logo.png', + }, + hasFixedGasCost: false, + enable: true, + test: true, + order: 11, + features: [FEATURES.SENDS], + ens: false, + showGasLevels: true, + iconUrls: [ + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/zetachaintestnet/info/logo.png', + ], + currentRpcUrl: 'https://rpc.ankr.com/zetachain_evm_athens_testnet', + blockExplorerName: 'ZetaChain Testnet Explorer', + blockExplorerUrls: ['https://zetachain-athens-3.blockscout.com/'], + actionsTimeIntervals: { + ...SLOW_TESTNET_TIME_INTERVALS_DEFAULT_VALUES, + }, + tornadoIntervals: { + depositConfirmations: 0, + derivationsForward: 0, + }, + nativelySupported: true, + }; + + const orderedNetworks = normalizeNetworksOrder(updatedNetworks); + + return { + ...persistedState, + NetworkController: { + ...persistedState.NetworkController, + availableNetworks: { ...orderedNetworks }, + }, + }; + }, + version: '1.1.19', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-72.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-72.ts new file mode 100644 index 000000000..0db416364 --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-72.ts @@ -0,0 +1,102 @@ +import { BlankAppState } from '@block-wallet/background/utils/constants/initialState'; +import { IMigration } from '../IMigration'; +import { normalizeNetworksOrder } from '../../../../utils/networks'; +import { FEATURES } from '../../../../utils/constants/features'; +import { + ACTIONS_TIME_INTERVALS_DEFAULT_VALUES, + SLOW_TESTNET_TIME_INTERVALS_DEFAULT_VALUES, +} from '../../../../utils/constants/networks'; + +/** + * This migration adds Scroll Mainnet network and updates Scrol Sepolia Tesnet + */ +export default { + migrate: async (persistedState: BlankAppState) => { + const { availableNetworks } = persistedState.NetworkController; + const updatedNetworks = { ...availableNetworks }; + + updatedNetworks.SCROLL_MAINNET = { + name: 'scroll_mainnet', + desc: 'Scroll', + chainId: 534352, + networkVersion: '534352', + nativeCurrency: { + name: 'Ether', + symbol: 'ETH', + decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/ethereum/info/logo.png', + }, + iconUrls: [ + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/scroll/info/logo.png', + ], + hasFixedGasCost: false, + enable: true, + test: false, + order: 12, + features: [FEATURES.SENDS], + ens: false, + showGasLevels: false, + currentRpcUrl: 'https://scroll-node.blockwallet.io', + defaultRpcUrl: 'https://scroll-node.blockwallet.io', + backupRpcUrls: [ + 'https://scroll.blockwallet.io', + 'https://rpc.scroll.io/', + ], + blockExplorerName: 'Scroll Blockchain Explorer', + blockExplorerUrls: ['https://scrollscan.com/'], + actionsTimeIntervals: { ...ACTIONS_TIME_INTERVALS_DEFAULT_VALUES }, + tornadoIntervals: { + depositConfirmations: 0, + derivationsForward: 0, + }, + nativelySupported: true, + }; + + updatedNetworks.SCROLL_L2_TESTNET = { + name: 'scroll_l2_testnet', + desc: 'Scroll Sepolia Testnet', + chainId: 534351, + networkVersion: '534351', + nativeCurrency: { + name: 'Ether', + symbol: 'ETH', + decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/ethereum/info/logo.png', + }, + iconUrls: [ + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/scroll/info/logo.png', + ], + hasFixedGasCost: false, + enable: true, + test: true, + order: 9, + features: [FEATURES.SENDS], + ens: false, + showGasLevels: false, + currentRpcUrl: `https://sepolia-rpc.scroll.io/`, + defaultRpcUrl: `https://sepolia-rpc.scroll.io/`, + backupRpcUrls: ['https://rpc.ankr.com/scroll_sepolia_testnet/'], + blockExplorerUrls: ['https://sepolia.scrollscan.com/'], + blockExplorerName: 'Scroll Sepolia Explorer', + actionsTimeIntervals: { + ...SLOW_TESTNET_TIME_INTERVALS_DEFAULT_VALUES, + }, + tornadoIntervals: { + depositConfirmations: 0, + derivationsForward: 0, + }, + nativelySupported: true, + }; + + const orderedNetworks = normalizeNetworksOrder(updatedNetworks); + + return { + ...persistedState, + NetworkController: { + ...persistedState.NetworkController, + availableNetworks: { ...orderedNetworks }, + }, + }; + }, + version: '1.1.20', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-73.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-73.ts new file mode 100644 index 000000000..9bae3c069 --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-73.ts @@ -0,0 +1,66 @@ +import { FEATURES } from '../../../../utils/constants/features'; +import { BlankAppState } from '../../../../utils/constants/initialState'; +import { SLOW_TESTNET_TIME_INTERVALS_DEFAULT_VALUES } from '../../../../utils/constants/networks'; +import { normalizeNetworksOrder } from '../../../../utils/networks'; +import { IMigration } from '../IMigration'; + +/** + * This migration removes RSK Testnet and adds OKX's testnet. + */ +export default { + migrate: async (persistedState: BlankAppState) => { + const { availableNetworks } = persistedState.NetworkController; + const updatedNetworks = { ...availableNetworks }; + + // Remove RSK Testnet + delete updatedNetworks['RSK_TESTNET']; + + // Add new OKX X1 testnet + updatedNetworks.X1_TESTNET = { + name: 'X1 Testnet', + desc: 'X1 Testnet', + chainId: 195, + networkVersion: '195', + nativeCurrency: { + name: 'OKB', + symbol: 'OKB', + decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/x1/info/logo.png', + }, + iconUrls: [ + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/x1/info/logo.png', + ], + hasFixedGasCost: false, + enable: true, + test: true, + order: 10, + features: [FEATURES.SENDS], + ens: false, + showGasLevels: false, + currentRpcUrl: `https://testrpc.x1.tech/`, + defaultRpcUrl: `https://testrpc.x1.tech/`, + backupRpcUrls: ['https://x1testrpc.okx.com/'], + blockExplorerUrls: ['https://sepolia.scrollscan.com/'], + blockExplorerName: 'Scroll Sepolia Explorer', + actionsTimeIntervals: { + ...SLOW_TESTNET_TIME_INTERVALS_DEFAULT_VALUES, + }, + tornadoIntervals: { + depositConfirmations: 0, + derivationsForward: 0, + }, + nativelySupported: true, + }; + + const orderedNetworks = normalizeNetworksOrder(updatedNetworks); + + return { + ...persistedState, + NetworkController: { + ...persistedState.NetworkController, + availableNetworks: { ...orderedNetworks }, + }, + }; + }, + version: '1.1.23', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrations/migration-74.ts b/packages/background/src/infrastructure/stores/migrator/migrations/migration-74.ts new file mode 100644 index 000000000..b752b5479 --- /dev/null +++ b/packages/background/src/infrastructure/stores/migrator/migrations/migration-74.ts @@ -0,0 +1,29 @@ +import { BlankAppState } from '../../../../utils/constants/initialState'; +import { IMigration } from '../IMigration'; +import { BigNumber } from '@ethersproject/bignumber'; + +/** + * This migration updates the gas lower cap for bnb + */ +export default { + migrate: async (persistedState: BlankAppState) => { + const { availableNetworks } = persistedState.NetworkController; + const updatedNetworks = { ...availableNetworks }; + + updatedNetworks['BSC'] = { + ...updatedNetworks['BSC'], + gasLowerCap: { + maxPriorityFeePerGas: BigNumber.from('3000000000'), // 3 GWEI, + }, + }; + + return { + ...persistedState, + NetworkController: { + ...persistedState.NetworkController, + availableNetworks: { ...updatedNetworks }, + }, + }; + }, + version: '1.1.24', +} as IMigration; diff --git a/packages/background/src/infrastructure/stores/migrator/migrator.ts b/packages/background/src/infrastructure/stores/migrator/migrator.ts index be12273f3..64d262c80 100644 --- a/packages/background/src/infrastructure/stores/migrator/migrator.ts +++ b/packages/background/src/infrastructure/stores/migrator/migrator.ts @@ -1,8 +1,9 @@ -import compareVersions from 'compare-versions'; +import { compareVersions } from 'compare-versions'; import migrations from './migrations'; import { BlankAppState } from '../../../utils/constants/initialState'; import { DeepPartial } from '../../../utils/types/helpers'; +import log from 'loglevel'; export const migrator = async ( version: string, @@ -11,7 +12,16 @@ export const migrator = async ( let newState = persistedState; for (const migration of migrations()) { if (compareVersions(migration.version, version) > 0) { - newState = await migration.migrate(newState); + try { + newState = await migration.migrate(newState); + } catch (error) { + // This will catch any potential migration errors and continue the process so the extension can remain usable. + // Previously, the exception remained unhandled, breaking the initialization process. + log.error( + `Could not apply migration ${migration.version} - `, + error + ); + } } } return newState as BlankAppState; diff --git a/packages/background/src/typings/worker-loader.d.ts b/packages/background/src/typings/worker-loader.d.ts deleted file mode 100644 index 5df331435..000000000 --- a/packages/background/src/typings/worker-loader.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare module 'worker-loader!*' { - class WebpackWorker extends Worker { - constructor(); - } - - export default WebpackWorker; -} diff --git a/packages/background/src/utils/account.ts b/packages/background/src/utils/account.ts index 4bd234e19..d8a075de0 100644 --- a/packages/background/src/utils/account.ts +++ b/packages/background/src/utils/account.ts @@ -5,7 +5,7 @@ import { isValidPrivate, stripHexPrefix, toBuffer, -} from 'ethereumjs-util'; +} from '@ethereumjs/util'; export enum ImportStrategy { PRIVATE_KEY = 'PRIVATE_KEY', @@ -80,3 +80,11 @@ export const getAccountJson = async ( const v3FormattedAcc = await account.toV3(password); return JSON.stringify(v3FormattedAcc); }; + +export const formatName = (name: string, maxLength = 25) => { + if (name.length < maxLength) { + return name; + } else { + return `${name.slice(0, maxLength - 3)}...`; + } +}; diff --git a/packages/background/src/utils/balance-checker/balanceChecker.ts b/packages/background/src/utils/balance-checker/balanceChecker.ts index 0adb5f04e..99924767b 100644 --- a/packages/background/src/utils/balance-checker/balanceChecker.ts +++ b/packages/background/src/utils/balance-checker/balanceChecker.ts @@ -1,5 +1,5 @@ import { isAddress } from '@ethersproject/address'; -import { isValidAddress, toChecksumAddress } from 'ethereumjs-util'; +import { isValidAddress, toChecksumAddress } from '@ethereumjs/util'; import { BigNumber } from '@ethersproject/bignumber'; import { Contract } from '@ethersproject/contracts'; import { Signer } from '@ethersproject/abstract-signer'; diff --git a/packages/background/src/utils/bnUtils.ts b/packages/background/src/utils/bnUtils.ts index fdb3bb983..e68a0735c 100644 --- a/packages/background/src/utils/bnUtils.ts +++ b/packages/background/src/utils/bnUtils.ts @@ -1,5 +1,5 @@ import BN from 'bn.js'; -import { addHexPrefix } from 'ethereumjs-util'; +import { addHexPrefix } from '@ethereumjs/util'; import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; /** diff --git a/packages/background/src/utils/bridgeApi.ts b/packages/background/src/utils/bridgeApi.ts index e87eea4ac..5a27d04f5 100644 --- a/packages/background/src/utils/bridgeApi.ts +++ b/packages/background/src/utils/bridgeApi.ts @@ -14,17 +14,9 @@ import { lifiFeeCostsToIBridgeFeeCosts, lifiTokenToIToken, LIFI_BRIDGE_ENDPOINT, + LIFI_KEY_HEADER, } from './types/lifi'; -const getMessageFromLiFiError = (errCode: string) => { - if (errCode === 'AMOUNT_TOO_LOW') { - return 'The amount is too low for executing this bridge.'; - } - if (errCode === 'QUOTE_NOT_FOUND') { - return "There isn't a quote for the requested parameters."; - } -}; - export class QuoteNotFoundError extends Error { details: LiFiErrorResponse; constructor(message: string | undefined, details: LiFiErrorResponse) { @@ -174,15 +166,17 @@ const LiFiBridge: IBridge = { getSupportedTokensForChain: async function ( chainId: number ): Promise { - const response = await httpClient.get( - `${LIFI_BRIDGE_ENDPOINT}/tokens` + const response = await httpClient.request( + `${LIFI_BRIDGE_ENDPOINT}/tokens`, + { headers: LIFI_KEY_HEADER } ); const chainTokens = response.tokens[chainId] || []; return chainTokens.map(lifiTokenToIToken); }, getSupportedChains: async function (): Promise { - const response = await httpClient.get( - `${LIFI_BRIDGE_ENDPOINT}/chains` + const response = await httpClient.request( + `${LIFI_BRIDGE_ENDPOINT}/chains`, + { headers: LIFI_KEY_HEADER } ); const chains = response.chains || []; return chains.map((chain) => ({ @@ -195,14 +189,17 @@ const LiFiBridge: IBridge = { getRoutes: async function ( request: getBridgeRoutesRequest ): Promise { - const response = await httpClient.get( + const response = await httpClient.request( `${LIFI_BRIDGE_ENDPOINT}/connections`, { - allowExchanges: '[]', - fromChain: request.fromChainId, - toChain: request.toChainId, - fromToken: request.fromTokenAddress, - toToken: request.toTokenAddress, + params: { + allowExchanges: '[]', + fromChain: request.fromChainId, + toChain: request.toChainId, + fromToken: request.fromTokenAddress, + toToken: request.toTokenAddress, + }, + headers: LIFI_KEY_HEADER, } ); const result = response.connections; @@ -215,19 +212,22 @@ const LiFiBridge: IBridge = { }, getQuote: async function (r: getBridgeQuoteRequest): Promise { try { - const response = await httpClient.get< + const response = await httpClient.request< GetLiFiQuoteResponse | LiFiErrorResponse >(`${LIFI_BRIDGE_ENDPOINT}/quote`, { - fromToken: r.fromTokenAddress, - toToken: r.toTokenAddress, - fromChain: r.fromChainId, - toChain: r.toChainId, - fromAmount: r.fromAmount, - fromAddress: r.fromAddress, - referrer: r.referrer, - integrator: 'blockwallet.io', - slippage: r.slippage, - fee: BASE_BRIDGE_FEE, + params: { + fromToken: r.fromTokenAddress, + toToken: r.toTokenAddress, + fromChain: r.fromChainId, + toChain: r.toChainId, + fromAmount: r.fromAmount, + fromAddress: r.fromAddress, + referrer: r.referrer, + integrator: 'blockwallet.io', + slippage: r.slippage, + fee: BASE_BRIDGE_FEE, + }, + headers: LIFI_KEY_HEADER, }); const responseData = response as GetLiFiQuoteResponse; return { @@ -253,29 +253,25 @@ const LiFiBridge: IBridge = { throw new Error('Request parameters are invalid.'); } else if (e.status === 404) { const quoteError = e.response as LiFiErrorResponse; - const errorCode = quoteError.errors?.length - ? quoteError.errors[0].code - : 'QUOTE_NOT_FOUND'; - const message = getMessageFromLiFiError(errorCode); + const message = quoteError.message; throw new QuoteNotFoundError(message, quoteError); } throw e; } }, getStatus: async function (r: getStatusRequest): Promise { - const response = await httpClient.get< + const response = await httpClient.request< GetLiFiStatusResponse | LiFiErrorResponse - >( - `${LIFI_BRIDGE_ENDPOINT}/status`, - { + >(`${LIFI_BRIDGE_ENDPOINT}/status`, { + params: { bridge: r.tool, fromChain: r.fromChainId, toChain: r.toChainId, txHash: r.sendTxHash, }, - undefined, - 'no-cache' - ); + headers: LIFI_KEY_HEADER, + cache: 'no-cache', + }); const responseData = response as GetLiFiStatusResponse; return { diff --git a/packages/background/src/utils/checksummedAddress.ts b/packages/background/src/utils/checksummedAddress.ts index b4c9e1146..7c475459f 100644 --- a/packages/background/src/utils/checksummedAddress.ts +++ b/packages/background/src/utils/checksummedAddress.ts @@ -1,4 +1,4 @@ -import { toChecksumAddress } from 'ethereumjs-util'; +import { toChecksumAddress } from '@ethereumjs/util'; import { memoize } from 'lodash'; const checksummedAddress = memoize((address: string) => diff --git a/packages/background/src/utils/constants/currencies.json b/packages/background/src/utils/constants/currencies.json index 9656cbee4..8f4452b67 100644 --- a/packages/background/src/utils/constants/currencies.json +++ b/packages/background/src/utils/constants/currencies.json @@ -1,244 +1,212 @@ [ - { - "code": "btc", - "name": "Bitcoin" - }, - { - "code": "eth", - "name": "Ethereum" - }, - { - "code": "ltc", - "name": "Binance-Peg Litecoin" - }, - { - "code": "bch", - "name": "Binance-Peg Bitcoin Cash" - }, - { - "code": "bnb", - "name": "Binance Coin" - }, - { - "code": "eos", - "name": "Binance-Peg EOS" - }, - { - "code": "xrp", - "name": "Binance-Peg XRP" - }, - { - "code": "xlm", - "name": "Stellar" - }, - { - "code": "link", - "name": "Chainlink" - }, - { - "code": "dot", - "name": "Binance-Peg Polkadot" - }, - { - "code": "yfi", - "name": "yearn.finance" - }, - { - "code": "usd", - "name": "United States Dollar" - }, - { - "code": "aed", - "name": "UAE Dirham" - }, - { - "code": "ars", - "name": "Argentine Peso" - }, - { - "code": "aud", - "name": "Australian Dollar" - }, - { - "code": "bdt", - "name": "Bangladeshi Taka" - }, - { - "code": "bhd", - "name": "Bahraini Dinar" - }, - { - "code": "bmd", - "name": "Bermudian Dollar" - }, - { - "code": "brl", - "name": "Brazilian Real" - }, - { - "code": "cad", - "name": "Canadian Dollar" - }, - { - "code": "chf", - "name": "Swiss Franc" - }, - { - "code": "clp", - "name": "Chilean Peso" - }, - { - "code": "cny", - "name": "Chinese Renminbi" - }, - { - "code": "czk", - "name": "Czech Koruna" - }, - { - "code": "dkk", - "name": "Danish Krone" - }, - { - "code": "eur", - "name": "Euro" - }, - { - "code": "gbp", - "name": "Pound Sterling" - }, - { - "code": "hkd", - "name": "Hong Kong Dollar" - }, - { - "code": "huf", - "name": "Hungarian Forint" - }, - { - "code": "idr", - "name": "Indonesian Rupiah" - }, - { - "code": "ils", - "name": "Israeli New Shekel" - }, - { - "code": "inr", - "name": "Indian Rupee" - }, - { - "code": "jpy", - "name": "Japanese Yen" - }, - { - "code": "krw", - "name": "South Korean Won" - }, - { - "code": "kwd", - "name": "Kuwaiti Dinar" - }, - { - "code": "lkr", - "name": "Sri Lanka Rupee" - }, - { - "code": "mmk", - "name": "Burmese Kyat" - }, - { - "code": "mxn", - "name": "Mexican Peso" - }, - { - "code": "myr", - "name": "Malaysian Ringgit" - }, - { - "code": "ngn", - "name": "Nigerian Naira" - }, - { - "code": "nok", - "name": "Norwegian Krone" - }, - { - "code": "nzd", - "name": "New Zealand Dollar" - }, - { - "code": "php", - "name": "Philippine Peso" - }, - { - "code": "pkr", - "name": "Pakistani Rupee" - }, - { - "code": "pln", - "name": "Polish Złoty" - }, - { - "code": "rub", - "name": "Russian Ruble" - }, - { - "code": "sar", - "name": "Saudi Riyal" - }, - { - "code": "sek", - "name": "Swedish Krona" - }, - { - "code": "sgd", - "name": "Singapore Dollar" - }, - { - "code": "thb", - "name": "Thai Baht" - }, - { - "code": "try", - "name": "Turkish Lira" - }, - { - "code": "twd", - "name": "New Taiwan Dollar" - }, - { - "code": "uah", - "name": "Ukrainian Hryvnia" - }, - { - "code": "vef" - }, - { - "code": "vnd", - "name": "Vietnamese Đồng" - }, - { - "code": "zar", - "name": "South African Rand" - }, - { - "code": "xdr", - "name": "Special Drawing Rights" - }, - { - "code": "xag", - "name": "Xrpalike Gene" - }, - { - "code": "xau" - }, - { - "code": "bits", - "name": "Bitswift" - }, - { - "code": "sats", - "name": "Baby Satoshi" - } -] \ No newline at end of file + { + "code": "btc", + "name": "Bitcoin" + }, + { + "code": "eth", + "name": "Ethereum" + }, + { + "code": "bnb", + "name": "Binance Coin" + }, + { + "code": "usd", + "name": "United States Dollar" + }, + { + "code": "aed", + "name": "UAE Dirham" + }, + { + "code": "ars", + "name": "Argentine Peso" + }, + { + "code": "aud", + "name": "Australian Dollar" + }, + { + "code": "bdt", + "name": "Bangladeshi Taka" + }, + { + "code": "bhd", + "name": "Bahraini Dinar" + }, + { + "code": "bmd", + "name": "Bermudian Dollar" + }, + { + "code": "brl", + "name": "Brazilian Real" + }, + { + "code": "cad", + "name": "Canadian Dollar" + }, + { + "code": "chf", + "name": "Swiss Franc" + }, + { + "code": "clp", + "name": "Chilean Peso" + }, + { + "code": "cny", + "name": "Chinese Renminbi" + }, + { + "code": "czk", + "name": "Czech Koruna" + }, + { + "code": "dkk", + "name": "Danish Krone" + }, + { + "code": "eur", + "name": "Euro" + }, + { + "code": "gbp", + "name": "Pound Sterling" + }, + { + "code": "hkd", + "name": "Hong Kong Dollar" + }, + { + "code": "huf", + "name": "Hungarian Forint" + }, + { + "code": "idr", + "name": "Indonesian Rupiah" + }, + { + "code": "ils", + "name": "Israeli New Shekel" + }, + { + "code": "inr", + "name": "Indian Rupee" + }, + { + "code": "jpy", + "name": "Japanese Yen" + }, + { + "code": "krw", + "name": "South Korean Won" + }, + { + "code": "kwd", + "name": "Kuwaiti Dinar" + }, + { + "code": "lkr", + "name": "Sri Lanka Rupee" + }, + { + "code": "mmk", + "name": "Burmese Kyat" + }, + { + "code": "mxn", + "name": "Mexican Peso" + }, + { + "code": "myr", + "name": "Malaysian Ringgit" + }, + { + "code": "ngn", + "name": "Nigerian Naira" + }, + { + "code": "nok", + "name": "Norwegian Krone" + }, + { + "code": "nzd", + "name": "New Zealand Dollar" + }, + { + "code": "php", + "name": "Philippine Peso" + }, + { + "code": "pkr", + "name": "Pakistani Rupee" + }, + { + "code": "pln", + "name": "Polish Złoty" + }, + { + "code": "rub", + "name": "Russian Ruble" + }, + { + "code": "sar", + "name": "Saudi Riyal" + }, + { + "code": "sek", + "name": "Swedish Krona" + }, + { + "code": "sgd", + "name": "Singapore Dollar" + }, + { + "code": "thb", + "name": "Thai Baht" + }, + { + "code": "try", + "name": "Turkish Lira" + }, + { + "code": "twd", + "name": "New Taiwan Dollar" + }, + { + "code": "uah", + "name": "Ukrainian Hryvnia" + }, + { + "code": "vef" + }, + { + "code": "vnd", + "name": "Vietnamese Đồng" + }, + { + "code": "zar", + "name": "South African Rand" + }, + { + "code": "xdr", + "name": "Special Drawing Rights" + }, + { + "code": "xag", + "name": "Xrpalike Gene" + }, + { + "code": "xau" + }, + { + "code": "bits", + "name": "Bitswift" + }, + { + "code": "sats", + "name": "Baby Satoshi" + } +] diff --git a/packages/background/src/utils/constants/initialState.ts b/packages/background/src/utils/constants/initialState.ts index 72fbe9df7..0ad04cc22 100644 --- a/packages/background/src/utils/constants/initialState.ts +++ b/packages/background/src/utils/constants/initialState.ts @@ -50,6 +50,8 @@ import { import { SwapControllerMemState } from '@block-wallet/background/controllers/SwapController'; import { RemoteConfigsControllerState } from '@block-wallet/background/controllers/RemoteConfigsController'; import CACHED_INCOMPATIBLE_SITES from '@block-wallet/remote-configs/provider/incompatible_sites.json'; +import { CampaignsControllerState } from '@block-wallet/background/controllers/CampaignsController'; +import { OnrampControllerMemState } from '@block-wallet/background/controllers/OnrampController'; export type BlankAppState = { AccountTrackerController: AccountTrackerState; @@ -70,6 +72,7 @@ export type BlankAppState = { TransactionWatcherControllerState: TransactionWatcherControllerState; BridgeController: BridgeControllerState; RemoteConfigsController: RemoteConfigsControllerState; + CampaignsController: CampaignsControllerState; }; export type BlankAppUIState = { @@ -90,6 +93,7 @@ export type BlankAppUIState = { BridgeController: BridgeControllerMemState; SwapController: SwapControllerMemState; BlankProviderController: BlankProviderControllerState; + OnrampController: OnrampControllerMemState; }; export type BlankAppStoreConfig = { @@ -121,6 +125,7 @@ const initialState: BlankAppState = { hiddenAccounts: {}, isRefreshingAllowances: false, isAccountTrackerLoading: false, + accountTokensOrder: {}, }, AppStateController: { idleTimeout: 5, @@ -129,6 +134,9 @@ const initialState: BlankAppState = { lockedByTimeout: false, }, BlockUpdatesController: { blockData: {} }, + CampaignsController: { + enrollments: {}, + }, KeyringController: { isUnlocked: false, keyringTypes: [], @@ -152,12 +160,16 @@ const initialState: BlankAppState = { popupTab: 'activity', settings: { hideAddressWarning: false, // Shown by default, + hideSendToContractWarning: false, // Shown by default + hideSendToNullWarning: false, // Shown by default subscribedToReleaseaNotes: true, + subscribedToNotifications: true, useAntiPhishingProtection: true, defaultBrowserWallet: true, hideEstimatedGasExceedsThresholdWarning: false, // Shown by default, hideDepositsExternalAccountsWarning: false, hideBridgeInsufficientNativeTokenWarning: false, // Shown by default + displayNetWorth: true, }, releaseNotesSettings: { lastVersionUserSawNews: '0.1.3', @@ -167,6 +179,9 @@ const initialState: BlankAppState = { account: [], }, defaultGasOption: 'medium', + hotkeysEnabled: true, + tokensSortValue: 'CUSTOM', + hideSmallBalances: false, }, TransactionController: { transactions: [], @@ -190,7 +205,12 @@ const initialState: BlankAppState = { availableNetworks: INITIAL_NETWORKS, isNetworkChanging: false, isUserNetworkOnline: true, - isProviderNetworkOnline: true, + providerStatus: { + isCurrentProviderOnline: true, + isDefaultProviderOnline: true, + isBackupProviderOnline: true, + isUsingBackupProvider: false, + }, isEIP1559Compatible: {}, }, ExchangeRatesController: { diff --git a/packages/background/src/utils/constants/networks.ts b/packages/background/src/utils/constants/networks.ts index 9faad26ab..678111ea1 100644 --- a/packages/background/src/utils/constants/networks.ts +++ b/packages/background/src/utils/constants/networks.ts @@ -20,9 +20,10 @@ export type Network = { name: string; symbol: string; decimals: number; + logo?: string; // Used if the native currency logo is different from network logo }; hasFixedGasCost?: boolean; - iconUrls?: string[]; + iconUrls?: string[]; // Network logo enable: boolean; features: BlankSupportedFeatures[]; test: boolean; @@ -34,13 +35,15 @@ export type Network = { maxPriorityFeePerGas?: BigNumber; gasPrice?: BigNumber; }; - rpcUrls: string[]; + currentRpcUrl: string; defaultRpcUrl?: string; + backupRpcUrls?: string[]; blockExplorerUrls?: string[]; blockExplorerName?: string; etherscanApiUrl?: string; actionsTimeIntervals: ActionsTimeInterval; tornadoIntervals?: TornadoIntervals; + rpcUrls?: string[]; /** * Indicates whether the network is natively supported @@ -69,6 +72,7 @@ export interface AddNetworkType { symbol: string; name?: string; decimals?: number; + logo?: string; }; rpcUrls?: string[]; test: boolean; @@ -162,13 +166,20 @@ export const INITIAL_NETWORKS: Networks = { features: [FEATURES.SENDS], ens: true, showGasLevels: true, - rpcUrls: [`https://mainnet-node.blockwallet.io`], + currentRpcUrl: `https://mainnet-node.blockwallet.io`, defaultRpcUrl: `https://mainnet-node.blockwallet.io`, + backupRpcUrls: [ + 'https://mainnet.blockwallet.io', + 'https://rpc.ankr.com/eth/', + ], + iconUrls: [ + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/ethereum/info/logo.png', + ], blockExplorerUrls: ['https://etherscan.io'], blockExplorerName: 'Etherscan', etherscanApiUrl: 'https://api.etherscan.io', actionsTimeIntervals: { - ...ACTIONS_TIME_INTERVALS_DEFAULT_VALUES, + ...FAST_TIME_INTERVALS_DEFAULT_VALUES, }, tornadoIntervals: { depositConfirmations: DEFAULT_TORNADO_CONFIRMATION, @@ -185,6 +196,7 @@ export const INITIAL_NETWORKS: Networks = { name: 'Ether', symbol: 'ETH', decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/ethereum/info/logo.png', }, hasFixedGasCost: false, enable: true, @@ -193,8 +205,15 @@ export const INITIAL_NETWORKS: Networks = { features: [FEATURES.SENDS], ens: false, showGasLevels: false, - rpcUrls: ['https://arbitrum-node.blockwallet.io'], + currentRpcUrl: 'https://arbitrum-node.blockwallet.io', + iconUrls: [ + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/arbitrum/info/logo.png', + ], defaultRpcUrl: 'https://arbitrum-node.blockwallet.io', + backupRpcUrls: [ + 'https://arbitrum.blockwallet.io', + 'https://rpc.ankr.com/arbitrum', + ], blockExplorerUrls: ['https://arbiscan.io'], blockExplorerName: 'Arbiscan', etherscanApiUrl: 'https://api.arbiscan.io', @@ -216,6 +235,7 @@ export const INITIAL_NETWORKS: Networks = { name: 'Ether', symbol: 'ETH', decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/ethereum/info/logo.png', }, hasFixedGasCost: false, gasLowerCap: { @@ -227,8 +247,15 @@ export const INITIAL_NETWORKS: Networks = { features: [FEATURES.SENDS], ens: false, showGasLevels: false, - rpcUrls: ['https://optimism-node.blockwallet.io'], + currentRpcUrl: 'https://optimism-node.blockwallet.io', + iconUrls: [ + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/optimism/info/logo.png', + ], defaultRpcUrl: 'https://optimism-node.blockwallet.io', + backupRpcUrls: [ + 'https://optimism.blockwallet.io', + 'https://rpc.ankr.com/optimism/', + ], blockExplorerUrls: ['https://optimistic.etherscan.io'], blockExplorerName: 'Etherscan', etherscanApiUrl: 'https://api-optimistic.etherscan.io', @@ -259,8 +286,12 @@ export const INITIAL_NETWORKS: Networks = { features: [FEATURES.SENDS], ens: false, showGasLevels: true, - rpcUrls: ['https://bsc-node.blockwallet.io'], + currentRpcUrl: 'https://bsc-node.blockwallet.io', defaultRpcUrl: 'https://bsc-node.blockwallet.io', + backupRpcUrls: [ + 'https://bsc.blockwallet.io', + 'https://rpc.ankr.com/bsc/', + ], blockExplorerUrls: ['https://bscscan.com'], blockExplorerName: 'Bscscan', etherscanApiUrl: 'https://api.bscscan.com', @@ -272,6 +303,9 @@ export const INITIAL_NETWORKS: Networks = { derivationsForward: DERIVATIONS_FORWARD, }, nativelySupported: true, + gasLowerCap: { + maxPriorityFeePerGas: BigNumber.from('3000000000'), // 3 GWEI, + }, }, POLYGON: { name: 'polygon', @@ -296,8 +330,12 @@ export const INITIAL_NETWORKS: Networks = { features: [FEATURES.SENDS], ens: false, showGasLevels: true, - rpcUrls: [`https://polygon-node.blockwallet.io`], + currentRpcUrl: `https://polygon-node.blockwallet.io`, defaultRpcUrl: `https://polygon-node.blockwallet.io`, + backupRpcUrls: [ + 'https://polygon.blockwallet.io', + 'https://rpc.ankr.com/polygon/', + ], blockExplorerUrls: ['https://polygonscan.com'], blockExplorerName: 'Polygonscan', etherscanApiUrl: 'https://api.polygonscan.com', @@ -333,8 +371,12 @@ export const INITIAL_NETWORKS: Networks = { features: [FEATURES.SENDS], ens: false, showGasLevels: true, - rpcUrls: [`https://avax-node.blockwallet.io`], + currentRpcUrl: `https://avax-node.blockwallet.io`, defaultRpcUrl: `https://avax-node.blockwallet.io`, + backupRpcUrls: [ + 'https://avax.blockwallet.io', + 'https://rpc.ankr.com/avalanche/', + ], blockExplorerUrls: ['https://snowtrace.io/'], blockExplorerName: 'Snowtrace', etherscanApiUrl: 'https://api.snowtrace.io/', @@ -367,8 +409,12 @@ export const INITIAL_NETWORKS: Networks = { features: [FEATURES.SENDS], ens: false, showGasLevels: true, - rpcUrls: [`https://fantom-node.blockwallet.io`], + currentRpcUrl: `https://fantom-node.blockwallet.io`, defaultRpcUrl: `https://fantom-node.blockwallet.io`, + backupRpcUrls: [ + 'https://fantom.blockwallet.io', + 'https://rpc.ankr.com/fantom/', + ], blockExplorerUrls: ['https://ftmscan.com'], blockExplorerName: 'FTMScan', etherscanApiUrl: 'https://api.ftmscan.com', @@ -388,6 +434,7 @@ export const INITIAL_NETWORKS: Networks = { name: 'xDAI', symbol: 'xDAI', decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/xdai/assets/0x/logo.png', }, hasFixedGasCost: false, enable: true, @@ -397,10 +444,14 @@ export const INITIAL_NETWORKS: Networks = { ens: false, showGasLevels: false, iconUrls: [ - 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/xdai/assets/0x/logo.png', + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/xdai/info/logo.png', ], - rpcUrls: ['https://xdai-node.blockwallet.io'], + currentRpcUrl: 'https://xdai-node.blockwallet.io', defaultRpcUrl: 'https://xdai-node.blockwallet.io', + backupRpcUrls: [ + 'https://xdai.blockwallet.io', + 'https://rpc.ankr.com/gnosis/', + ], blockExplorerUrls: ['https://blockscout.com/xdai/mainnet'], blockExplorerName: 'Blockscout', etherscanApiUrl: 'https://api-gnosis.etherscan.io', @@ -420,6 +471,7 @@ export const INITIAL_NETWORKS: Networks = { name: 'Smart Bitcoin', symbol: 'RBTC', decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/rsk/assets/0x/logo.png', }, hasFixedGasCost: false, enable: true, @@ -429,10 +481,14 @@ export const INITIAL_NETWORKS: Networks = { ens: false, showGasLevels: false, // "Slow" gas level might be lower than the minimumGasPrice so the tx will be rejected iconUrls: [ - 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/rsk/assets/0x/logo.png', + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/rsk/info/logo.png', ], - rpcUrls: ['https://rsk-node.blockwallet.io'], + currentRpcUrl: 'https://rsk-node.blockwallet.io', defaultRpcUrl: 'https://rsk-node.blockwallet.io', + backupRpcUrls: [ + 'https://rsk.blockwallet.io', + 'https://public-node.rsk.co ', + ], blockExplorerName: 'RSK Explorer', blockExplorerUrls: ['https://explorer.rsk.co'], actionsTimeIntervals: { ...ACTIONS_TIME_INTERVALS_DEFAULT_VALUES }, @@ -442,6 +498,114 @@ export const INITIAL_NETWORKS: Networks = { }, nativelySupported: true, }, + ZKSYNC_ERA_MAINNET: { + name: 'zksync_era_mainnet', + desc: 'zkSync Era Mainnet', + chainId: 324, + networkVersion: '324', + nativeCurrency: { + name: 'Ether', + symbol: 'ETH', + decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/ethereum/info/logo.png', + }, + iconUrls: [ + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/zksync/info/logo.png', + ], + hasFixedGasCost: false, + enable: true, + test: false, + order: 10, + features: [FEATURES.SENDS], + ens: false, + showGasLevels: false, + currentRpcUrl: `https://zksync-node.blockwallet.io`, + defaultRpcUrl: `https://zksync-node.blockwallet.io`, + backupRpcUrls: [ + 'https://zksync.blockwallet.io', + 'https://mainnet.era.zksync.io', + ], + blockExplorerUrls: ['https://explorer.zksync.io/'], + blockExplorerName: 'zkSync Explorer', + actionsTimeIntervals: { ...ACTIONS_TIME_INTERVALS_DEFAULT_VALUES }, + tornadoIntervals: { + depositConfirmations: DEFAULT_TORNADO_CONFIRMATION, + derivationsForward: DERIVATIONS_FORWARD, + }, + nativelySupported: true, + }, + POLYGON_ZKEVM: { + name: 'polygon_zkevm', + desc: 'Polygon zkEVM', + chainId: 1101, + networkVersion: '1101', + nativeCurrency: { + name: 'Ether', + symbol: 'ETH', + decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/ethereum/info/logo.png', + }, + iconUrls: [ + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/polygonzkevm/info/logo.png', + ], + hasFixedGasCost: false, + enable: true, + test: false, + order: 11, + features: [FEATURES.SENDS], + ens: false, + showGasLevels: false, + currentRpcUrl: `https://polygon-zkevm-node.blockwallet.io`, + defaultRpcUrl: `https://polygon-zkevm-node.blockwallet.io`, + backupRpcUrls: [ + 'https://polygon-zkevm.blockwallet.io', + 'https://rpc.ankr.com/polygon_zkevm', + ], + blockExplorerUrls: ['https://zkevm.polygonscan.com/'], + blockExplorerName: 'Polygon zkEVM Explorer', + actionsTimeIntervals: { ...ACTIONS_TIME_INTERVALS_DEFAULT_VALUES }, + tornadoIntervals: { + depositConfirmations: DEFAULT_TORNADO_CONFIRMATION, + derivationsForward: DERIVATIONS_FORWARD, + }, + nativelySupported: true, + }, + SCROLL_MAINNET: { + name: 'scroll_mainnet', + desc: 'Scroll', + chainId: 534352, + networkVersion: '534352', + nativeCurrency: { + name: 'Ether', + symbol: 'ETH', + decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/ethereum/info/logo.png', + }, + iconUrls: [ + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/scroll/info/logo.png', + ], + hasFixedGasCost: false, + enable: true, + test: false, + order: 12, + features: [FEATURES.SENDS], + ens: false, + showGasLevels: false, + currentRpcUrl: 'https://scroll-node.blockwallet.io', + defaultRpcUrl: 'https://scroll-node.blockwallet.io', + backupRpcUrls: [ + 'https://scroll.blockwallet.io', + 'https://rpc.scroll.io/', + ], + blockExplorerName: 'Scroll Blockchain Explorer', + blockExplorerUrls: ['https://scrollscan.com/'], + actionsTimeIntervals: { ...ACTIONS_TIME_INTERVALS_DEFAULT_VALUES }, + tornadoIntervals: { + depositConfirmations: DEFAULT_TORNADO_CONFIRMATION, + derivationsForward: DERIVATIONS_FORWARD, + }, + nativelySupported: true, + }, GOERLI: { name: 'goerli', desc: 'Goerli Testnet', @@ -459,8 +623,15 @@ export const INITIAL_NETWORKS: Networks = { features: [FEATURES.SENDS], ens: true, showGasLevels: true, - rpcUrls: [`https://goerli-node.blockwallet.io`], + iconUrls: [ + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/ethereum/info/logo.png', + ], + currentRpcUrl: `https://goerli-node.blockwallet.io`, defaultRpcUrl: `https://goerli-node.blockwallet.io`, + backupRpcUrls: [ + 'https://goerli.blockwallet.io', + 'https://rpc.ankr.com/eth_goerli', + ], blockExplorerUrls: ['https://goerli.etherscan.io'], blockExplorerName: 'Etherscan', etherscanApiUrl: 'https://api-goerli.etherscan.io', @@ -491,7 +662,7 @@ export const INITIAL_NETWORKS: Networks = { features: [FEATURES.SENDS], ens: false, showGasLevels: true, - rpcUrls: ['https://data-seed-prebsc-1-s1.binance.org:8545'], + currentRpcUrl: 'https://data-seed-prebsc-1-s1.binance.org:8545', blockExplorerUrls: ['https://testnet.bscscan.io'], blockExplorerName: 'Bscscan', actionsTimeIntervals: { ...TESTNET_TIME_INTERVALS_DEFAULT_VALUES }, @@ -521,9 +692,9 @@ export const INITIAL_NETWORKS: Networks = { features: [FEATURES.SENDS], ens: false, showGasLevels: true, - rpcUrls: [`https://matic-mumbai.chainstacklabs.com`], + currentRpcUrl: `https://matic-mumbai.chainstacklabs.com`, blockExplorerUrls: ['https://mumbai.polygonscan.com'], - blockExplorerName: 'Etherscan', + blockExplorerName: 'Polygonscan', etherscanApiUrl: 'https://mumbai.polygonscan.com', actionsTimeIntervals: { ...TESTNET_TIME_INTERVALS_DEFAULT_VALUES }, tornadoIntervals: { @@ -534,13 +705,14 @@ export const INITIAL_NETWORKS: Networks = { }, ZKSYNC_ALPHA_TESTNET: { name: 'zksync_alpha_testnet', - desc: 'zkSync Alpha Testnet', + desc: 'zkSync Era Testnet', chainId: 280, networkVersion: '280', nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/ethereum/info/logo.png', }, iconUrls: [ 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/zksync/info/logo.png', @@ -552,10 +724,14 @@ export const INITIAL_NETWORKS: Networks = { features: [FEATURES.SENDS], ens: false, showGasLevels: false, - rpcUrls: [`https://zksync-testnet-node.blockwallet.io`], + currentRpcUrl: `https://zksync-testnet-node.blockwallet.io`, defaultRpcUrl: `https://zksync-testnet-node.blockwallet.io`, + backupRpcUrls: [ + 'https://zksync-testnet.blockwallet.io', + 'https://testnet.era.zksync.dev', + ], blockExplorerUrls: ['https://goerli.explorer.zksync.io'], - blockExplorerName: 'zkSync Explorer', + blockExplorerName: 'zkSync Testnet Explorer', actionsTimeIntervals: { ...SLOW_TESTNET_TIME_INTERVALS_DEFAULT_VALUES }, tornadoIntervals: { depositConfirmations: DEFAULT_TORNADO_CONFIRMATION, @@ -563,15 +739,16 @@ export const INITIAL_NETWORKS: Networks = { }, nativelySupported: true, }, - SCROLL_L1_TESTNET: { - name: 'scroll_l1_testnet', - desc: 'Scroll L1 Testnet', + SCROLL_L2_TESTNET: { + name: 'scroll_l2_testnet', + desc: 'Scroll Sepolia Testnet', chainId: 534351, networkVersion: '534351', nativeCurrency: { name: 'Ether', - symbol: 'TSETH', + symbol: 'ETH', decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/ethereum/info/logo.png', }, iconUrls: [ 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/scroll/info/logo.png', @@ -579,14 +756,15 @@ export const INITIAL_NETWORKS: Networks = { hasFixedGasCost: false, enable: true, test: true, - order: 8, + order: 9, features: [FEATURES.SENDS], ens: false, showGasLevels: false, - rpcUrls: [`https://scroll-l1-testnet-node.blockwallet.io`], - defaultRpcUrl: `https://scroll-l1-testnet-node.blockwallet.io`, - blockExplorerUrls: ['https://l1scan.scroll.io/'], - blockExplorerName: 'Scroll L1 Explorer', + currentRpcUrl: `https://sepolia-rpc.scroll.io/`, + defaultRpcUrl: `https://sepolia-rpc.scroll.io/`, + backupRpcUrls: ['https://rpc.ankr.com/scroll_sepolia_testnet/'], + blockExplorerUrls: ['https://sepolia.scrollscan.com/'], + blockExplorerName: 'Scroll Sepolia Explorer', actionsTimeIntervals: { ...SLOW_TESTNET_TIME_INTERVALS_DEFAULT_VALUES }, tornadoIntervals: { depositConfirmations: DEFAULT_TORNADO_CONFIRMATION, @@ -594,30 +772,32 @@ export const INITIAL_NETWORKS: Networks = { }, nativelySupported: true, }, - SCROLL_L2_TESTNET: { - name: 'scroll_l2_testnet', - desc: 'Scroll L2 Testnet', - chainId: 534354, - networkVersion: '534354', + X1_TESTNET: { + name: 'X1_Testnet', + desc: 'X1 Testnet', + chainId: 195, + networkVersion: '195', nativeCurrency: { - name: 'Ether', - symbol: 'TSETH', + name: 'OKB', + symbol: 'OKB', decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/x1/info/logo.png', }, iconUrls: [ - 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/scroll/info/logo.png', + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/x1/info/logo.png', ], hasFixedGasCost: false, enable: true, test: true, - order: 9, + order: 10, features: [FEATURES.SENDS], ens: false, showGasLevels: false, - rpcUrls: [`https://scroll-l2-testnet-node.blockwallet.io`], - defaultRpcUrl: `https://scroll-l2-testnet-node.blockwallet.io`, - blockExplorerUrls: ['https://l2scan.scroll.io/'], - blockExplorerName: 'Scroll L2 Explorer', + currentRpcUrl: `https://testrpc.x1.tech/`, + defaultRpcUrl: `https://testrpc.x1.tech/`, + backupRpcUrls: ['https://x1testrpc.okx.com/'], + blockExplorerUrls: ['https://www.oklink.com/x1-test'], + blockExplorerName: 'X1 Testnet Explorer', actionsTimeIntervals: { ...SLOW_TESTNET_TIME_INTERVALS_DEFAULT_VALUES }, tornadoIntervals: { depositConfirmations: DEFAULT_TORNADO_CONFIRMATION, @@ -625,29 +805,30 @@ export const INITIAL_NETWORKS: Networks = { }, nativelySupported: true, }, - RSK_TESTNET: { - name: 'rsk_testnet', - desc: 'Rootstock Testnet', - chainId: 31, - networkVersion: '31', + ZETACHAIN_TESTNET: { + name: 'zetachain_testnet', + desc: 'ZetaChain Testnet', + chainId: 7001, + networkVersion: '7001', nativeCurrency: { - name: 'Testnet Smart Bitcoin', - symbol: 'tRBTC', + name: 'Testnet ZETA', + symbol: 'aZETA', decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/zetachaintestnet/info/logo.png', }, hasFixedGasCost: false, enable: true, test: true, - order: 10, + order: 11, features: [FEATURES.SENDS], ens: false, showGasLevels: true, iconUrls: [ - 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/rsk/assets/0x/logo.png', + 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/zetachaintestnet/info/logo.png', ], - rpcUrls: ['https://did.testnet.rsk.co:4444'], - blockExplorerName: 'RSK Testnet Explorer', - blockExplorerUrls: ['https://explorer.testnet.rsk.co'], + currentRpcUrl: 'https://rpc.ankr.com/zetachain_evm_athens_testnet', + blockExplorerName: 'ZetaChain Testnet Explorer', + blockExplorerUrls: ['https://zetachain-athens-3.blockscout.com/'], actionsTimeIntervals: { ...SLOW_TESTNET_TIME_INTERVALS_DEFAULT_VALUES }, tornadoIntervals: { depositConfirmations: DEFAULT_TORNADO_CONFIRMATION, @@ -664,6 +845,7 @@ export const INITIAL_NETWORKS: Networks = { name: 'Ether', symbol: 'ETH', decimals: 18, + logo: 'https://raw.githubusercontent.com/block-wallet/assets/master/blockchains/ethereum/info/logo.png', }, hasFixedGasCost: false, enable: true, @@ -672,7 +854,7 @@ export const INITIAL_NETWORKS: Networks = { features: [FEATURES.SENDS], ens: false, showGasLevels: false, - rpcUrls: ['http://localhost:8545'], + currentRpcUrl: 'http://localhost:8545', actionsTimeIntervals: { ...ACTIONS_TIME_INTERVALS_DEFAULT_VALUES }, tornadoIntervals: { depositConfirmations: DEFAULT_TORNADO_CONFIRMATION, @@ -681,8 +863,3 @@ export const INITIAL_NETWORKS: Networks = { nativelySupported: true, }, }; - -export const HARDFORKS = { - BERLIN: 'berlin', - LONDON: 'london', -}; diff --git a/packages/background/src/utils/contractsInfo.ts b/packages/background/src/utils/contractsInfo.ts index c9db2c9c4..9eb10e467 100644 --- a/packages/background/src/utils/contractsInfo.ts +++ b/packages/background/src/utils/contractsInfo.ts @@ -24,7 +24,7 @@ export async function fetchContractDetails( try { const responseText = await retryHandling( () => - http.get( + http.request( `${CONTRACTS_URL}/${chainId}/${toChecksumAddress( address )}.json` diff --git a/packages/background/src/utils/currency.ts b/packages/background/src/utils/currency.ts index f7b359375..6dac0e924 100644 --- a/packages/background/src/utils/currency.ts +++ b/packages/background/src/utils/currency.ts @@ -2,6 +2,7 @@ import CURRENCIES from './constants/currencies.json'; export interface Currency { code: string; name?: string; + symbol?: string; } export const getCurrencies = (): Currency[] => { return CURRENCIES.sort((a, b) => a.code.localeCompare(b.code)); diff --git a/packages/background/src/utils/env.ts b/packages/background/src/utils/env.ts new file mode 100644 index 000000000..fc97e52a7 --- /dev/null +++ b/packages/background/src/utils/env.ts @@ -0,0 +1,7 @@ +export const ENVIRONMENT = process.env.NODE_ENV; + +function isDevEnvironment(): boolean { + return ENVIRONMENT === 'development'; +} + +export { isDevEnvironment }; diff --git a/packages/background/src/utils/ethereumChain.ts b/packages/background/src/utils/ethereumChain.ts index 1c101dcbc..f529b345c 100644 --- a/packages/background/src/utils/ethereumChain.ts +++ b/packages/background/src/utils/ethereumChain.ts @@ -1,5 +1,4 @@ import { StaticJsonRpcProvider } from '@ethersproject/providers'; -import { memoize } from 'lodash'; import { getChainListItem } from './chainlist'; import { AddEthereumChainParameter, @@ -9,6 +8,7 @@ import { isABlockWalletNode, customHeadersForBlockWalletNode, } from '../utils/nodes'; +import { isHttpsURL } from './http'; /** * It validates and parses the chainId parameter checking if it's in the expected form @@ -82,20 +82,18 @@ export const validateNetworkChainId = async ( * * @param rpcUrl The RPC endpoint */ -export const getCustomRpcChainId = memoize( - async (rpcUrl: string): Promise => { - // Check that chainId matches with network's - const tempProvider = new StaticJsonRpcProvider({ - url: rpcUrl, - headers: isABlockWalletNode(rpcUrl) - ? customHeadersForBlockWalletNode - : undefined, - }); - const { chainId: rpcChainId } = await tempProvider.getNetwork(); +export const getCustomRpcChainId = async (rpcUrl: string): Promise => { + // Check that chainId matches with network's + const tempProvider = new StaticJsonRpcProvider({ + url: rpcUrl, + headers: isABlockWalletNode(rpcUrl) + ? customHeadersForBlockWalletNode + : undefined, + }); + const { chainId: rpcChainId } = await tempProvider.getNetwork(); - return rpcChainId; - } -); + return rpcChainId; +}; /** * Validates the parameters passed to add a new chain @@ -119,7 +117,7 @@ export const validateAddEthereumChainParameters = async ( throw new Error('Invalid type for blockExplorerUrls'); } else { const explorerUrl = blockExplorerUrls[0]; - if (explorerUrl && explorerUrl.indexOf('https://') === -1) { + if (explorerUrl && !isHttpsURL(explorerUrl)) { throw new Error('Block explorer endpoint must be https'); } } @@ -129,7 +127,7 @@ export const validateAddEthereumChainParameters = async ( if (!Array.isArray(iconUrls)) { throw new Error('Invalid type for iconUrls'); } else { - if (iconUrls.length > 0 && iconUrls[0].indexOf('https://') === -1) { + if (iconUrls.length > 0 && !isHttpsURL(iconUrls[0])) { throw new Error( 'Invalid icon URL provided: protocol must be https' ); @@ -146,17 +144,14 @@ export const validateAddEthereumChainParameters = async ( throw new Error('Invalid type for rpcUrls'); } else { const rpcUrl = rpcUrls[0]; - if (rpcUrl && rpcUrl.indexOf('https://') === -1) { + if (rpcUrl && !isHttpsURL(rpcUrl)) { throw new Error('Invalid RPC provided: protocol must be https'); } } } else { if (chainDataFromList) { const rpcUrl = chainDataFromList.rpc[0]; - if ( - typeof rpcUrl === 'undefined' || - rpcUrl.indexOf('https://') === -1 - ) { + if (typeof rpcUrl === 'undefined' || !isHttpsURL(rpcUrl)) { throw new Error('Invalid RPC provided'); } } diff --git a/packages/background/src/utils/hasher.ts b/packages/background/src/utils/hasher.ts index b55e5f5d9..5add16709 100644 --- a/packages/background/src/utils/hasher.ts +++ b/packages/background/src/utils/hasher.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { sha256 } from 'ethereumjs-util'; +import { createHash } from 'crypto'; +import browser from 'webextension-polyfill'; export const SALT = - chrome && chrome.runtime && chrome.runtime.id ? chrome.runtime.id : 'salt'; + browser && browser.runtime && browser.runtime.id + ? browser.runtime.id + : 'salt'; export function Hash( target: any, @@ -65,9 +68,10 @@ export class Hasher { for (const [index, paramValue] of paramValues.entries()) { if (paramIndexes.indexOf(index) != -1) { if (typeof paramValue !== 'undefined') { - paramValues[index] = sha256( - Buffer.from('' + paramValue + SALT) - ).toString(); + paramValues[index] = createHash('sha256') + .update(Buffer.from('' + paramValue + SALT)) + .digest() + .toString(); } } } diff --git a/packages/background/src/utils/http.ts b/packages/background/src/utils/http.ts index ca4f765c8..d8eb376cd 100644 --- a/packages/background/src/utils/http.ts +++ b/packages/background/src/utils/http.ts @@ -12,7 +12,10 @@ export class RequestError extends Error { } const GET = 'GET'; -const POST = 'POST'; + +export function isHttpsURL(url: string): boolean { + return url.startsWith('https://'); +} function isJsonResponse(r: Response) { return r.headers.get('content-type')?.includes('application/json'); @@ -42,22 +45,23 @@ const fetchWithTimeout = async ( return response; }; +const defaultOptions = { + method: GET, + timeout: 60000, + cache: 'default' as RequestCache, +}; const request = async ( url: string, - params: Record | undefined, - method = GET, - timeout = 60000, - cache: RequestCache = 'default' + options: RequestOptions = defaultOptions ): Promise => { - const options: RequestInit & { timeout?: number } = { - method, - timeout, - cache, + const safeOptions: RequestOptions = { + ...defaultOptions, + ...options, }; // Check the method and set the options accordingly - if (method === GET) { - const safeParams = Object.entries(params || {}).reduce( + if (safeOptions.method === GET) { + const safeParams = Object.entries(safeOptions.params || {}).reduce( (acc, [key, value]) => { if (isNil(value)) { return acc; @@ -70,12 +74,10 @@ const request = async ( {} ); url += '?' + new URLSearchParams(safeParams).toString(); - } else { - options.body = JSON.stringify(params); } // Fetch with timeout - const response = await fetchWithTimeout(url, options); + const response = await fetchWithTimeout(url, safeOptions); // If response ok, we assume data is JSON type if (response.ok) { @@ -83,11 +85,18 @@ const request = async ( } // If response is not ok, check if content-type is json before converting. - const data = isJsonResponse(response) && (await response.json()); + const data = isJsonResponse(response) ? await response.json() : undefined; // Check if there's an 'error' or err' key in the response - const errMessage = - 'error' in data ? data.error : 'err' in data ? data.err : undefined; + let errMessage = ''; + if (data && typeof data === 'object') { + for (const prop in data) { + if (['err', 'error'].includes(prop)) { + errMessage = data[prop]; + break; + } + } + } // Throw the request error throw new RequestError( @@ -97,57 +106,22 @@ const request = async ( ); }; -const get = async < - T, - P extends Record | undefined = Record ->( - url: string, - params?: P, - timeout?: number, - cache?: RequestCache -) => request(url, params, GET, timeout, cache); - -const post = async < - T, - P extends Record | undefined = Record ->( - url: string, - params?: P, - timeout?: number, - cache?: RequestCache -) => request(url, params, POST, timeout, cache); +export interface RequestOptions extends RequestInit { + params?: Record | undefined; + timeout?: number; +} interface HttpClient { /** * Performs an HTTP GET request * * @param url the URL - * @param params query parameters - * @returns The parsed JSON response - */ - get | undefined = Record>( - url: string, - params?: P, - timeout?: number, - cache?: RequestCache - ): Promise; - - /** - * Performs an HTTP POST request - * - * @param url the URL - * @param params The body content + * @param options options of the request * @returns The parsed JSON response */ - post | undefined = Record>( - url: string, - params?: P, - timeout?: number, - cache?: RequestCache - ): Promise; + request(url: string, options?: RequestOptions): Promise; } export default { - get, - post, + request, } as HttpClient; diff --git a/packages/background/src/utils/manifest.ts b/packages/background/src/utils/manifest.ts index c6339882d..208e79c0f 100644 --- a/packages/background/src/utils/manifest.ts +++ b/packages/background/src/utils/manifest.ts @@ -1,7 +1,6 @@ +import browser from 'webextension-polyfill'; + export const isManifestV3 = () => { - if (!chrome || !chrome.runtime || !chrome.runtime.getManifest()) { - return false; - } else { - return chrome.runtime.getManifest().manifest_version === 3; - } + console.log('isManifestV3'); + return browser.runtime.getManifest().manifest_version === 3; }; diff --git a/packages/background/src/utils/notifications.ts b/packages/background/src/utils/notifications.ts deleted file mode 100644 index 3e6474c6b..000000000 --- a/packages/background/src/utils/notifications.ts +++ /dev/null @@ -1,217 +0,0 @@ -import { - TransactionCategories, - TransactionMeta, - TransactionStatus, -} from '../controllers/transactions/utils/types'; -import { - createCustomAccountLink, - createCustomExplorerLink, -} from '@block-wallet/explorer-link'; -import { getChainListItem } from './chainlist'; - -export const showSetUpCompleteNotification = (): void => { - const url = ''; - const title = 'Block Wallet is ready!'; - const message = - "You've completed the set-up process. Check the extension in the upper right corner of your browser."; - - showNotification(title, message, url); -}; - -export const showTransactionNotification = (txMeta: TransactionMeta): void => { - const { status, transactionCategory } = txMeta; - - if ( - transactionCategory === TransactionCategories.BLANK_DEPOSIT || - transactionCategory === TransactionCategories.BLANK_WITHDRAWAL - ) { - showBlankContractNotification(txMeta); - } else if (status === TransactionStatus.CONFIRMED) { - showSucceededTransaction(txMeta); - } else if (status === TransactionStatus.FAILED) { - showFailedTransaction(txMeta); - } else if (status === TransactionStatus.REJECTED) { - showRejectedTransaction(txMeta.error?.message ?? ''); - } -}; - -export const showBlankContractNotification = ( - txMeta: TransactionMeta -): void => { - const { status } = txMeta; - - if (status === TransactionStatus.CONFIRMED) { - showSucceededBlankInteraction(txMeta); - } else if (status === TransactionStatus.FAILED) { - showFailedBlankInteraction(txMeta); - } else if (status === TransactionStatus.REJECTED) { - showRejectedBlankInteraction(txMeta.error?.message ?? ''); - } -}; - -export const showIncomingTransactionNotification = ( - account: string, - chainId: number, - section?: '' | 'tokentxns' | 'tokentxnsErc721' | 'tokentxnsErc1155' -): void => { - const explorerUrl = getExplorerUrl(chainId); - if (!explorerUrl) { - return; - } - - addOnClickListener(); - - const url = createCustomAccountLink( - account as string, - explorerUrl, - section - ); - const title = 'Incoming Transaction'; - const message = 'An incoming transaction to your address was confirmed!'; - - showNotification(title, message, url); -}; - -const showSucceededTransaction = (txMeta: TransactionMeta) => { - const { chainId, transactionParams } = txMeta; - if (!chainId) { - return; - } - - const explorerUrl = getExplorerUrl(chainId); - if (!explorerUrl) { - return; - } - - addOnClickListener(); - - const { hash, nonce } = transactionParams; - - const url = createCustomExplorerLink(hash as string, explorerUrl); - const title = 'Transaction confirmed'; - const message = `Transaction with nonce ${nonce} confirmed!`; - - showNotification(title, message, url); -}; - -const showSucceededBlankInteraction = (txMeta: TransactionMeta) => { - const { chainId, transactionParams } = txMeta; - if (!chainId) { - return; - } - - const explorerUrl = getExplorerUrl(chainId); - if (!explorerUrl) { - return; - } - - addOnClickListener(); - - const { hash } = transactionParams; - - const url = createCustomExplorerLink(hash as string, explorerUrl); - const title = 'Blank interaction succeeded'; - const message = 'Privacy Smart Contract interaction has been confirmed!'; - - showNotification(title, message, url); -}; - -const showFailedTransaction = (txMeta: TransactionMeta) => { - const { chainId, transactionParams } = txMeta; - if (!chainId) { - return; - } - - const explorerUrl = getExplorerUrl(chainId); - if (!explorerUrl) { - return; - } - - addOnClickListener(); - - const { hash, nonce } = transactionParams; - - const url = createCustomExplorerLink(hash as string, explorerUrl); - const title = 'Transaction failed'; - const message = `Transaction with nonce ${nonce} failed!`; - - showNotification(title, message, url); -}; - -const showFailedBlankInteraction = (txMeta: TransactionMeta) => { - const { chainId, transactionParams } = txMeta; - if (!chainId) { - return; - } - - const explorerUrl = getExplorerUrl(chainId); - if (!explorerUrl) { - return; - } - - addOnClickListener(); - - const { hash } = transactionParams; - - const url = createCustomExplorerLink(hash as string, explorerUrl); - const title = 'Blank interaction failed'; - const message = 'Privacy Smart Contract interaction failed!'; - - showNotification(title, message, url); -}; - -const showRejectedTransaction = (message: string) => { - addOnClickListener(); - - const title = 'Transaction was rejected'; - - showNotification(title, message, ''); -}; - -const showRejectedBlankInteraction = (message: string) => { - addOnClickListener(); - - const title = 'Blank interaction rejected'; - - showNotification(title, message, ''); -}; - -const showNotification = (title: string, message: string, url: string) => { - chrome.notifications.create(url, { - title: title, - message: message, - iconUrl: chrome.runtime.getURL('icons/icon-48.png'), - type: 'basic', - }); -}; - -const addOnClickListener = () => { - if (!chrome.notifications.onClicked.hasListener(linkToEtherscan)) { - chrome.notifications.onClicked.addListener(linkToEtherscan); - } -}; - -const linkToEtherscan = (url: string) => { - if (url.startsWith('https://')) { - chrome.tabs.create({ url: url }); - } -}; - -const getExplorerUrl = (chainId: number) => { - if (isNaN(chainId)) { - return undefined; - } - - const network = getChainListItem(chainId); - if (!network) { - return undefined; - } - if (!network.explorers || !network.explorers.length) { - return undefined; - } - if (!network.explorers.some((e) => e.url)) { - return undefined; - } - - return network.explorers.find((e) => e.url)?.url; -}; diff --git a/packages/background/src/utils/onrampApi.ts b/packages/background/src/utils/onrampApi.ts new file mode 100644 index 000000000..881955d64 --- /dev/null +++ b/packages/background/src/utils/onrampApi.ts @@ -0,0 +1,92 @@ +import { Currency } from './currency'; +import httpClient from './http'; +import { MILISECOND } from '../utils/constants/time'; +import { retryHandling } from './retryHandling'; + +export enum OnrampImplementation { + ONRAMPER_BUY = 'Onramper_BUY', +} + +interface OnRampToken { + address: string; + code: string; + icon: string; + id: string; + name: string; + network: string; + symbol: string; + decimals: number; + chainId: string; +} + +interface GetOnrampCurrencyResponse { + message: { + crypto: OnRampToken[]; + fiat: Currency[]; + }; +} + +interface GetOnrampNetworkResponse { + chainId: string; + networkName: string; +} + +export interface IOnramp { + getSupportedCurrencies: () => Promise; + getSupportedNetworks: () => Promise; +} + +const ONRAMPER_API_KEY = 'pk_prod_01GYCJHNRP0V65F272K4Z02JY0'; + +const ONRAMP_ENDPOINT = 'https://api.onramper.com/'; +const API_CALLS_DELAY = 100 * MILISECOND; +const API_CALLS_RETRIES = 5; + +const OnrampBuy: IOnramp = { + getSupportedCurrencies: + async function (): Promise { + const apiresponse = await retryHandling( + () => + httpClient.request( + `${ONRAMP_ENDPOINT}/supported`, + { + headers: { Authorization: ONRAMPER_API_KEY }, + } + ), + API_CALLS_DELAY, + API_CALLS_RETRIES + ); + return apiresponse; + }, + getSupportedNetworks: async function (): Promise< + GetOnrampNetworkResponse[] + > { + const apiresponse = await retryHandling( + () => + httpClient.request( + `${ONRAMP_ENDPOINT}/supported`, + { + headers: { Authorization: ONRAMPER_API_KEY }, + } + ), + API_CALLS_DELAY, + API_CALLS_RETRIES + ); + + const result: GetOnrampNetworkResponse[] = []; + apiresponse.message.crypto.map((token) => + result.push({ chainId: token.chainId, networkName: token.network }) + ); + + return result.filter( + (value, index) => + result.map((r) => r.chainId).indexOf(value.chainId) === index + ); + }, +}; + +const onrampAPIs: Record = { + [OnrampImplementation.ONRAMPER_BUY]: OnrampBuy, +}; + +export default onrampAPIs; diff --git a/packages/background/src/utils/popup.ts b/packages/background/src/utils/popup.ts index f33fdf7ab..031eaa81d 100644 --- a/packages/background/src/utils/popup.ts +++ b/packages/background/src/utils/popup.ts @@ -17,10 +17,11 @@ import { } from './types/communication'; import log from 'loglevel'; import { POPUP_TAB_NAME } from './constants/tab'; +import { toError } from './toError'; // Define window size for each os const windowSize: { [os in PlatformOS]: { height: number; width: number } } = { - win: { height: 639, width: 373 }, + win: { height: 640, width: 373 }, mac: { height: 630, width: 358 }, linux: { height: 600, width: 357 }, android: { height: 600, width: 357 }, @@ -142,6 +143,13 @@ export const closeExtensionInstance = (instanceId: string): void => { id: BackgroundActions.CLOSE_WINDOW, } as TransportResponseMessage); } catch (error) { + const safeError = toError(error); + if ( + safeError.message + .toLowerCase() + .includes('attempting to use a disconnected port object') + ) + delete extensionInstances[instanceId]; log.error(`Couldn't close instance ${instanceId}`); } }; diff --git a/packages/background/src/utils/rateService.ts b/packages/background/src/utils/rateService.ts index f2c51cc60..316b331a5 100644 --- a/packages/background/src/utils/rateService.ts +++ b/packages/background/src/utils/rateService.ts @@ -5,6 +5,16 @@ import { formatUnits } from '@ethersproject/units'; import { RATES_IDS_LIST } from '@block-wallet/chains-assets'; import CHAINLINK_DATAFEEDS_CONTRACTS from './chain-link/dataFeeds'; import httpClient from '../utils/http'; +import log from 'loglevel'; +import { customHeadersForBlockWalletNode } from './nodes'; +import { isDevEnvironment } from './env'; +import { MINUTE } from './constants/time'; + +export const BLOCK_WALLET_COINS_ENDPOINT = 'https://coin.blockwallet.io/simple'; + +export const COINGECKO_PUBLIC_ENDPOINT = + 'https://api.coingecko.com/api/v3/simple'; + interface getRateOptions { networkProvider?: StaticJsonRpcProvider; } @@ -17,7 +27,15 @@ export interface RateService { ): Promise; } -export const BaseApiEndpoint = 'https://api.coingecko.com/api/v3/simple/'; +export interface CoingeckoService extends RateService { + getTokensRates( + coingeckoPlatformId: string, + tokenContracts: string[], + nativeCurrency: string + ): Promise<{ + [lowerCaseAddress: string]: { [currency: string]: number }; + }>; +} export const chainLinkService: RateService = { async getRate(currency, symbol, options: getRateOptions = {}) { @@ -46,36 +64,80 @@ export const chainLinkService: RateService = { //At this point there is no dataFeedPair in CHAINLINK_DATAFEEDS_CONTRACTS return 0; } catch (e) { - console.log('Failed fecthing price from ChainLink. Ex: ' + e); + log.error('Failed fecthing price from ChainLink. Ex: ' + e); return 0; } }, }; -export const coingekoService: RateService = { +export const coingeckoService: ( + endpoint: string, + fallbackEndpoint?: string +) => CoingeckoService = (baseEndpoint: string, fallbackEndpoint?: string) => ({ async getRate(currency, symbol) { try { - const query = `${BaseApiEndpoint}price`; + const query = `${baseEndpoint}/price`; const currencyApiId = overloadCurrencyApiId( RATES_IDS_LIST[ symbol.toUpperCase() as keyof typeof RATES_IDS_LIST ] ); - const response = await httpClient.get< + const response = await httpClient.request< Record> >(query, { - ids: currencyApiId, - vs_currencies: currency, + params: { ids: currencyApiId, vs_currencies: currency }, + headers: customHeadersForBlockWalletNode, + timeout: 1.5 * MINUTE, //Sometimes coingecko takes more than 1 minute to respond }); return response[currencyApiId][currency]; } catch (e) { - console.log('Failed fecthing price from Coingeko. Ex: ' + e); + if (fallbackEndpoint) { + log.error( + `Primary endpoint failed: ${baseEndpoint}. Ex: ${e}.\nUsing fallback endpoint: ${fallbackEndpoint}` + ); + return coingeckoService(fallbackEndpoint).getRate( + currency, + symbol + ); + } + log.error('Failed fecthing price from Coingecko. Ex: ' + e); return 0; } }, -}; + async getTokensRates( + coingeckoPlatformId: string, + tokenContracts: string[], + nativeCurrency: string + ): Promise<{ + [lowerCaseAddress: string]: { [currency: string]: number }; + }> { + const query = `${baseEndpoint}/token_price/${coingeckoPlatformId}`; + try { + return httpClient.request(query, { + params: { + contract_addresses: tokenContracts.join(','), + vs_currencies: nativeCurrency, + }, + headers: customHeadersForBlockWalletNode, + timeout: 1.5 * MINUTE, //Sometimes coingecko takes more than 1 minute to respond + }); + } catch (e) { + if (fallbackEndpoint) { + log.error( + `Primary endpoint failed: ${baseEndpoint}. Ex: ${e}.\nUsing fallback endpoint: ${fallbackEndpoint}` + ); + return coingeckoService(fallbackEndpoint).getTokensRates( + coingeckoPlatformId, + tokenContracts, + nativeCurrency + ); + } + throw e; + } + }, +}); const overloadCurrencyApiId = (currencyApiId: string) => { switch (currencyApiId) { @@ -90,11 +152,22 @@ const rateProvider: Record = { 'usd-eth': chainLinkService, }; +export const getCoingeckoService = (): CoingeckoService => { + if (isDevEnvironment()) { + return coingeckoService(COINGECKO_PUBLIC_ENDPOINT); + } + + return coingeckoService( + BLOCK_WALLET_COINS_ENDPOINT, + COINGECKO_PUBLIC_ENDPOINT + ); +}; + export const getRateService = ( nativeCurrency: string, tokenSymbol: string ): RateService => { const provider = rateProvider[`${nativeCurrency}-${tokenSymbol}`.toLowerCase()]; - return provider || coingekoService; + return provider || getCoingeckoService(); }; diff --git a/packages/background/src/utils/releaseNotes.ts b/packages/background/src/utils/releaseNotes.ts index bd6b5dd90..f56c7fb14 100644 --- a/packages/background/src/utils/releaseNotes.ts +++ b/packages/background/src/utils/releaseNotes.ts @@ -1,5 +1,5 @@ import { ReleaseNote } from '../controllers/PreferencesController'; -import compareVersions from 'compare-versions'; +import { compareVersions, validate } from 'compare-versions'; interface Options { lastVersionSeen?: string; stackNotes?: boolean; @@ -16,7 +16,7 @@ const generateReleaseNotesNews = ( userCurrentVersion: string, options: Options = defaultOptions ): ReleaseNote[] | null => { - if (!compareVersions.validate(userCurrentVersion)) { + if (!validate(userCurrentVersion)) { return null; } const safeOptions = { diff --git a/packages/background/src/utils/scanner/EtherscanFetcher.ts b/packages/background/src/utils/scanner/EtherscanFetcher.ts index 82a4264c7..417a3d5ea 100644 --- a/packages/background/src/utils/scanner/EtherscanFetcher.ts +++ b/packages/background/src/utils/scanner/EtherscanFetcher.ts @@ -28,17 +28,14 @@ export class EtherscanFetcher { ): Promise<{ result: T[]; status: string }> { let retry = 0; while (retry < this.config.maxRetries) { - const result = await httpClient.get<{ + const result = await httpClient.request<{ status: string; message: string; result: T[]; - }>( - `${this.etherscanApiUrl}/api`, - { - ...params, - }, - 30000 - ); + }>(`${this.etherscanApiUrl}/api`, { + params: params, + timeout: 30000, + }); if (result.status === '0' && result.message === 'NOTOK') { await sleep(this.config.explorerAPIDelay); diff --git a/packages/background/src/utils/signature.ts b/packages/background/src/utils/signature.ts index 82adc7303..069bbc841 100644 --- a/packages/background/src/utils/signature.ts +++ b/packages/background/src/utils/signature.ts @@ -5,7 +5,7 @@ import { isValidAddress, stripHexPrefix, toChecksumAddress, -} from 'ethereumjs-util'; +} from '@ethereumjs/util'; import { JSONRPCMethod, MessageSchema, diff --git a/packages/background/src/utils/types/1inch.ts b/packages/background/src/utils/swaps/1inch.ts similarity index 51% rename from packages/background/src/utils/types/1inch.ts rename to packages/background/src/utils/swaps/1inch.ts index f251450ba..8d2fb0914 100644 --- a/packages/background/src/utils/types/1inch.ts +++ b/packages/background/src/utils/swaps/1inch.ts @@ -5,7 +5,21 @@ * * https://docs.1inch.io/docs/aggregation-protocol/introduction */ +import { + SwapParameters, + SwapQuoteParams, + SwapQuoteResponse, + SwapRequestParams, +} from '@block-wallet/background/controllers/SwapController'; import { INITIAL_NETWORKS } from '../constants/networks'; +import { retryHandling } from '../retryHandling'; +import httpClient from './../http'; +import { + get1InchErrorMessageFromResponse, + map1InchErrorMessage, +} from './1inchError'; +import { BigNumber } from '@ethersproject/bignumber'; +import { ContractSignatureParser } from '@block-wallet/background/controllers/transactions/ContractSignatureParser'; const KLAYTN_MAINNET_CHAIN_ID = 8217; const AURORA_MAINNET_CHAIN_ID = 1313161554; @@ -20,6 +34,7 @@ export const ONEINCH_SWAPS_NETWORKS: number[] = [ INITIAL_NETWORKS.AVALANCHEC.chainId, INITIAL_NETWORKS.FANTOM.chainId, INITIAL_NETWORKS.XDAI.chainId, + INITIAL_NETWORKS.ZKSYNC_ERA_MAINNET.chainId, KLAYTN_MAINNET_CHAIN_ID, AURORA_MAINNET_CHAIN_ID, ]; @@ -37,8 +52,7 @@ export const GAS_LIMIT_INCREASE = 1.25; export const ONEINCH_SWAPS_ENDPOINT = 'https://api-blockwallet.1inch.io/v5.0/'; // This address makes reference to the native asset -export const ONEINCH_NATIVE_ADDRESS = - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; +export const SWAP_NATIVE_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; // Spender API response export interface OneInchSpenderRes { @@ -51,8 +65,8 @@ export interface BasicToken { name: string; decimals: number; address: string; - logoURI: string; - tags: string[]; + logoURI?: string; + tags?: string[]; } export interface OneInchSwapQuoteParams { @@ -105,13 +119,129 @@ export interface OneInchSwapRequestResponse { toToken: BasicToken; fromTokenAmount: string; // Input amount of fromToken in minimal divisible units toTokenAmount: string; // Result amount of toToken in minimal divisible units - protocols: unknown[]; // Route of the trade - tx: { - from: string; // Transactions will be sent from this address - to: string; // Transactions will be sent to our contract address - data: string; // Bytes of call data - value: string; // Amount of ETH (in wei) will be sent to the contract address - gas: number; // Estimated amount of the gas limit, increase this value by 25% - gasPrice: string; // Gas price in wei - }; + protocols?: unknown[]; // Route of the trade + tx: SwapTxMeta; +} + +export interface SwapTxMeta { + from: string; // Transactions will be sent from this address + to: string; // Transactions will be sent to our contract address + data: string; // Bytes of call data + value: string; // Amount of ETH (in wei) will be sent to the contract address + gas: number; // Estimated amount of the gas limit, increase this value by 25% + gasPrice: string; // Gas price in wei } + +export const OneInchService = { + parseQuoteParams({ + fromToken, + toToken, + amount, + }: SwapQuoteParams): OneInchSwapQuoteParams { + return { + fromTokenAddress: + fromToken.address === '0x0' + ? SWAP_NATIVE_ADDRESS + : fromToken.address, + toTokenAddress: + toToken.address === '0x0' + ? SWAP_NATIVE_ADDRESS + : toToken.address, + amount, + }; + }, + async getSpender(chainId: number): Promise { + try { + // 1Inch router contract address + const res = await retryHandling(() => + httpClient.request( + `${ONEINCH_SWAPS_ENDPOINT}${chainId}/approve/spender` + ) + ); + + return res.address; + } catch (error) { + throw new Error('Unable to fetch exchange spender'); + } + }, + async getSwapQuote( + chainId: number, + params: SwapQuoteParams + ): Promise { + try { + const { fromTokenAddress, toTokenAddress, amount } = + this.parseQuoteParams(params); + const res = await retryHandling(() => + httpClient.request( + `${ONEINCH_SWAPS_ENDPOINT}${chainId}/quote`, + { + params: { + fromTokenAddress, + toTokenAddress, + amount, + fee: BASE_SWAP_FEE, + }, + } + ) + ); + + return { + ...res, + blockWalletFee: BigNumber.from(res.fromTokenAmount) + .mul(BASE_SWAP_FEE * 10) + .div(1000), + estimatedGas: Math.round(res.estimatedGas * GAS_LIMIT_INCREASE), + }; + } catch (error) { + const errMessage = map1InchErrorMessage( + get1InchErrorMessageFromResponse(error) // Error should be of type RequestError + ); + throw new Error(errMessage || 'Error getting 1Inch swap quote'); + } + }, + async getSwapParameters( + chainId: number, + signatureParser: ContractSignatureParser, + swapParams: SwapRequestParams + ): Promise { + try { + const res = await retryHandling(() => + httpClient.request( + `${ONEINCH_SWAPS_ENDPOINT}${chainId}/swap`, + { + params: { + ...swapParams, + fee: BASE_SWAP_FEE, + referrerAddress: REFERRER_ADDRESS, + allowPartialFill: false, + }, + } + ) + ); + + const methodSignature = await signatureParser.getMethodSignature( + res.tx.data, + res.tx.to + ); + + return { + ...res, + methodSignature, + blockWalletFee: BigNumber.from(res.fromTokenAmount) + .mul(BASE_SWAP_FEE * 10) + .div(1000), + tx: { + ...res.tx, + gas: Math.round(res.tx.gas * GAS_LIMIT_INCREASE), + }, + }; + } catch (error) { + const errMessage = map1InchErrorMessage( + get1InchErrorMessageFromResponse(error) // Error should be of type RequestError + ); + throw new Error( + errMessage || 'Error getting 1Inch swap parameters' + ); + } + }, +}; diff --git a/packages/background/src/utils/1inchError.ts b/packages/background/src/utils/swaps/1inchError.ts similarity index 97% rename from packages/background/src/utils/1inchError.ts rename to packages/background/src/utils/swaps/1inchError.ts index 465f98093..7364ca03f 100644 --- a/packages/background/src/utils/1inchError.ts +++ b/packages/background/src/utils/swaps/1inchError.ts @@ -1,4 +1,4 @@ -import { RequestError } from './http'; +import { RequestError } from '../http'; const EXACT_MATCH_ERRORS = ['Insufficient liquidity', 'Cannot estimate']; const NOT_ENOUGH_BALANCE = `Balance too low to cover swap and gas cost.`; diff --git a/packages/background/src/utils/swaps/openOcean.ts b/packages/background/src/utils/swaps/openOcean.ts new file mode 100644 index 000000000..70aefc99a --- /dev/null +++ b/packages/background/src/utils/swaps/openOcean.ts @@ -0,0 +1,273 @@ +import { INITIAL_NETWORKS } from '../constants/networks'; +import { + BASE_SWAP_FEE, + BasicToken, + REFERRER_ADDRESS, + SWAP_NATIVE_ADDRESS, +} from './1inch'; +import { retryHandling } from '../retryHandling'; +import httpClient from './../http'; +import { + SwapParameters, + SwapQuoteParams, + SwapQuoteResponse, + SwapRequestParams, +} from '@block-wallet/background/controllers/SwapController'; +import { formatUnits } from 'ethers/lib/utils'; +import { BigNumber } from '@ethersproject/bignumber'; +import { ContractSignatureParser } from '@block-wallet/background/controllers/transactions/ContractSignatureParser'; + +type OpenOceanNetworks = { + // ChainId: Smart Contract (for allowance spender param) + [chainId: number]: string; +}; +/** + * OpenOcean Supported Chains + * https://docs.openocean.finance/dev/supported-chains + */ +export const OPENOCEAN_AGGREGATOR_NETWORKS: OpenOceanNetworks = { + [INITIAL_NETWORKS.MAINNET.chainId]: + '0x6352a56caadc4f1e25cd6c75970fa768a3304e64', + [INITIAL_NETWORKS.BSC.chainId]: + '0x6352a56caadc4f1e25cd6c75970fa768a3304e64', + [INITIAL_NETWORKS.POLYGON.chainId]: + '0x6352a56caadc4f1e25cd6c75970fa768a3304e64', + [INITIAL_NETWORKS.OPTIMISM.chainId]: + '0x6352a56caadc4f1e25cd6c75970fa768a3304e64', + [INITIAL_NETWORKS.ARBITRUM.chainId]: + '0x6352a56caadc4f1e25cd6c75970fa768a3304e64', + [INITIAL_NETWORKS.AVALANCHEC.chainId]: + '0x6352a56caadc4f1e25cd6c75970fa768a3304e64', + [INITIAL_NETWORKS.FANTOM.chainId]: + '0x6352a56caadc4f1e25cd6c75970fa768a3304e64', + [INITIAL_NETWORKS.XDAI.chainId]: + '0x6352a56caadc4f1e25cd6c75970fa768a3304e64', + [INITIAL_NETWORKS.ZKSYNC_ERA_MAINNET.chainId]: + '0x36A1aCbbCAfca2468b85011DDD16E7Cb4d673230', + [INITIAL_NETWORKS.POLYGON_ZKEVM.chainId]: + '0x6dd434082EAB5Cd134B33719ec1FF05fE985B97b', + [8453]: '0x6352a56caadc4f1e25cd6c75970fa768a3304e64', //Base + [59140]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //Linea + [288]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //Boba Network + [1285]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //Moonriver + [1313161554]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //Aurora + [25]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //Cronos + [1666600000]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //Harmony + [2222]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //Kava + [1088]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //Metis Andromeda + [42220]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //Celo + [40]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //Telos + [58]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //Ontology EVM + [66]: '0xc0006Be82337585481044a7d11941c0828FFD2D4', //OKX Chain + [128]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //HECO Chain + [204]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //opBNB + [5000]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //Mantle + [169]: '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64', //Manta + [534352]: '0x6352a56caadc4f1e25cd6c75970fa768a3304e64', //Scroll +}; + +export const OPENOCEAN_AGGREGATOR_ENDPOINT = + 'https://open-api.openocean.finance/v3/'; + +export const OPENOCEAN_GAS_LIMIT_INCREASE = 1.4; + +// https://docs.openocean.finance/dev/aggregator-api-and-sdk/aggregator-api#api-reference + +export interface OpenOceanSwapQuoteParams { + chain: string; + inTokenAddress: string; + outTokenAddress: string; + amount: string; // Please set token amount without decimals.e.g. -1.00 ETH set as 1 -1.23 USDC set as 1.23 + gasPrice?: number; // set the gas price in GWEI without decimals. + slippage?: number; // Define the acceptable slippage level by inputting a percentage value within the range of 0.05 to 50. + enabledDexIds?: string; // https://docs.openocean.finance/dev/aggregator-api-and-sdk/aggregator-api#get-dexes-list + disabledDexIds?: string; // enableDexIds has higher priority compare with disabledDexIds +} + +export interface OpenOceanSwapQuoteResponse { + code: number; + data: { + inToken: BasicToken; + outToken: BasicToken; + inAmount: string; + outAmount: string; + estimatedGas: number; + path: object; + }; +} + +export interface OpenOceanSwapRequestParams { + chain: string; + inTokenAddress: string; + outTokenAddress: string; + amount: string; // Please set token amount without decimals.e.g. -1.00 ETH set as 1 -1.23 USDC set as 1.23 + gasPrice: number; // set the gas price in GWEI without decimals. + slippage: number; // Define the acceptable slippage level by inputting a percentage value within the range of 0.05 to 50. + account: string; // seller's address, please ensure that it has been approved to spend the necessary amount of inTokenAddress + referrer: string; // the wallet address used to be mark as partners and receive an extra referrerFee from user. + referrerFee: number; // Specify the percentage of in-token you wish to receive from the transaction, within the range of 0% to 3%, with 1% represented as '1'. +} + +export interface OpenOceanSwapRequestResponse { + code: number; + data: { + inToken: BasicToken; + outToken: BasicToken; + inAmount: string; + outAmount: string; + estimatedGas: number; + from: string; + to: string; + value: string; + data: string; + }; +} + +export const OpenOceanService = { + parseQuoteParams( + chainId: number, + { fromToken, toToken, amount, gasPrice }: SwapQuoteParams + ): OpenOceanSwapQuoteParams { + return { + chain: chainId.toString(), + inTokenAddress: + fromToken.address === '0x0' + ? SWAP_NATIVE_ADDRESS + : fromToken.address, + outTokenAddress: + toToken.address === '0x0' + ? SWAP_NATIVE_ADDRESS + : toToken.address, + amount: formatUnits(amount, fromToken.decimals), + gasPrice: parseFloat(gasPrice ?? '0'), + slippage: 1, + }; + }, + parseSwapParams( + chainId: number, + { + fromToken, + toToken, + fromAddress, + amount, + gasPrice, + slippage, + }: SwapRequestParams + ): OpenOceanSwapRequestParams { + return { + chain: chainId.toString(), + account: fromAddress, + inTokenAddress: fromToken.address, + outTokenAddress: toToken.address, + amount: formatUnits(amount, fromToken.decimals), + gasPrice: parseFloat(gasPrice ?? '0'), + slippage, + referrer: REFERRER_ADDRESS, + referrerFee: BASE_SWAP_FEE, + }; + }, + getSpender(chainId: number): string { + if (chainId in OPENOCEAN_AGGREGATOR_NETWORKS) + return OPENOCEAN_AGGREGATOR_NETWORKS[chainId]; + + throw new Error('Unable to fetch exchange spender'); + }, + async getSwapQuote( + chainId: number, + params: SwapQuoteParams + ): Promise { + const { + chain, + inTokenAddress, + outTokenAddress, + amount, + slippage, + gasPrice, + } = this.parseQuoteParams(chainId, params); + const res = await retryHandling(() => + httpClient.request( + `${OPENOCEAN_AGGREGATOR_ENDPOINT}${chainId}/swap_quote`, + { + params: { + chain, + account: params.fromAddress, + referrer: REFERRER_ADDRESS, + referrerFee: BASE_SWAP_FEE, + inTokenAddress, + outTokenAddress, + amount, + slippage, + gasPrice, + }, + } + ) + ); + + if (res.code !== 200) + throw new Error( + 'Could not find a quote for the swap. Try a different amount or token and check you have enough funds for the gas.' + ); + + return { + fromToken: res.data.inToken, + fromTokenAmount: res.data.inAmount, + toToken: res.data.outToken, + toTokenAmount: res.data.outAmount, + blockWalletFee: BigNumber.from(res.data.inAmount) + .mul(BASE_SWAP_FEE * 10) + .div(1000), + estimatedGas: Math.round( + res.data.estimatedGas * OPENOCEAN_GAS_LIMIT_INCREASE + ), + }; + }, + async getSwapParameters( + chainId: number, + signatureParser: ContractSignatureParser, + swapParams: SwapRequestParams + ): Promise { + const params = this.parseSwapParams(chainId, swapParams); + const { code, data } = + await retryHandling(() => + httpClient.request( + `${OPENOCEAN_AGGREGATOR_ENDPOINT}${chainId}/swap_quote`, + { + params: { + ...params, + }, + } + ) + ); + + if (code !== 200) + throw new Error( + 'There was a problem fetching the quote. Please try again.' + ); + + const methodSignature = await signatureParser.getMethodSignature( + data.data, + data.to + ); + + return { + fromToken: data.inToken, + toToken: data.outToken, + fromTokenAmount: data.inAmount, + toTokenAmount: data.outAmount, + methodSignature, + tx: { + data: data.data, + from: data.from, + to: data.to, + gas: Math.round( + data.estimatedGas * OPENOCEAN_GAS_LIMIT_INCREASE + ), + value: data.value, + gasPrice: params.gasPrice.toString(), + }, + blockWalletFee: BigNumber.from(data.inAmount) + .mul(BASE_SWAP_FEE * 10) + .div(1000), + }; + }, +}; diff --git a/packages/background/src/utils/token.ts b/packages/background/src/utils/token.ts index 4e1f47a4a..62dbf42f8 100644 --- a/packages/background/src/utils/token.ts +++ b/packages/background/src/utils/token.ts @@ -2,7 +2,7 @@ import { addHexPrefix, isValidAddress, toChecksumAddress, -} from 'ethereumjs-util'; +} from '@ethereumjs/util'; import { compareAddresses } from '../controllers/transactions/utils/utils'; import { IToken, Token } from '../controllers/erc-20/Token'; import { @@ -10,8 +10,10 @@ import { WatchAssetParameters, WatchAssetReq, } from './types/ethereum'; -import { BigNumber } from 'ethers'; +import { BigNumber } from '@ethersproject/bignumber'; import { MaxUint256 } from '@ethersproject/constants'; +import { formatUnits } from '@ethersproject/units'; +import { FixedNumber } from '@ethersproject/bignumber'; const IS_BASE64_IMAGE = 'IS_BASE64_IMAGE'; @@ -168,3 +170,26 @@ export function mergeTokens( totalSupply: baseToken.totalSupply || mergeToken.totalSupply, }; } + +/** + * Formats the token amount. + * @param amount - The token amount. + * @param decimals - The token decimals. + * @param symbol - The token symbol. + * @returns The formatted token amount + symbol. + * @example + * formatTokenAmount('1000000000000000000', 18, 'ETH') // '1 ETH' + * formatTokenAmount('2000000000000000000', 18) // '2 tokens' + * formatTokenAmount('1000000000000000000') // 'some tokens' + * formatTokenAmount() // 'some tokens' + * formatTokenAmount(undefined, 18, 'ETH') // 'some ETH' + */ +export const formatTokenAmount = ( + amount: BigNumber | string, + decimals: number, + symbol: string +) => { + return `${FixedNumber.fromString(formatUnits(amount, decimals)) + .round(5) + .toString()} ${symbol}`; +}; diff --git a/packages/background/src/utils/types/communication.ts b/packages/background/src/utils/types/communication.ts index 5244d46bb..0561b1699 100644 --- a/packages/background/src/utils/types/communication.ts +++ b/packages/background/src/utils/types/communication.ts @@ -19,13 +19,16 @@ import { import { TransactionAdvancedData, TransactionMeta, + TransactionStatus, } from '../../controllers/transactions/utils/types'; import { ImportStrategy, ImportArguments } from '../account'; import { SwapParameters, ExchangeType, - SwapQuote, + SwapQuoteResponse, SwapTransaction, + SwapQuoteParams, + SwapRequestParams, } from '../../controllers/SwapController'; import { ProviderEvents, @@ -49,7 +52,6 @@ import { import { TransactionFeeData } from '@block-wallet/background/controllers/erc-20/transactions/SignedTransaction'; import { Currency } from '../currency'; import { Devices } from './hardware'; -import { OneInchSwapQuoteParams, OneInchSwapRequestParams } from './1inch'; import { ChainListItem } from '@block-wallet/chains-assets'; import { IChain } from './chain'; import { @@ -61,6 +63,10 @@ import { } from '@block-wallet/background/controllers/BridgeController'; import { GasPriceData } from '@block-wallet/background/controllers/GasPricesController'; import { RemoteConfigsControllerState } from '@block-wallet/background/controllers/RemoteConfigsController'; +import { TypedTransaction } from '@ethereumjs/tx'; +import browser from 'webextension-polyfill'; +import { GetOnRampCurrencies } from '@block-wallet/background/controllers/OnrampController'; +import { SwapTxMeta } from '../swaps/1inch'; enum ACCOUNT { CREATE = 'CREATE_ACCOUNT', @@ -77,6 +83,13 @@ enum ACCOUNT { REFRESH_TOKEN_ALLOWANCES = 'REFRESH_TOKEN_ALLOWANCES', UNHIDE = 'UNHIDE_ACCOUNT', GET_NATIVE_TOKEN_BALANCE = 'GET_NATIVE_TOKEN_BALANCE', + EDIT_ACCOUNT_TOKENS_ORDER = 'EDIT_ACCOUNT_TOKENS_ORDER', + SET_ACCOUNT_SORT_VALUE = 'SET_ACCOUNT_SORT_VALUE', + ORDER_ACCOUNTS = 'ORDER_ACCOUNTS', +} + +enum ADDRESS { + GET_TYPE = 'GET_TYPE', } enum APP { @@ -130,6 +143,7 @@ export enum EXTERNAL { SW_REINIT = 'SW_REINIT', SET_ICON = 'SET_ICON', GET_PROVIDER_CONFIG = 'GET_PROVIDER_CONFIG', + IS_ENROLLED = 'IS_ENROLLED', } export enum CONTENT { @@ -144,9 +158,12 @@ enum NETWORK { EDIT_NETWORK = 'EDIT_NETWORK', EDIT_NETWORKS_ORDER = 'EDIT_NETWORKS_ORDER', REMOVE_NETWORK = 'REMOVE_NETWORK', + SWITCH_PROVIDER = 'SWITCH_PROVIDER', GET_SPECIFIC_CHAIN_DETAILS = 'GET_SPECIFIC_CHAIN_DETAILS', GET_DEFAULT_RPC = 'GET_DEFAULT_RPC', + GET_RPCS = 'GET_RPCS', GET_RPC_CHAIN_ID = 'GET_RPC_CHAIN_ID', + IS_RPC_VALID = 'IS_RPC_VALID', SEARCH_CHAINS = 'SEARCH_CHAINS', } @@ -186,9 +203,12 @@ enum TRANSACTION { GET_SEND_TRANSACTION_RESULT = 'GET_SEND_TRANSACTION_RESULT', CALCULATE_SEND_TRANSACTION_GAS_LIMIT = 'CALCULATE_SEND_TRANSACTION_GAS_LIMIT', CALCULATE_APPROVE_TRANSACTION_GAS_LIMIT = 'CALCULATE_APPROVE_TRANSACTION_GAS_LIMIT', + CALCULATE_SWAP_TRANSACTION_GAS_LIMIT = 'CALCULATE_SWAP_TRANSACTION_GAS_LIMIT', CONFIRM = 'CONFIRM_TRANSACTION', REJECT = 'REJECT_TRANSACTION', + UPDATE_STATUS = 'UPDATE_STATUS', GET_LATEST_GAS_PRICE = 'GET_LATEST_GAS_PRICE', + UPDATE_GAS_PRICE = 'UPDATE_GAS_PRICE', FETCH_LATEST_GAS_PRICE = 'FETCH_LATEST_GAS_PRICE', SEND_ETHER = 'SEND_ETHER', CANCEL_TRANSACTION = 'CANCEL_TRANSACTION', @@ -224,6 +244,15 @@ enum WALLET { HARDWARE_SET_HD_PATH = 'HARDWARE_SET_HD_PATH', HARDWARE_IS_LINKED = 'HARDWARE_IS_LINKED', SET_DEFAULT_GAS = 'SET_DEFAULT_GAS', + // qr hardware devices + HARDWARE_QR_SUBMIT_CRYPTO_HD_KEY_OR_ACCOUNT = 'HARDWARE_QR_SUBMIT_CRYPTO_HD_KEY_OR_ACCOUNT', + HARDWARE_QR_SUBMIT_SIGNATURE = 'HARDWARE_QR_SUBMIT_SIGNATURE', + HARDWARE_QR_CANCEL_SIGN_REQUEST = 'HARDWARE_QR_CANCEL_SIGN_REQUEST', + //hotkeys + SET_HOTKEYS_ENABLED = 'SET_HOTKEYS_ENABLED', + //onramp + GET_ONRAMP_CURRENCIES = 'GET_ONRAMP_CURRENCIES', + SET_HIDESMALLBALANCES = 'SET_HIDESMALLBALANCES', } enum TOKEN { @@ -257,8 +286,16 @@ enum FILTERS { SET_ACCOUNT_FILTERS = 'SET_ACCOUNT_FILTERS', } +export enum ProviderType { + DEFAULT = 'DEFAULT', + BACKUP = 'BACKUP', + CUSTOM = 'CUSTOM', + CURRENT = 'CURRENT', +} + export const Messages = { ACCOUNT, + ADDRESS, APP, BACKGROUND, CONTENT, @@ -283,6 +320,7 @@ export const Messages = { // [MessageType]: [RequestType, ResponseType, SubscriptionMessageType?] export interface RequestSignatures { [Messages.BROWSER.GET_WINDOW_ID]: [undefined, string]; + [Messages.ADDRESS.GET_TYPE]: [string, AddressType]; [Messages.ACCOUNT.CREATE]: [RequestAccountCreate, AccountInfo]; [Messages.ACCOUNT.EXPORT_JSON]: [RequestAccountExportJson, string]; [Messages.ACCOUNT.EXPORT_PRIVATE_KEY]: [RequestAccountExportPK, string]; @@ -303,6 +341,9 @@ export interface RequestSignatures { number, BigNumber | undefined ]; + [Messages.ACCOUNT.EDIT_ACCOUNT_TOKENS_ORDER]: [RequestTokensOrder, void]; + [Messages.ACCOUNT.SET_ACCOUNT_SORT_VALUE]: [string, void]; + [Messages.ACCOUNT.ORDER_ACCOUNTS]: [RequestOrderAccounts, void]; [Messages.APP.GET_IDLE_TIMEOUT]: [undefined, number]; [Messages.APP.SET_IDLE_TIMEOUT]: [RequestSetIdleTimeout, void]; [Messages.APP.SET_LAST_USER_ACTIVE_TIME]: [undefined, void]; @@ -326,7 +367,7 @@ export interface RequestSignatures { boolean ]; [Messages.EXCHANGE.APPROVE]: [RequestApproveExchange, boolean]; - [Messages.EXCHANGE.GET_QUOTE]: [RequestGetExchangeQuote, SwapQuote]; + [Messages.EXCHANGE.GET_QUOTE]: [RequestGetExchangeQuote, SwapQuoteResponse]; [Messages.EXCHANGE.GET_EXCHANGE]: [RequestGetExchange, SwapParameters]; [Messages.EXCHANGE.GET_SPENDER]: [RequestGetExchangeSpender, string]; [Messages.EXCHANGE.EXECUTE]: [RequestExecuteExchange, string]; @@ -338,6 +379,7 @@ export interface RequestSignatures { undefined, RemoteConfigsControllerState['provider'] ]; + [Messages.EXTERNAL.IS_ENROLLED]: [RequestIsEnrolled, boolean]; [Messages.BRIDGE.GET_BRIDGE_TOKENS]: [RequestGetBridgeTokens, IToken[]]; [Messages.BRIDGE.APPROVE_BRIDGE_ALLOWANCE]: [ @@ -367,6 +409,7 @@ export interface RequestSignatures { [Messages.NETWORK.EDIT_NETWORK]: [RequestEditNetwork, void]; [Messages.NETWORK.EDIT_NETWORKS_ORDER]: [RequestEditNetworksOrder, void]; [Messages.NETWORK.REMOVE_NETWORK]: [RequestRemoveNetwork, void]; + [Messages.NETWORK.SWITCH_PROVIDER]: [RequestSwitchProvider, void]; [Messages.NETWORK.GET_SPECIFIC_CHAIN_DETAILS]: [ RequestGetChainData, ChainListItem @@ -402,11 +445,16 @@ export interface RequestSignatures { [Messages.UD.RESOLVE_NAME]: [RequestUDResolve, string | null]; [Messages.TRANSACTION.CONFIRM]: [RequestConfirmTransaction, string]; [Messages.TRANSACTION.REJECT]: [RequestRejectTransaction, boolean]; + [Messages.TRANSACTION.UPDATE_STATUS]: [ + RequestUpdateTransactionStatus, + boolean + ]; [Messages.TRANSACTION.REJECT_REPLACEMENT_TRANSACTION]: [ RequestRejectTransaction, boolean ]; [Messages.TRANSACTION.GET_LATEST_GAS_PRICE]: [undefined, BigNumber]; + [Messages.TRANSACTION.UPDATE_GAS_PRICE]: [undefined, undefined]; [Messages.TRANSACTION.FETCH_LATEST_GAS_PRICE]: [number, GasPriceData]; [Messages.TRANSACTION.SEND_ETHER]: [RequestSendEther, string]; [Messages.TRANSACTION.ADD_NEW_SEND_TRANSACTION]: [ @@ -437,6 +485,10 @@ export interface RequestSignatures { RequestCalculateSendTransactionGasLimit, TransactionGasEstimation ]; + [Messages.TRANSACTION.CALCULATE_SWAP_TRANSACTION_GAS_LIMIT]: [ + RequestCalculateSwapTransactionGasLimit, + TransactionGasEstimation + ]; [Messages.TRANSACTION.CANCEL_TRANSACTION]: [RequestCancelTransaction, void]; [Messages.TRANSACTION.SPEED_UP_TRANSACTION]: [ RequestSpeedUpTransaction, @@ -542,6 +594,24 @@ export interface RequestSignatures { RequestGenerateOnDemandReleaseNotes, ReleaseNote[] ]; + [Messages.WALLET.HARDWARE_QR_SUBMIT_CRYPTO_HD_KEY_OR_ACCOUNT]: [ + SubmitQRHardwareCryptoHDKeyOrAccountMessage, + boolean + ]; + [Messages.WALLET.HARDWARE_QR_SUBMIT_SIGNATURE]: [ + SubmitQRHardwareSignatureMessage, + boolean + ]; + [Messages.WALLET.HARDWARE_QR_CANCEL_SIGN_REQUEST]: [ + CancelQRHardwareSignRequestMessage, + boolean + ]; + [Messages.WALLET.SET_HOTKEYS_ENABLED]: [RequestSetHotkeys, void]; + [Messages.WALLET.GET_ONRAMP_CURRENCIES]: [void, GetOnRampCurrencies]; + [Messages.WALLET.SET_HIDESMALLBALANCES]: [ + RequestSetHideSmallBalances, + void + ]; } export type MessageTypes = keyof RequestSignatures; @@ -550,6 +620,13 @@ export type RequestTypes = { [MessageType in keyof RequestSignatures]: RequestSignatures[MessageType][0]; }; +export enum AddressType { + NORMAL = 'NORMAL', + SMART_CONTRACT = 'SMART_CONTRACT', + ERC20 = 'ERC20', + NULL = 'NULL', +} + export interface RequestSetUserOnline { networkStatus: boolean; } @@ -625,6 +702,10 @@ export interface RequestRejectDappRequest { export interface RequestReconnectDevice { address: string; } + +export interface RequestIsEnrolled { + campaignId: string; +} export interface RequestCheckExchangeAllowance { account: string; amount: BigNumber; @@ -643,12 +724,12 @@ export interface RequestApproveExchange { export interface RequestGetExchangeQuote { exchangeType: ExchangeType; - quoteParams: OneInchSwapQuoteParams; + quoteParams: SwapQuoteParams; } export interface RequestGetExchange { exchangeType: ExchangeType; - exchangeParams: OneInchSwapRequestParams; + exchangeParams: SwapRequestParams; } export interface RequestGetExchangeSpender { @@ -711,6 +792,8 @@ export interface RequestAddNetwork { currencySymbol: string; blockExplorerUrl: string; test: boolean; + // Flag to indicate if we should switch to the added/edited network after saving changes. + switchToNetwork: boolean; } export interface RequestEditNetwork { @@ -736,6 +819,12 @@ export interface RequestRemoveNetwork { chainId: number; } +export interface RequestSwitchProvider { + chainId: number; + providerType: ProviderType; + customRpcUrl?: string; +} + export interface RequestGetChainData { chainId: number; } @@ -924,6 +1013,10 @@ export interface RequestCalculateApproveTransactionGasLimit { amount: BigNumber | 'UNLIMITED'; } +export interface RequestCalculateSwapTransactionGasLimit { + tx: SwapTxMeta; +} + export interface RequestCalculateSendTransactionGasLimit { address: string; to: string; @@ -992,6 +1085,11 @@ export interface RequestRejectTransaction { transactionId: string; } +export interface RequestUpdateTransactionStatus { + transactionId: string; + status: TransactionStatus; +} + export interface RequestAddressBookClear {} export interface RequestAddressBookDelete { @@ -1097,8 +1195,27 @@ export interface WindowTransportResponseMessage origin: Origin; } +export interface URParameter { + type: string; + cbor: string; +} +export interface SubmitQRHardwareCryptoHDKeyOrAccountMessage { + ur: URParameter; +} + +export interface SubmitQRHardwareSignatureMessage { + requestId: string; + ur: URParameter; +} +export interface CancelQRHardwareSignRequestMessage {} + export interface DismissMessage {} +export interface GetQRHardwareETHSignRequestMessage { + ethTx: TypedTransaction; + _fromAddress: string; +} + export enum Origin { BACKGROUND = 'BLANK_BACKGROUND', EXTENSION = 'BLANK_EXTENSION', @@ -1107,7 +1224,7 @@ export enum Origin { } export interface ExtensionInstances { - [id: string]: { port: chrome.runtime.Port }; + [id: string]: { port: browser.Runtime.Port }; } export interface ProviderInstances { @@ -1115,7 +1232,7 @@ export interface ProviderInstances { } export interface ProviderInstance { - port: chrome.runtime.Port; + port: browser.Runtime.Port; tabId: number; windowId: number; origin: string; @@ -1138,3 +1255,19 @@ export type Handlers = Record; export enum BackgroundActions { CLOSE_WINDOW = 'CLOSE_WINDOW', } + +export interface RequestSetHotkeys { + enabled: boolean; +} + +export interface RequestTokensOrder { + [tokenAddress: string]: number; +} + +export interface RequestOrderAccounts { + accountsInfo: AccountInfo[]; +} + +export interface RequestSetHideSmallBalances { + enabled: boolean; +} diff --git a/packages/background/src/utils/types/ethereum.ts b/packages/background/src/utils/types/ethereum.ts index 31ac38a12..90e3c6d64 100644 --- a/packages/background/src/utils/types/ethereum.ts +++ b/packages/background/src/utils/types/ethereum.ts @@ -101,6 +101,16 @@ export interface DappRequest { * The time when the request was marked as APPROVED and submitted for signing (only for message signing requests) */ approveTime?: number; + + /** + * Params needed for signing the message using a QR device + */ + qrParams?: QRSignParams; +} + +export interface QRSignParams { + requestId: string; + qrSignRequest: string[]; } // EIP-3085 diff --git a/packages/background/src/utils/types/hardware.ts b/packages/background/src/utils/types/hardware.ts index d58065a0a..4b97d1af6 100644 --- a/packages/background/src/utils/types/hardware.ts +++ b/packages/background/src/utils/types/hardware.ts @@ -4,6 +4,7 @@ export enum Devices { LEDGER = 'LEDGER', TREZOR = 'TREZOR', + KEYSTONE = 'KEYSTONE', } type HDPath = { @@ -26,4 +27,9 @@ export const HDPaths: DevicesHDPath = { { name: 'BIP44 Standard', path: BIP44_PATH, default: true }, /*{ name: 'Trezor Testnets', path: `m/44'/1'/0'/0` },*/ ], + KEYSTONE: [ + { name: 'BIP44 Standard', path: BIP44_PATH }, + { name: 'Ledger Legacy', path: `m/44'/60'/0'` }, + { name: 'Ledger Live', path: `m/44'/60'/0'/0/0`, default: true }, + ], }; diff --git a/packages/background/src/utils/types/lifi.ts b/packages/background/src/utils/types/lifi.ts index b7badb1b3..382c3e28c 100644 --- a/packages/background/src/utils/types/lifi.ts +++ b/packages/background/src/utils/types/lifi.ts @@ -11,6 +11,11 @@ export const BRIDGE_REFERRER_ADDRESS = export const LIFI_NATIVE_ADDRESS = '0x0000000000000000000000000000000000000000'; +export const LIFI_KEY_HEADER = { + 'x-lifi-api-key': + 'dbc88206-1461-4c10-b12b-f44e20e46e52.f225f863-4b69-4f67-be6d-1aa4fb8e0162', +}; + // Base endpoint export const LIFI_BRIDGE_ENDPOINT = 'https://li.quest/v1'; @@ -114,13 +119,7 @@ interface QuoteAction { fromAddress?: string; toAddress: string; } -interface QuoteNotFoundErrorDetails { - errorType: string; - code: string; - action: QuoteAction; - tool: string; - message: string; -} + interface LiFiTransactionRequest { from: string; to: string; @@ -147,9 +146,28 @@ interface Connection { toTokens: LiFiToken[]; } +interface Failed { + overallPath: string; + subpaths: { [key: string]: Subpath[] }; +} + +interface Subpath { + errorType: string; + code: string; + action: QuoteAction; + tool: string; + message: string; +} + +interface FilteredOut { + overallPath: string; + reason: string; +} + export interface LiFiErrorResponse { message: string; - errors: QuoteNotFoundErrorDetails[]; + code: number; + errors: { filteredOut: FilteredOut[]; failed: Failed[] }; } export const lifiTokenToIToken = (token: LiFiToken): IToken => { diff --git a/packages/background/src/utils/userPreferences.ts b/packages/background/src/utils/userPreferences.ts index f75fd80f9..91fc0f320 100644 --- a/packages/background/src/utils/userPreferences.ts +++ b/packages/background/src/utils/userPreferences.ts @@ -3,12 +3,12 @@ import { ReleaseNote, } from '../controllers/PreferencesController'; import { generateReleaseNotesNews } from './releaseNotes'; - +import browser from 'webextension-polyfill'; interface ReleaseNotesFile { releaseNotes: ReleaseNote[]; } export const getReleaseNotes = async (): Promise => { - const url = chrome.runtime.getURL('/release-notes.json'); + const url = browser.runtime.getURL('/release-notes.json'); try { const response = await fetch(url); const parsedFile: ReleaseNotesFile = await response.json(); diff --git a/packages/background/src/utils/window.ts b/packages/background/src/utils/window.ts index 46a57cfb2..92b88d67b 100644 --- a/packages/background/src/utils/window.ts +++ b/packages/background/src/utils/window.ts @@ -1,11 +1,12 @@ import { ONBOARDING_TAB_NAME } from './constants/tab'; +import browser from 'webextension-polyfill'; /** * Checks for runtime error * */ const checkForError = () => { - const error = chrome.runtime.lastError; + const error = browser.runtime.lastError; if (!error) { return undefined; } @@ -19,10 +20,10 @@ export const getCurrentWindowId = (): Promise => { return new Promise((resolve) => { const error = checkForError(); //do not fail on errors - if (!chrome.windows || error) { + if (!browser.windows || error) { return resolve(undefined); } - chrome.windows.getCurrent((window) => { + browser.windows.getCurrent().then((window) => { return resolve(window.id); }); }); @@ -39,7 +40,7 @@ export const closeCurrentWindow = async (): Promise => { return Promise.reject(error); } if (windowId) { - return chrome.windows.remove(windowId); + return browser.windows.remove(windowId); } }; @@ -49,7 +50,7 @@ export const closeCurrentWindow = async (): Promise => { */ export const closeTab = (tabId: number): Promise => { return new Promise((resolve, reject) => { - chrome.tabs.remove(tabId, () => { + browser.tabs.remove(tabId).then(() => { const error = checkForError(); if (error) { reject(error); @@ -65,7 +66,7 @@ export const closeTab = (tabId: number): Promise => { */ export const focusWindow = (windowId: number): Promise => { return new Promise((resolve, reject) => { - chrome.windows.update(windowId, { focused: true }, () => { + browser.windows.update(windowId, { focused: true }).then(() => { const error = checkForError(); if (error) { reject(error); @@ -79,9 +80,9 @@ export const focusWindow = (windowId: number): Promise => { * Returns active windows * */ -export const getActiveTabs = (): Promise => { - return new Promise((resolve, reject) => { - chrome.tabs.query({ active: true }, (tabs) => { +export const getActiveTabs = (): Promise => { + return new Promise((resolve, reject) => { + browser.tabs.query({ active: true }).then((tabs) => { const error = checkForError(); if (error) { reject(error); @@ -95,9 +96,9 @@ export const getActiveTabs = (): Promise => { * Returns all open windows * */ -export const getAllWindows = (): Promise => { - return new Promise((resolve, reject) => { - chrome.windows.getAll((windows) => { +export const getAllWindows = (): Promise => { + return new Promise((resolve, reject) => { + browser.windows.getAll().then((windows) => { const error = checkForError(); if (error) { reject(error); @@ -111,10 +112,10 @@ export const getAllWindows = (): Promise => { * Returns the current selected tab * */ -export const getCurrentTab = (): Promise => { - return new Promise((resolve, reject) => { - chrome.tabs && - chrome.tabs.getCurrent((tab) => { +export const getCurrentTab = (): Promise => { + return new Promise((resolve, reject) => { + browser.tabs && + browser.tabs.getCurrent().then((tab) => { const error = checkForError(); if (error) { reject(error); @@ -128,9 +129,9 @@ export const getCurrentTab = (): Promise => { * Returns the last focused window * */ -export const getLastFocusedWindow = (): Promise => { - return new Promise((resolve, reject) => { - chrome.windows.getLastFocused((windowObject) => { +export const getLastFocusedWindow = (): Promise => { + return new Promise((resolve, reject) => { + browser.windows.getLastFocused().then((windowObject) => { const error = checkForError(); if (error) { reject(error); @@ -145,7 +146,7 @@ export const getLastFocusedWindow = (): Promise => { * */ export const getVersion = (): string => { - return chrome.runtime.getManifest().version; + return browser.runtime.getManifest().version; }; /** @@ -156,7 +157,7 @@ export const openExtensionInBrowser = ( route: string | null = null, queryString = null ): void => { - let extensionURL = chrome.runtime.getURL(ONBOARDING_TAB_NAME); + let extensionURL = browser.runtime.getURL(ONBOARDING_TAB_NAME); if (queryString) { extensionURL += `?${queryString}`; @@ -175,10 +176,10 @@ export const openExtensionInBrowser = ( * @param options settings for the new tab */ export const openTab = ( - options: chrome.tabs.CreateProperties -): Promise => { - return new Promise((resolve, reject) => { - chrome.tabs.create(options, (newTab) => { + options: browser.Tabs.CreateCreatePropertiesType +): Promise => { + return new Promise((resolve, reject) => { + browser.tabs.create(options).then((newTab) => { const error = checkForError(); if (error) { reject(error); @@ -193,17 +194,19 @@ export const openTab = ( * */ export const openWindow = ( - options: chrome.windows.CreateData -): Promise => { - return new Promise((resolve, reject) => { - chrome.windows.create(options, (newWindow) => { - const error = checkForError(); - if (error) { - return reject(error); - } - return resolve(newWindow); - }); - }); + options: browser.Windows.CreateCreateDataType +): Promise => { + return new Promise( + (resolve, reject) => { + browser.windows.create(options).then((newWindow) => { + const error = checkForError(); + if (error) { + return reject(error); + } + return resolve(newWindow); + }); + } + ); }; /** @@ -212,17 +215,19 @@ export const openWindow = ( */ export const updateWindow = ( windowId: number, - updateInfo: chrome.windows.UpdateInfo -): Promise => { - return new Promise((resolve, reject) => { - chrome.windows.update(windowId, updateInfo, (newWindow) => { - const error = checkForError(); - if (error) { - return reject(error); - } - return resolve(newWindow); - }); - }); + updateInfo: browser.Windows.UpdateUpdateInfoType +): Promise => { + return new Promise( + (resolve, reject) => { + browser.windows.update(windowId, updateInfo).then((newWindow) => { + const error = checkForError(); + if (error) { + return reject(error); + } + return resolve(newWindow); + }); + } + ); }; /** @@ -231,9 +236,9 @@ export const updateWindow = ( */ export const switchToTab = ( tabId: number -): Promise => { - return new Promise((resolve, reject) => { - chrome.tabs.update(tabId, { highlighted: true }, (tab) => { +): Promise => { + return new Promise((resolve, reject) => { + browser.tabs.update(tabId, { highlighted: true }).then((tab) => { const error = checkForError(); if (error) { reject(error); @@ -247,9 +252,9 @@ export const switchToTab = ( * Returns the platform info * */ -export const getPlatformInfo = (): Promise => { - return new Promise((resolve, reject) => { - chrome.runtime.getPlatformInfo((info) => { +export const getPlatformInfo = (): Promise => { + return new Promise((resolve, reject) => { + browser.runtime.getPlatformInfo().then((info) => { const error = checkForError(); if (error) { reject(error); diff --git a/packages/background/test/controllers/AccountTrackerController.test.ts b/packages/background/test/controllers/AccountTrackerController.test.ts index f8b2b5ecc..1961f78d9 100644 --- a/packages/background/test/controllers/AccountTrackerController.test.ts +++ b/packages/background/test/controllers/AccountTrackerController.test.ts @@ -36,6 +36,8 @@ import { TypedTransaction } from '@ethereumjs/tx'; import { mockedPermissionsController } from 'test/mocks/mock-permissions'; import { GasPricesController } from '@block-wallet/background/controllers/GasPricesController'; import PermissionsController from '@block-wallet/background/controllers/PermissionsController'; +import { mockKeyringController } from 'test/mocks/mock-keyring-controller'; +import { Devices } from '@block-wallet/background/utils/types/hardware'; describe('AccountTracker controller implementation', function () { const accounts = { @@ -106,11 +108,12 @@ describe('AccountTracker controller implementation', function () { gasPricesController, tokenController, blockUpdatesController, + mockKeyringController, { transactions: [], txSignTimeout: 0, }, - async (ethTx: TypedTransaction) => { + async (_: string, ethTx: TypedTransaction) => { const privateKey = Buffer.from(accounts.goerli[0].key, 'hex'); return Promise.resolve(ethTx.sign(privateKey)); }, @@ -290,7 +293,7 @@ describe('AccountTracker controller implementation', function () { ).equal(BigNumber.from('0x00')._hex); }); - it('An account with eth balance', async () => { + it.skip('An account with eth balance', async () => { const accountAddress = '0x25f3f89bc136975c10a1afe9ad70695a4f451ac4'; accountTrackerController.store.updateState({ accounts: { @@ -326,7 +329,7 @@ describe('AccountTracker controller implementation', function () { ).not.equal(BigNumber.from('0x00')._hex); }); - it('An account with eth balance without specifying the account', async () => { + it.skip('An account with eth balance without specifying the account', async () => { const accountAddress = '0x25f3f89bc136975c10a1afe9ad70695a4f451ac4'; accountTrackerController.store.updateState({ accounts: { @@ -395,7 +398,7 @@ describe('AccountTracker controller implementation', function () { expect(accounts[accountAddress].balances[5].tokens).to.be.empty; }); - it('A simple token balance check with balance', async () => { + it.skip('A simple token balance check with balance', async () => { const accountAddress = '0x281ae730d284bDA68F4e9Ac747319c8eDC7dF3B1'; const assetAddress = '0xdc31Ee1784292379Fbb2964b3B9C4124D8F89C60'; accountTrackerController.store.updateState({ @@ -434,7 +437,7 @@ describe('AccountTracker controller implementation', function () { ).to.be.not.null; }); - it('A simple token balance check without balance but with manually added tokens', async () => { + it.skip('A simple token balance check without balance but with manually added tokens', async () => { sinon.stub(TokenController.prototype, 'getUserTokens').returns( new Promise((resolve) => { resolve({ @@ -631,7 +634,7 @@ describe('AccountTracker controller implementation', function () { expect(accounts[accountAddress3].balances[5].tokens).to.be.empty; }); - it('A multiple accounts check with balance', async () => { + it.skip('A multiple accounts check with balance', async () => { const accountAddress1 = '0x281ae730d284bDA68F4e9Ac747319c8eDC7dF3B1'; const accountAddress2 = @@ -688,7 +691,7 @@ describe('AccountTracker controller implementation', function () { .empty; }); - it('A multiple accounts check without token balance but with manually added tokens', async () => { + it.skip('A multiple accounts check without token balance but with manually added tokens', async () => { sinon.stub(TokenController.prototype, 'getUserTokens').returns( new Promise((resolve) => { resolve({ @@ -816,7 +819,7 @@ describe('AccountTracker controller implementation', function () { ).to.be.not.null; }); - it('A multiple accounts check without token balance but with manually added tokens and manually deleted tokens', async () => { + it.skip('A multiple accounts check without token balance but with manually added tokens and manually deleted tokens', async () => { sinon.stub(TokenController.prototype, 'getUserTokens').returns( new Promise((resolve) => { resolve({ @@ -953,7 +956,7 @@ describe('AccountTracker controller implementation', function () { ).to.be.undefined; }); - it('A multiple accounts check with balance and without balance', async () => { + it.skip('A multiple accounts check with balance and without balance', async () => { const accountAddress1 = '0x281ae730d284bDA68F4e9Ac747319c8eDC7dF3B1'; const accountAddress2 = @@ -1601,4 +1604,27 @@ describe('AccountTracker controller implementation', function () { } as Accounts); }); }); + + it('getAccountTypeFromDevice', () => { + let device = accountTrackerController.getAccountTypeFromDevice( + Devices.KEYSTONE + ); + expect(device).equal(AccountType.KEYSTONE); + + device = accountTrackerController.getAccountTypeFromDevice( + Devices.LEDGER + ); + expect(device).equal(AccountType.LEDGER); + + device = accountTrackerController.getAccountTypeFromDevice( + Devices.TREZOR + ); + expect(device).equal(AccountType.TREZOR); + + try { + accountTrackerController.getAccountTypeFromDevice(Devices.TREZOR); + } catch (error) { + expect(error).equal(new Error('Invalid device')); + } + }); }); diff --git a/packages/background/test/controllers/AddressBookController.test.ts b/packages/background/test/controllers/AddressBookController.test.ts index 17c0ca8d5..5aa28d803 100644 --- a/packages/background/test/controllers/AddressBookController.test.ts +++ b/packages/background/test/controllers/AddressBookController.test.ts @@ -104,11 +104,12 @@ describe('Address book controller implementation', function () { gasPricesController, tokenController, blockUpdatesController, + mockKeyringController, { transactions: [], txSignTimeout: 0, }, - async (ethTx: TypedTransaction) => { + async (_: string, ethTx: TypedTransaction) => { const privateKey = Buffer.from(accounts.goerli[0].key, 'hex'); return Promise.resolve(ethTx.sign(privateKey)); }, diff --git a/packages/background/test/controllers/AppStateController.test.ts b/packages/background/test/controllers/AppStateController.test.ts index 657f10f8c..a8ceca2bd 100644 --- a/packages/background/test/controllers/AppStateController.test.ts +++ b/packages/background/test/controllers/AppStateController.test.ts @@ -16,12 +16,19 @@ import { } from '@block-wallet/background/controllers/erc-20/TokenController'; import { TokenOperationsController } from '@block-wallet/background/controllers/erc-20/transactions/TokenOperationsController'; import { sleep } from '@block-wallet/background/utils/sleep'; +import * as ManifestUtils from '@block-wallet/background/utils/manifest'; +import sinon from 'sinon'; + + + describe('AppState Controller', function () { let appStateController: AppStateController; const defaultIdleTimeout = 5; this.beforeAll(function () { + sinon.stub(ManifestUtils, 'isManifestV3').returns(false) + const networkController = getNetworkControllerInstance(); const preferencesController = mockPreferencesController; const permissionsController = mockedPermissionsController; @@ -78,11 +85,12 @@ describe('AppState Controller', function () { ), tokenController, blockUpdatesController, + mockKeyringController, { transactions: [], txSignTimeout: 0, }, - async (ethTx: TypedTransaction) => { + async (_: string, ethTx: TypedTransaction) => { const privateKey = Buffer.from( accounts.goerli[0].key, 'hex' @@ -92,6 +100,11 @@ describe('AppState Controller', function () { { txHistoryLimit: 40 } ) ); + + }); + + this.afterAll(function () { + sinon.restore(); }); it('should update the last user active time', async function () { diff --git a/packages/background/test/controllers/BlankProviderController.test.ts b/packages/background/test/controllers/BlankProviderController.test.ts index f4cbbdec5..17fde419e 100644 --- a/packages/background/test/controllers/BlankProviderController.test.ts +++ b/packages/background/test/controllers/BlankProviderController.test.ts @@ -42,13 +42,12 @@ import BlockFetchController from '@block-wallet/background/controllers/block-upd import { ExternalEventSubscription } from '@block-wallet/background/utils/types/communication'; import * as random from '@block-wallet/background/utils/randomBytes'; import { TransactionWatcherController } from '@block-wallet/background/controllers/TransactionWatcherController'; -import { PrivacyAsyncController } from '@block-wallet/background/controllers/privacy/PrivacyAsyncController'; +import * as ManifestUtils from '@block-wallet/background/utils/manifest'; const UNI_ORIGIN = 'https://app.uniswap.org'; const TX_HASH = '0x3979f7ae255171ae6c6fd1c625219b45e2da7e52e6401028c29f0f27581af601'; const TEXT_FOR_HASH = 'HASH ME'; - describe('Blank Provider Controller', function () { const defaultIdleTimeout = 500000; const portId = '7e24f69d-c740-4eb3-9c6e-4d47df491005'; @@ -66,7 +65,7 @@ describe('Blank Provider Controller', function () { }; providerInstances[portId] = { - port: chrome.runtime.connect(), + port: chrome.runtime.connect() as any, tabId: 420, windowId: 404, origin: UNI_ORIGIN, @@ -154,6 +153,8 @@ describe('Blank Provider Controller', function () { let transactionWatcherController: TransactionWatcherController; beforeEach(function () { + sinon.stub(ManifestUtils, 'isManifestV3').returns(false); + // Instantiate objects networkController = getNetworkControllerInstance(); @@ -209,11 +210,12 @@ describe('Blank Provider Controller', function () { gasPricesController, tokenController, blockUpdatesController, + mockKeyringController, { transactions: [], txSignTimeout: 0, }, - async (ethTx: TypedTransaction) => { + async (_: string, ethTx: TypedTransaction) => { const privateKey = Buffer.from(accounts.goerli[0].key, 'hex'); return Promise.resolve(ethTx.sign(privateKey)); }, @@ -259,7 +261,7 @@ describe('Blank Provider Controller', function () { ); }); - afterEach(function () { + this.afterEach(function () { sinon.restore(); }); diff --git a/packages/background/test/controllers/BridgeController.test.ts b/packages/background/test/controllers/BridgeController.test.ts index 17ae0ac93..54896aaef 100644 --- a/packages/background/test/controllers/BridgeController.test.ts +++ b/packages/background/test/controllers/BridgeController.test.ts @@ -35,7 +35,10 @@ import { ContractSignatureParser } from '@block-wallet/background/controllers/tr import { expectThrowsAsync } from 'test/utils/expectThrowsAsync.test'; import { BigNumber } from '@ethersproject/bignumber'; import { getChainListItem } from '@block-wallet/background/utils/chainlist'; -import { BRIDGE_REFERRER_ADDRESS } from '@block-wallet/background/utils/types/lifi'; +import { + BRIDGE_REFERRER_ADDRESS, + LiFiErrorResponse, +} from '@block-wallet/background/utils/types/lifi'; import MOCKS from '../mocks/mock-bridge-operations'; import TokenAllowanceController from '@block-wallet/background/controllers/erc-20/transactions/TokenAllowanceController'; import { sleep } from '@block-wallet/background/utils/sleep'; @@ -169,11 +172,12 @@ describe('Bridge Controller', () => { gasPricesController, tokenController, blockUpdatesController, + mockKeyringController, { transactions: [], txSignTimeout: 0, }, - async (ethTx: TypedTransaction) => { + async (_: string, ethTx: TypedTransaction) => { const privateKey = Buffer.from(accounts.goerli[0].key, 'hex'); return Promise.resolve(ethTx.sign(privateKey)); }, @@ -472,49 +476,67 @@ describe('Bridge Controller', () => { }); it('Should return QuoteNotFound error if there is no quote', async () => { quoteSandbox.restore(); - const errorMessage = 'quote not found'; - const errors = [ - { - errorType: 'NOT_FOUND', - code: '123', - action: { - fromChainId: 123, - toChainId: 123, - fromToken: { - address: - '0x41A3Dba3D677E573636BA691a70ff2D606c29666', - decimals: 18, - logo: 'logo1', - chainId: 1, - name: 'eth', - symbol: 'GETH', - type: '', - coinKey: 'coin', - priceUSD: 1, - logoURI: 'logo.png', + const errorMessage = + 'No available quotes for the requested transfer'; + const errors: LiFiErrorResponse = { + message: + 'No available quotes for the requested transfer', + code: 1002, + errors: { + filteredOut: [ + { + overallPath: '324:ETH-cbridge-42161:ETH', + reason: 'Transferred amount (1000000000000000) out of acceptable range (min: 5000000000000001, max: 90000000000000000000)', }, - toToken: { - address: - '0x41A3Dba3D677E573636BA691a70ff2D606c29666', - decimals: 18, - logo: 'logo1', - chainId: 1, - name: 'eth', - symbol: 'GETH', - type: '', - coinKey: 'coin', - priceUSD: 2, - logoURI: 'logo.png', + ], + failed: [ + { + overallPath: + '324:ETH~324:ETH-324:ETH-across-42161:ETH', + subpaths: { + '324:ETH~324:ETH': [ + { + errorType: 'NOT_FOUND', + code: '123', + action: { + fromChainId: 123, + toChainId: 123, + fromToken: { + address: + '0x41A3Dba3D677E573636BA691a70ff2D606c29666', + decimals: 18, + chainId: 1, + name: 'eth', + symbol: 'GETH', + coinKey: 'coin', + priceUSD: 1, + logoURI: 'logo.png', + }, + toToken: { + address: + '0x41A3Dba3D677E573636BA691a70ff2D606c29666', + decimals: 18, + chainId: 1, + name: 'eth', + symbol: 'GETH', + coinKey: 'coin', + priceUSD: 2, + logoURI: 'logo.png', + }, + fromAmount: 'asd', + slippage: 123, + toAddress: + '0x41A3Dba3D677E573636BA691a70ff2D606c29666', + }, + tool: 'tool', + message: 'message', + }, + ], + }, }, - fromAmount: 'asd', - slippage: 123, - toAddress: - '0x41A3Dba3D677E573636BA691a70ff2D606c29666', - }, - tool: 'tool', - message: 'message', + ], }, - ]; + }; quoteSandbox .stub(BridgeAPI.LIFI_BRIDGE, 'getQuote') .withArgs({ @@ -529,10 +551,10 @@ describe('Bridge Controller', () => { referrer: BRIDGE_REFERRER_ADDRESS, }) .throwsException( - new QuoteNotFoundError('quote not found', { - errors: errors, - message: errorMessage, - }) + new QuoteNotFoundError( + 'No available quotes for the requested transfer', + errors + ) ); const quoteResponse = (await bridgeController.getQuote( @@ -550,7 +572,6 @@ describe('Bridge Controller', () => { )) as GetBridgeQuoteNotFoundResponse; expect(quoteResponse).not.to.be.undefined; expect(quoteResponse.message).to.equal(errorMessage); - expect(quoteResponse.errors).to.equal(errors); }); it('Should return a valid quote without checking allowance', async () => { sandbox.restore(); diff --git a/packages/background/test/controllers/EnsController.test.ts b/packages/background/test/controllers/EnsController.test.ts index 7c9cbeb9f..6311b71c5 100644 --- a/packages/background/test/controllers/EnsController.test.ts +++ b/packages/background/test/controllers/EnsController.test.ts @@ -16,7 +16,7 @@ describe('ENS Controller', function () { }); }); - it('should resolve ENS name properly', async function () { + it.skip('should resolve ENS name properly', async function () { const resolvedAddress = await ensController.resolveName( blankEnsGoerliName ); @@ -24,7 +24,7 @@ describe('ENS Controller', function () { expect(resolvedAddress).to.be.equal(blankEnsGoerliAddress); }); - it('should lookup address properly', async function () { + it.skip('should lookup address properly', async function () { const resolvedAddress = await ensController.lookupAddress( blankEnsGoerliAddress ); diff --git a/packages/background/test/controllers/GasPricesController.test.ts b/packages/background/test/controllers/GasPricesController.test.ts index c0128b6b3..3e191a9a6 100644 --- a/packages/background/test/controllers/GasPricesController.test.ts +++ b/packages/background/test/controllers/GasPricesController.test.ts @@ -17,7 +17,6 @@ import BlockUpdatesController from '@block-wallet/background/controllers/block-u import BlockFetchController from '@block-wallet/background/controllers/block-updates/BlockFetchController'; import httpClient, { RequestError } from './../../src/utils/http'; -// TODO(REC): FIX US! describe('GasPrices Controller', () => { let gasPricesController: GasPricesController; let networkController: NetworkController; @@ -1035,7 +1034,7 @@ describe('GasPrices Controller', () => { .stub(gasPricesController as any, '_shouldRequestChainService') .returns(true); - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((resolve) => { resolve({ blockNumber: '22332861', @@ -1102,7 +1101,7 @@ describe('GasPrices Controller', () => { .stub(gasPricesController as any, '_shouldRequestChainService') .returns(true); - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((resolve) => { resolve({ blockNumber: '13775611', @@ -1174,7 +1173,7 @@ describe('GasPrices Controller', () => { .stub(gasPricesController as any, '_shouldRequestChainService') .returns(true); - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((_, reject) => { reject(new RequestError('400', 400, {})); }) @@ -1244,7 +1243,7 @@ describe('GasPrices Controller', () => { .stub(gasPricesController as any, '_shouldRequestChainService') .returns(true); - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((_, reject) => { reject(new RequestError('400', 400, {})); }) @@ -1389,7 +1388,7 @@ describe('GasPrices Controller', () => { .stub(gasPricesController as any, '_shouldRequestChainService') .returns(false); - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((_, reject) => { reject(new RequestError('400', 400, {})); }) @@ -1470,7 +1469,7 @@ describe('GasPrices Controller', () => { .stub(gasPricesController as any, '_shouldRequestChainService') .returns(true); - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((_, reject) => { reject(new RequestError('500', 500, {})); }) @@ -1540,7 +1539,7 @@ describe('GasPrices Controller', () => { .stub(gasPricesController as any, '_shouldRequestChainService') .returns(true); - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((_, reject) => { reject(new RequestError('500', 500, {})); }) @@ -1621,7 +1620,7 @@ describe('GasPrices Controller', () => { .stub(gasPricesController as any, '_shouldRequestChainService') .returns(true); - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((_, reject) => { reject(new RequestError('400', 400, {})); }) diff --git a/packages/background/test/controllers/KeyringControllerDerivated.test.ts b/packages/background/test/controllers/KeyringControllerDerivated.test.ts index 57dcc36a2..0d78bf50a 100644 --- a/packages/background/test/controllers/KeyringControllerDerivated.test.ts +++ b/packages/background/test/controllers/KeyringControllerDerivated.test.ts @@ -8,6 +8,8 @@ import { KeyringController, } from '@metamask/eth-keyring-controller'; import mockEncryptor from 'test/mocks/mock-encryptor'; +import { hexToString } from '@block-wallet/background/utils/signature'; +import { bufferToHex } from '@ethereumjs/util'; describe('KeyringControllerDerivated', () => { const MOCK_PASSWORD = 'passoword'; @@ -357,7 +359,9 @@ describe('KeyringControllerDerivated', () => { vault.keyrings[0].type )[0]; const serialized = await primaryKeyring.serialize(); - const seedPhraseToCheck = serialized.mnemonic; + const seedPhraseToCheck = hexToString( + bufferToHex(serialized.mnemonic) + ); expect(seedPhraseToCheck).not.equal(''); expect(seedPhraseToCheck).equal(seedPhrase); diff --git a/packages/background/test/controllers/NetworkController.test.ts b/packages/background/test/controllers/NetworkController.test.ts index 97423e7e7..219bd6448 100644 --- a/packages/background/test/controllers/NetworkController.test.ts +++ b/packages/background/test/controllers/NetworkController.test.ts @@ -60,10 +60,10 @@ describe('Network controller', function () { expect( (await networkController.getLatestBlock()).number ).to.be.greaterThan(12556240); - }).timeout(30000); + }).timeout(100000); describe('EIP1559 compatibility', async () => { - it('There is a value for the chain', async () => { + it.skip('There is a value for the chain', async () => { networkController.store.updateState({ isEIP1559Compatible: { 5: true, @@ -84,6 +84,57 @@ describe('Network controller', function () { await networkController.getEIP1559Compatibility(5, false); expect(shouldNotBeCompatibleWithEIP155).equal(false); }); + it('Catched by the NO_EIP_1559_NETWORKS list', async () => { + networkController.store.updateState({ + isEIP1559Compatible: {}, + }); + + const shouldBeCompatibleWithEIP155 = + await networkController.getEIP1559Compatibility(324, false); + expect(shouldBeCompatibleWithEIP155).equal(false); + }); + it.skip('The chain fee service indicates EIP 1559 compatibility', async () => { + networkController.store.updateState({ + isEIP1559Compatible: {}, + }); + + const shouldBeCompatibleWithEIP155 = + await networkController.getEIP1559Compatibility(5, false); + expect(shouldBeCompatibleWithEIP155).equal(true); + }); + it('The chain fee service indicates NO EIP 1559 compatibility', async () => { + const providerStub = sinon.stub( + networkController.getProvider(), + 'getBlock' + ); + const feeHistoryStub = sinon.stub( + networkController.getProvider(), + 'send' + ); + + networkController.store.updateState({ + isEIP1559Compatible: {}, + }); + + providerStub.onFirstCall().returns( + new Promise((resolve) => { + resolve({ baseFeePerGas: BigNumber.from('1') } as Block); + }) + ); + feeHistoryStub.onFirstCall().returns( + new Promise((_, err) => { + err(new Error('some error')); + }) + ); + + networkController.store.updateState({ + isEIP1559Compatible: {}, + }); + + const shouldBeCompatibleWithEIP155 = + await networkController.getEIP1559Compatibility(1101, false); + expect(shouldBeCompatibleWithEIP155).equal(false); + }); it('There is not a value for the chain', async () => { const providerStub = sinon.stub( networkController.getProvider(), @@ -116,7 +167,7 @@ describe('Network controller', function () { ); const shouldBeCompatibleWithEIP155 = - await networkController.getEIP1559Compatibility(5, false); + await networkController.getEIP1559Compatibility(555, false); expect(shouldBeCompatibleWithEIP155).equal(true); networkController.store.updateState({ @@ -124,7 +175,7 @@ describe('Network controller', function () { }); const shouldNotBeCompatibleWithEIP155 = - await networkController.getEIP1559Compatibility(5, false); + await networkController.getEIP1559Compatibility(555, false); expect(shouldNotBeCompatibleWithEIP155).equal(false); }); it('eth_feeHistory is not available', async () => { @@ -153,7 +204,7 @@ describe('Network controller', function () { ); const shouldBeNotCompatibleWithEIP155 = - await networkController.getEIP1559Compatibility(5, false); + await networkController.getEIP1559Compatibility(555, false); expect(shouldBeNotCompatibleWithEIP155).equal(false); }); it('eth_feeHistory is available', async () => { @@ -231,7 +282,7 @@ describe('Network controller', function () { ); const shouldNotBeCompatibleWithEIP155 = - await networkController.getEIP1559Compatibility(5, true); + await networkController.getEIP1559Compatibility(555, true); expect(shouldNotBeCompatibleWithEIP155).equal(false); }); }); diff --git a/packages/background/test/controllers/PermissionsController.test.ts b/packages/background/test/controllers/PermissionsController.test.ts index 46246958e..c17d07071 100644 --- a/packages/background/test/controllers/PermissionsController.test.ts +++ b/packages/background/test/controllers/PermissionsController.test.ts @@ -3,12 +3,14 @@ import { PreferencesController } from '@block-wallet/background/controllers/Pref import { providerInstances } from '@block-wallet/background/infrastructure/connection'; import { expect } from 'chai'; import { mockPreferencesController } from '../mocks/mock-preferences'; +import browser from 'webextension-polyfill'; describe('Permissions Controller', function () { + chrome.runtime.id = "testid"; const portId = '7e24f69d-c740-4eb3-9c6e-4d47df491005'; providerInstances[portId] = { - port: chrome.runtime.connect(), + port: browser.runtime.connect(), tabId: 420, windowId: 404, origin: 'https://app.uniswap.org', @@ -166,8 +168,8 @@ describe('Permissions Controller', function () { for (let i = 1; i < 4; i++) { permissionsController['_handlers'][`${i}`] = { - reject: (error: Error) => {}, - resolve: (data: any) => {}, + reject: (error: Error) => { }, + resolve: (data: any) => { }, }; } diff --git a/packages/background/test/controllers/SwapController.test.ts b/packages/background/test/controllers/SwapController.test.ts index 3206dc383..2fd1e1903 100644 --- a/packages/background/test/controllers/SwapController.test.ts +++ b/packages/background/test/controllers/SwapController.test.ts @@ -29,10 +29,11 @@ import { TokenController, TokenControllerProps, } from '../../src/controllers/erc-20/TokenController'; -import { BASE_SWAP_FEE } from '../../src/utils/types/1inch'; import httpClient from './../../src/utils/http'; import { TypedTransaction } from '@ethereumjs/tx'; import TokenAllowanceController from '@block-wallet/background/controllers/erc-20/transactions/TokenAllowanceController'; +import { mockKeyringController } from 'test/mocks/mock-keyring-controller'; +import { BASE_SWAP_FEE } from '@block-wallet/background/utils/swaps/1inch'; const BLANK_TOKEN_ADDRESS = '0x41a3dba3d677e573636ba691a70ff2d606c29666'; @@ -107,11 +108,12 @@ describe('Swap Controller', function () { gasPricesController, tokenController, blockUpdatesController, + mockKeyringController, { transactions: [], txSignTimeout: 0, }, - async (ethTx: TypedTransaction) => { + async (_: string, ethTx: TypedTransaction) => { const privateKey = Buffer.from(accounts.goerli[0].key, 'hex'); return Promise.resolve(ethTx.sign(privateKey)); }, @@ -129,7 +131,8 @@ describe('Swap Controller', function () { networkController, transactionController, tokenController, - tokenAllowanceController + tokenAllowanceController, + gasPricesController ); }); @@ -157,17 +160,27 @@ describe('Swap Controller', function () { await swapController.getExchangeQuote( 'Not an exchange type' as ExchangeType, { - fromTokenAddress: - '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - toTokenAddress: - '0x41a3dba3d677e573636ba691a70ff2d606c29666', + fromToken: { + address: + '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + toToken: { + address: + '0x41a3dba3d677e573636ba691a70ff2d606c29666', + decimals: 18, + name: 'Token', + symbol: 'TKN', + }, amount: '10000000000000000', } ); }); expect(error).not.to.be.undefined; - expect(error).to.be.equal('Exchange type not supported'); + expect(error).to.be.equal('Exchange type not supported.'); }); it('Should fail for bad exchange type on swap params', async function () { @@ -175,9 +188,26 @@ describe('Swap Controller', function () { await swapController.getExchangeParameters( 'Not an exchange type' as ExchangeType, { - fromTokenAddress: - '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - toTokenAddress: BLANK_TOKEN_ADDRESS, + fromToken: { + symbol: 'ETH', + name: 'Ethereum', + decimals: 18, + address: + '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + logoURI: + 'https://tokens.1inch.io/0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.png', + tags: ['native'], + }, + toToken: { + symbol: 'ETH', + name: 'Ethereum', + decimals: 18, + address: + '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + logoURI: + 'https://tokens.1inch.io/0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.png', + tags: ['native'], + }, amount: '10000000000000000', fromAddress: accounts.goerli[0].address, slippage: 0.5, @@ -186,7 +216,7 @@ describe('Swap Controller', function () { }); expect(error).not.to.be.undefined; - expect(error).to.be.equal('Exchange type not supported'); + expect(error).to.be.equal('Exchange type not supported.'); }); it('Should fail for bad exchange type on execute exchange', async function () { @@ -244,7 +274,7 @@ describe('Swap Controller', function () { }); expect(error).not.to.be.undefined; - expect(error).to.be.equal('Exchange type not supported'); + expect(error).to.be.equal('Exchange type not supported.'); }); }); @@ -257,7 +287,7 @@ describe('Swap Controller', function () { }) ); - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( mockHttpClientResponse({ address: '0x1111111254fb6c44bac0bed2854e76f90643097d', }) @@ -284,7 +314,7 @@ describe('Swap Controller', function () { }) ); - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( mockHttpClientResponse({ address: '0x1111111254fb6c44bac0bed2854e76f90643097d', }) @@ -312,7 +342,7 @@ describe('Swap Controller', function () { }); it('Should fail to submit an approve transaction', async function () { - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( mockHttpClientResponse({ address: '0x1111111254fb6c44bac0bed2854e76f90643097d', }) @@ -362,7 +392,7 @@ describe('Swap Controller', function () { }); it('Should submit an approve transaction', async function () { - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( mockHttpClientResponse({ address: '0x1111111254fb6c44bac0bed2854e76f90643097d', }) @@ -388,7 +418,7 @@ describe('Swap Controller', function () { }); it('Should fail to get a swap quote', async function () { - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( mockHttpClientResponse({ statusCode: 400, error: 'Bad Request', @@ -408,10 +438,18 @@ describe('Swap Controller', function () { error = await expectThrowsAsync(async () => { await swapController.getExchangeQuote(ExchangeType.SWAP_1INCH, { - fromTokenAddress: - '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - toTokenAddress: - '0x41a3dba3d677e573636ba691a70ff2d606c29666', + fromToken: { + address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + toToken: { + address: '0x41a3dba3d677e573636ba691a70ff2d606c29666', + decimals: 18, + name: 'Token', + symbol: 'TKN', + }, amount: '10000000000000000', }); }); @@ -421,7 +459,7 @@ describe('Swap Controller', function () { }); it('Should get a swap quote', async function () { - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( mockHttpClientResponse({ fromToken: { symbol: 'ETH', @@ -464,9 +502,18 @@ describe('Swap Controller', function () { const res = await swapController.getExchangeQuote( ExchangeType.SWAP_1INCH, { - fromTokenAddress: - '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - toTokenAddress: BLANK_TOKEN_ADDRESS, + fromToken: { + address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + toToken: { + address: BLANK_TOKEN_ADDRESS, + decimals: 18, + name: 'Token', + symbol: 'TKN', + }, amount: '10000000000000000', } ); @@ -481,7 +528,7 @@ describe('Swap Controller', function () { expect(res.fromTokenAmount).to.be.equal('10000000000000000'); expect(res.toTokenAmount).to.be.equal('200000000000000000000'); expect(BigNumber.isBigNumber(res.blockWalletFee)).to.be.true; - expect(res.blockWalletFee.toString()).to.be.equal( + expect(res.blockWalletFee!.toString()).to.be.equal( BigNumber.from(res.fromTokenAmount) .mul(BASE_SWAP_FEE * 10) .div(1000) @@ -492,7 +539,7 @@ describe('Swap Controller', function () { }); it('Should fail to get a swap transaction parameters', async function () { - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( mockHttpClientResponse({ statusCode: 400, error: 'Bad Request', @@ -514,9 +561,26 @@ describe('Swap Controller', function () { await swapController.getExchangeParameters( ExchangeType.SWAP_1INCH, { - fromTokenAddress: - '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - toTokenAddress: BLANK_TOKEN_ADDRESS, + fromToken: { + symbol: 'ETH', + name: 'Ethereum', + decimals: 18, + address: + '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + logoURI: + 'https://tokens.1inch.io/0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.png', + tags: ['native'], + }, + toToken: { + symbol: 'BLANK', + name: 'GoBlank Token', + decimals: 18, + address: + '0x41a3dba3d677e573636ba691a70ff2d606c29666', + logoURI: + 'https://tokens.1inch.io/0xaec7e1f531bb09115103c53ba76829910ec48966.png', + tags: ['tokens'], + }, amount: '10000000000000000', fromAddress: accounts.goerli[0].address, slippage: 0.5, @@ -529,7 +593,7 @@ describe('Swap Controller', function () { }); it('Should get a swap transaction parameters', async function () { - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( mockHttpClientResponse({ fromToken: { symbol: 'ETH', @@ -622,9 +686,24 @@ describe('Swap Controller', function () { const res = await swapController.getExchangeParameters( ExchangeType.SWAP_1INCH, { - fromTokenAddress: - '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - toTokenAddress: BLANK_TOKEN_ADDRESS, + fromToken: { + symbol: 'ETH', + name: 'Ethereum', + decimals: 18, + address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + logoURI: + 'https://tokens.1inch.io/0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.png', + tags: ['native'], + }, + toToken: { + symbol: 'BLANK', + name: 'GoBlank Token', + decimals: 18, + address: '0x41a3dba3d677e573636ba691a70ff2d606c29666', + logoURI: + 'https://tokens.1inch.io/0xaec7e1f531bb09115103c53ba76829910ec48966.png', + tags: ['tokens'], + }, amount: '10000000000000000', fromAddress: accounts.goerli[0].address, slippage: 0.5, @@ -843,7 +922,7 @@ describe('Swap Controller', function () { }); expect(error).not.to.be.undefined; - expect(error).to.be.equal('Error executing 1Inch Swap'); + expect(error).to.be.equal('Error executing Swap'); }); it('Should submit a swap transaction', async function () { diff --git a/packages/background/test/controllers/block-updates/BlockFetchController.test.ts b/packages/background/test/controllers/block-updates/BlockFetchController.test.ts index e9b2c4850..3c4b45f6b 100644 --- a/packages/background/test/controllers/block-updates/BlockFetchController.test.ts +++ b/packages/background/test/controllers/block-updates/BlockFetchController.test.ts @@ -27,7 +27,7 @@ describe('OffChainBlockFetchService', () => { describe('fetchBlockNumber', async () => { it('should return an error if the service is not available', async () => { - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((_, reject) => { reject('service not available'); }) @@ -42,7 +42,7 @@ describe('OffChainBlockFetchService', () => { } }); it('should return an error if the service returns an invalid status code', async () => { - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((_, reject) => { reject(new RequestError('400', 400, {})); }) @@ -57,7 +57,7 @@ describe('OffChainBlockFetchService', () => { } }); it('should return an error if the service returns an invalid payload', async () => { - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((resolve, _) => { resolve({}); }) @@ -73,7 +73,7 @@ describe('OffChainBlockFetchService', () => { }); it('should return an error if the service returns an invalid block number', async () => { sinon - .stub(httpClient, 'get') + .stub(httpClient, 'request') .onFirstCall() .returns( new Promise((resolve, _) => { @@ -108,7 +108,7 @@ describe('OffChainBlockFetchService', () => { } }); it('should return a block number if the service returns ok', async () => { - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((resolve, _) => { resolve({ bn: '50120221117' }); }) @@ -126,7 +126,7 @@ describe('OffChainBlockFetchService', () => { let set = false; let errorCount = 0; - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((resolve, _) => { resolve({ bn: mockBlockNumber.toString() }); }) @@ -189,7 +189,7 @@ describe('OffChainBlockFetchService', () => { let watchedError: Error = new Error('not set'); let atLeastOneErrorDetected = false; - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((_, reject) => { reject(new RequestError('500', 500, {})); }) @@ -245,7 +245,7 @@ describe('OffChainBlockFetchService', () => { let watchedError: Error = new Error('not set'); let atLeastOneErrorDetected = false; - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((resolve, _) => { resolve({ bn: '50120221117' }); }) @@ -286,7 +286,7 @@ describe('OffChainBlockFetchService', () => { expect(_recurrentFetch).not.equal(undefined); expect(_recurrentFetch).not.equal(null); - await wait(700); + await wait(1000); loopActivated = false; await errorsPromise; @@ -297,7 +297,7 @@ describe('OffChainBlockFetchService', () => { let watchedError: Error | null = null; let atLeastOneErrorDetected = false; - const stubGet = sinon.stub(httpClient, 'get'); + const stubGet = sinon.stub(httpClient, 'request'); for (let i = 0; i < 200; i++) { stubGet.onCall(i).returns( new Promise((resolve, _) => { @@ -380,7 +380,7 @@ describe('BlockFetchController', () => { lastBlockOffChainChecked: 10, } as Partial); - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((resolve, _) => { resolve({ bn: '20000' }); }) @@ -405,7 +405,7 @@ describe('BlockFetchController', () => { lastBlockOffChainChecked: 10, } as Partial); - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((resolve, _) => { resolve({ blockNumber: '500' }); }) @@ -430,7 +430,7 @@ describe('BlockFetchController', () => { lastBlockOffChainChecked: 10, } as Partial); - sinon.stub(httpClient, 'get').returns( + sinon.stub(httpClient, 'request').returns( new Promise((_, reject) => { reject(new RequestError('500', 500, {})); }) @@ -471,7 +471,7 @@ describe('BlockFetchController', () => { describe('getBlockNumberCallback', async () => { it('it should switch from off chain to on chain', async () => { sinon - .stub(httpClient, 'get') + .stub(httpClient, 'request') .onFirstCall() .returns( new Promise((resolve, _) => { @@ -533,7 +533,7 @@ describe('BlockFetchController', () => { }); }); it('it should switch from on chain to off chain', async () => { - const stubGet = sinon.stub(httpClient, 'get'); + const stubGet = sinon.stub(httpClient, 'request'); for (let i = 0; i < 500; i++) { stubGet.onCall(i).returns( new Promise((resolve, _) => { diff --git a/packages/background/test/controllers/erc-20/TokenController.test.ts b/packages/background/test/controllers/erc-20/TokenController.test.ts index 8d68e030e..8eedf1b2f 100644 --- a/packages/background/test/controllers/erc-20/TokenController.test.ts +++ b/packages/background/test/controllers/erc-20/TokenController.test.ts @@ -10,7 +10,7 @@ import { ITokens, Token } from '../../../src/controllers/erc-20/Token'; import NetworkController from '../../../src/controllers/NetworkController'; import sinon from 'sinon'; import { TokenOperationsController } from '@block-wallet/background/controllers/erc-20/transactions/TokenOperationsController'; -import { toChecksumAddress } from 'ethereumjs-util'; +import { toChecksumAddress } from '@ethereumjs/util'; import { getNetworkControllerInstance } from '../../mocks/mock-network-instance'; import { PreferencesController } from '@block-wallet/background/controllers/PreferencesController'; import { mockPreferencesController } from '../../mocks/mock-preferences'; diff --git a/packages/background/test/controllers/erc-20/transactions/ApproveTransaction.test.ts b/packages/background/test/controllers/erc-20/transactions/ApproveTransaction.test.ts index ac8499a11..1739b5b29 100644 --- a/packages/background/test/controllers/erc-20/transactions/ApproveTransaction.test.ts +++ b/packages/background/test/controllers/erc-20/transactions/ApproveTransaction.test.ts @@ -30,6 +30,8 @@ import BlockUpdatesController from '@block-wallet/background/controllers/block-u import BlockFetchController from '@block-wallet/background/controllers/block-updates/BlockFetchController'; import { TokenOperationsController } from '@block-wallet/background/controllers/erc-20/transactions/TokenOperationsController'; import { IAccountTokens } from '@block-wallet/background/controllers/erc-20/Token'; +import { mockKeyringController } from 'test/mocks/mock-keyring-controller'; + describe('ApproveTransaction implementation', function () { const daiAddress = '0xdc31Ee1784292379Fbb2964b3B9C4124D8F89C60'; @@ -111,11 +113,12 @@ describe('ApproveTransaction implementation', function () { gasPricesController, tokenController, blockUpdatesController, + mockKeyringController, { transactions: [], txSignTimeout: 0, }, - async (ethTx: TypedTransaction) => { + async (_: string, ethTx: TypedTransaction) => { const privateKey = Buffer.from(accounts.goerli[0].key, 'hex'); return Promise.resolve(ethTx.sign(privateKey)); }, diff --git a/packages/background/test/controllers/erc-20/transactions/Transaction.test.ts b/packages/background/test/controllers/erc-20/transactions/Transaction.test.ts index 6eb759f82..433006b03 100644 --- a/packages/background/test/controllers/erc-20/transactions/Transaction.test.ts +++ b/packages/background/test/controllers/erc-20/transactions/Transaction.test.ts @@ -116,7 +116,7 @@ describe('Transaction', function () { .balanceOf(daiAddress, '') .catch((e) => expect(e).equal(accountParamNotPresentError)); }); - it('Should get balance', async () => { + it.skip('Should get balance', async () => { const balance = await tokenOperationsController.balanceOf( daiAddress, accounts.goerli[0].address @@ -143,7 +143,7 @@ describe('Transaction', function () { .allowance(daiAddress, accounts.goerli[0].address, '') .catch((e) => expect(e).equal(spenderParamNotPresentError)); }); - it('Should get allowance', async () => { + it.skip('Should get allowance', async () => { const allowance = await tokenOperationsController.allowance( daiAddress, accounts.goerli[0].address, diff --git a/packages/background/test/controllers/erc-20/transactions/TransferTransaction.test.ts b/packages/background/test/controllers/erc-20/transactions/TransferTransaction.test.ts index cb060c3df..02e121308 100644 --- a/packages/background/test/controllers/erc-20/transactions/TransferTransaction.test.ts +++ b/packages/background/test/controllers/erc-20/transactions/TransferTransaction.test.ts @@ -26,6 +26,7 @@ import { getNetworkControllerInstance } from '../../../mocks/mock-network-instan import TransactionController from '@block-wallet/background/controllers/transactions/TransactionController'; import BlockUpdatesController from '@block-wallet/background/controllers/block-updates/BlockUpdatesController'; import BlockFetchController from '@block-wallet/background/controllers/block-updates/BlockFetchController'; +import { mockKeyringController } from 'test/mocks/mock-keyring-controller'; describe('TransferTransaction implementation', function () { const daiAddress = '0xdc31Ee1784292379Fbb2964b3B9C4124D8F89C60'; @@ -88,11 +89,12 @@ describe('TransferTransaction implementation', function () { gasPricesController, tokenController, blockUpdatesController, + mockKeyringController, { transactions: [], txSignTimeout: 0, }, - async (ethTx: TypedTransaction) => { + async (_: string, ethTx: TypedTransaction) => { const privateKey = Buffer.from(accounts.goerli[0].key, 'hex'); return Promise.resolve(ethTx.sign(privateKey)); }, diff --git a/packages/background/test/controllers/transactions/ContractSignatureParser.test.ts b/packages/background/test/controllers/transactions/ContractSignatureParser.test.ts index d089ec983..907dbbf81 100644 --- a/packages/background/test/controllers/transactions/ContractSignatureParser.test.ts +++ b/packages/background/test/controllers/transactions/ContractSignatureParser.test.ts @@ -81,7 +81,7 @@ describe('Contract Signature Parser', () => { entries: (_: string) => Promise.resolve(), } as any; - sinon.stub(httpClient, 'get').callsFake(() => + sinon.stub(httpClient, 'request').callsFake(() => Promise.resolve({ count: 1, results: [{ text_signature: 'transfer(address,uint256)' }], @@ -96,7 +96,7 @@ describe('Contract Signature Parser', () => { it('Should try to lookup for a method signature, throw and return undefined', async () => { sinon - .stub(httpClient, 'get') + .stub(httpClient, 'request') .callsFake(() => Promise.reject('Error fetching')); contractSignatureParser['signatureRegistry'] = { diff --git a/packages/background/test/controllers/transactions/TransactionController.test.ts b/packages/background/test/controllers/transactions/TransactionController.test.ts index abe2b03bf..9a9d55256 100644 --- a/packages/background/test/controllers/transactions/TransactionController.test.ts +++ b/packages/background/test/controllers/transactions/TransactionController.test.ts @@ -30,6 +30,8 @@ import BlockUpdatesController from '@block-wallet/background/controllers/block-u import BlockFetchController from '@block-wallet/background/controllers/block-updates/BlockFetchController'; import { TokenController } from '@block-wallet/background/controllers/erc-20/TokenController'; import { TokenOperationsController } from '@block-wallet/background/controllers/erc-20/transactions/TokenOperationsController'; +import { mockKeyringController } from 'test/mocks/mock-keyring-controller'; + // TODO: Test gas override @@ -123,11 +125,12 @@ describe('Transactions Controller', () => { gasPricesController, tokenController, blockUpdatesController, + mockKeyringController, { transactions: [], txSignTimeout: 0, }, - async (ethTx: TypedTransaction) => { + async (_: string, ethTx: TypedTransaction) => { const privateKey = Buffer.from( mockedAccounts.goerli[0].key, 'hex' @@ -157,7 +160,7 @@ describe('Transactions Controller', () => { }), estimateGas: () => BigNumber.from('150000'), getGasPrice: () => BigNumber.from('2000000000'), - on: (event: string, func: Function) => {}, + on: (event: string, func: Function) => { }, getCode: (addresOrName: string) => Promise.resolve('0xabc'), }); sinon.stub(gasPricesController, 'getState').returns({ @@ -182,7 +185,7 @@ describe('Transactions Controller', () => { it('Should fallback the gasLimit to the latest block one', async () => { sinon.stub(networkController, 'getProvider').returns({ ...providerMock, - on: (event: string, func: Function) => {}, + on: (event: string, func: Function) => { }, getGasPrice: () => BigNumber.from('2000000000'), estimateGas: () => { throw new Error('Error estimating'); @@ -213,7 +216,7 @@ describe('Transactions Controller', () => { it('Should fail while trying to estimate gas and return a fallback value', async () => { sinon.stub(networkController, 'getProvider').returns({ ...providerMock, - on: (event: string, func: Function) => {}, + on: (event: string, func: Function) => { }, getGasPrice: () => BigNumber.from('2000000000'), estimateGas: () => { throw new Error('Error estimating'); @@ -252,7 +255,7 @@ describe('Transactions Controller', () => { }), estimateGas: () => BigNumber.from('150000'), getGasPrice: () => BigNumber.from('2000000000'), - on: (event: string, func: Function) => {}, + on: (event: string, func: Function) => { }, getCode: (addresOrName: string) => Promise.resolve('0xabc'), }); sinon.stub(gasPricesController, 'getState').returns({ @@ -277,7 +280,7 @@ describe('Transactions Controller', () => { it('Should return the unmodified estimated gasLimit', async () => { sinon.stub(networkController, 'getProvider').returns({ ...providerMock, - on: (event: string, func: Function) => {}, + on: (event: string, func: Function) => { }, getGasPrice: () => BigNumber.from('2000000000'), estimateGas: () => BigNumber.from('190000'), getBlock: (block: any) => ({ @@ -306,7 +309,7 @@ describe('Transactions Controller', () => { it('Should return the send gas cost', async () => { sinon.stub(networkController, 'getProvider').returns({ ...providerMock, - on: (event: string, func: Function) => {}, + on: (event: string, func: Function) => { }, getGasPrice: () => BigNumber.from('2000000000'), estimateGas: () => BigNumber.from('21000'), getBlock: (block: any) => ({ @@ -339,7 +342,7 @@ describe('Transactions Controller', () => { sinon.stub(networkController, 'getProvider').returns({ ...providerMock, - on: (event: string, func: Function) => {}, + on: (event: string, func: Function) => { }, getGasPrice: () => BigNumber.from('2000000000'), estimateGas: () => { return BigNumber.from('1200000'); @@ -455,11 +458,12 @@ describe('Transactions Controller', () => { gasPricesController, tokenController, blockUpdatesController, + mockKeyringController, { transactions: [], txSignTimeout: 0, }, - async (ethTx: TypedTransaction) => { + async (_: string, ethTx: TypedTransaction) => { const privateKey = Buffer.from( mockedAccounts.goerli[0].key, 'hex' @@ -1126,17 +1130,17 @@ describe('Transactions Controller', () => { return hash === '0x4930060e5e465f32c78cea9d467b8d7e9176653cd0416040c44af404dac53fed' ? Promise.resolve({ - status: 1, - }) + status: 1, + }) : Promise.resolve(null); }, getTransaction: (hash) => { return hash === '0x4930060e5e465f32c78cea9d467b8d7e9176653cd0416040c44af404dac53fed' ? Promise.resolve({ - blockNumber: 1, - timestamp: new Date().getTime() / 1000, - }) + blockNumber: 1, + timestamp: new Date().getTime() / 1000, + }) : Promise.resolve(null); }, }); @@ -1240,17 +1244,17 @@ describe('Transactions Controller', () => { return hash === '0x4930060e5e465f32c78cea9d467b8d7e9176653cd0416040c44af404dac53fed' ? Promise.resolve({ - status: 1, - }) + status: 1, + }) : Promise.resolve(null); }, getTransaction: (hash) => { return hash === '0x4930060e5e465f32c78cea9d467b8d7e9176653cd0416040c44af404dac53fed' ? Promise.resolve({ - blockNumber: 1, - timestamp: new Date().getTime() / 1000, - }) + blockNumber: 1, + timestamp: new Date().getTime() / 1000, + }) : Promise.resolve(null); }, }); @@ -1306,6 +1310,10 @@ describe('Transactions Controller', () => { }) ); + sinon.stub(networkController, 'getEIP1559Compatibility').returns( + Promise.resolve(true) + ) + const { transactionMeta } = await transactionController.addTransaction({ transaction: { @@ -1776,11 +1784,12 @@ describe('Transactions Controller', () => { gasPricesController, tokenController, blockUpdatesController, + mockKeyringController, { transactions: [], txSignTimeout: 0, }, - async (ethTx: TypedTransaction) => { + async (_: string, ethTx: TypedTransaction) => { const privateKey = Buffer.from( mockedAccounts.goerli[0].key, 'hex' @@ -1850,6 +1859,8 @@ describe('Transactions Controller', () => { expect(updatedTx).deep.equal({ transactionParams: { gasPrice: BigNumber.from('10'), + maxFeePerGas: undefined, + maxPriorityFeePerGas: undefined }, } as TransactionMeta); }); diff --git a/packages/background/test/infrastructure/stores/migrator/migrator.test.ts b/packages/background/test/infrastructure/stores/migrator/migrator.test.ts index 0c928c5dd..a7469d808 100644 --- a/packages/background/test/infrastructure/stores/migrator/migrator.test.ts +++ b/packages/background/test/infrastructure/stores/migrator/migrator.test.ts @@ -8,7 +8,7 @@ import { IMigration } from '@block-wallet/background/infrastructure/stores/migra import { expect } from 'chai'; import { lt, lte, valid } from 'semver'; import { version } from '../../../../package.json'; -import { sha256 } from 'ethereumjs-util'; +import { sha256 } from '@ethersproject/sha2'; describe('Migrations integrity', () => { it('package version integrity', () => { @@ -63,9 +63,8 @@ describe('Migrations integrity', () => { return allMigrations.some((b: IMigration, j: number) => { return ( i !== j && - sha256(Buffer.from(a.migrate.toString())).equals( + sha256(Buffer.from(a.migrate.toString())) === sha256(Buffer.from(b.migrate.toString())) - ) ); }); } diff --git a/packages/background/test/infrastructure/stores/migrator/reconciler.test.ts b/packages/background/test/infrastructure/stores/migrator/reconciler.test.ts index de7e332be..fc4760336 100644 --- a/packages/background/test/infrastructure/stores/migrator/reconciler.test.ts +++ b/packages/background/test/infrastructure/stores/migrator/reconciler.test.ts @@ -142,7 +142,12 @@ const persistedState: DeepPartial = { selectedNetwork: 'mainnet', isNetworkChanging: false, isUserNetworkOnline: true, - isProviderNetworkOnline: true, + providerStatus: { + isCurrentProviderOnline: true, + isDefaultProviderOnline: true, + isBackupProviderOnline: true, + isUsingBackupProvider: false, + }, isEIP1559Compatible: {}, }, TransactionController: { @@ -157,6 +162,9 @@ const initialState: newBlankAppState = { transactions: {}, tokenAllowanceEvents: {}, }, + CampaignsController: { + enrollments: {}, + }, BridgeController: { bridgeReceivingTransactions: {}, perndingBridgeReceivingTransactions: {}, @@ -187,6 +195,7 @@ const initialState: newBlankAppState = { isRefreshingAllowances: false, hiddenAccounts: {}, accounts: {}, + accountTokensOrder: {}, }, AppStateController: { idleTimeout: 5, @@ -207,7 +216,12 @@ const initialState: newBlankAppState = { availableNetworks: INITIAL_NETWORKS, isNetworkChanging: false, isUserNetworkOnline: true, - isProviderNetworkOnline: true, + providerStatus: { + isCurrentProviderOnline: true, + isDefaultProviderOnline: true, + isBackupProviderOnline: true, + isUsingBackupProvider: false, + }, isEIP1559Compatible: {}, }, OnboardingController: { @@ -227,12 +241,16 @@ const initialState: newBlankAppState = { antiPhishingImage: '', settings: { hideAddressWarning: false, + hideSendToContractWarning: false, + hideSendToNullWarning: false, subscribedToReleaseaNotes: true, + subscribedToNotifications: true, useAntiPhishingProtection: true, defaultBrowserWallet: true, hideEstimatedGasExceedsThresholdWarning: false, hideDepositsExternalAccountsWarning: false, hideBridgeInsufficientNativeTokenWarning: false, + displayNetWorth: true, }, releaseNotesSettings: { lastVersionUserSawNews: '0.1.3', @@ -242,6 +260,9 @@ const initialState: newBlankAppState = { account: [], }, defaultGasOption: 'medium', + hotkeysEnabled: true, + tokensSortValue: 'CUSTOM', + hideSmallBalances: false, }, TransactionController: { transactions: [], @@ -332,6 +353,9 @@ describe('State reconciler', () => { BlockUpdatesController: { blockData: { 5: { blockNumber: -1 } }, }, + CampaignsController: { + enrollments: {}, + }, AddressBookController: { addressBook: {} as AddressBook, recentAddresses: {} as AddressBook, @@ -364,6 +388,7 @@ describe('State reconciler', () => { }, }, hiddenAccounts: {}, + accountTokensOrder: {}, }, AppStateController: { idleTimeout: 5, @@ -440,7 +465,12 @@ describe('State reconciler', () => { availableNetworks: INITIAL_NETWORKS, isNetworkChanging: false, isUserNetworkOnline: true, - isProviderNetworkOnline: true, + providerStatus: { + isCurrentProviderOnline: true, + isDefaultProviderOnline: true, + isBackupProviderOnline: true, + isUsingBackupProvider: false, + }, isEIP1559Compatible: {}, }, PreferencesController: { @@ -454,13 +484,17 @@ describe('State reconciler', () => { showDefaultWalletPreferences: false, popupTab: 'activity', settings: { + subscribedToNotifications: true, subscribedToReleaseaNotes: true, hideAddressWarning: false, + hideSendToContractWarning: false, + hideSendToNullWarning: false, useAntiPhishingProtection: true, defaultBrowserWallet: true, hideEstimatedGasExceedsThresholdWarning: false, hideDepositsExternalAccountsWarning: false, hideBridgeInsufficientNativeTokenWarning: false, + displayNetWorth: true, }, releaseNotesSettings: { lastVersionUserSawNews: '0.1.3', @@ -470,6 +504,9 @@ describe('State reconciler', () => { account: [], }, defaultGasOption: 'medium', + hotkeysEnabled: true, + tokensSortValue: 'CUSTOM', + hideSmallBalances: false, }, TransactionController: { transactions: [], diff --git a/packages/background/test/mocks/mock-exchangerates.ts b/packages/background/test/mocks/mock-exchangerates.ts index 20e034372..c5559ce0c 100644 --- a/packages/background/test/mocks/mock-exchangerates.ts +++ b/packages/background/test/mocks/mock-exchangerates.ts @@ -31,8 +31,9 @@ import TransactionController from '@block-wallet/background/controllers/transact import { mockedPermissionsController } from './mock-permissions'; import { TypedTransaction } from '@ethereumjs/tx'; import { GasPricesController } from '@block-wallet/background/controllers/GasPricesController'; -import { BigNumber } from 'ethers'; +import { BigNumber } from '@ethersproject/bignumber'; import { TokenOperationsController } from '@block-wallet/background/controllers/erc-20/transactions/TokenOperationsController'; +import { mockKeyringController } from './mock-keyring-controller'; let exchangeRatesControllerETH: ExchangeRatesControllerState; let blockFetchController: BlockFetchController; @@ -116,6 +117,7 @@ transactionController = new TransactionController( gasPricesController, tokenController, blockUpdatesController, + mockKeyringController, { transactions: [], txSignTimeout: 0, diff --git a/packages/background/test/mocks/mock-network-instance.ts b/packages/background/test/mocks/mock-network-instance.ts index 6e39e16db..8d6f9660c 100644 --- a/packages/background/test/mocks/mock-network-instance.ts +++ b/packages/background/test/mocks/mock-network-instance.ts @@ -8,7 +8,12 @@ const initialNetworkControllerState: NetworkControllerState = { availableNetworks: INITIAL_NETWORKS, isNetworkChanging: false, isUserNetworkOnline: true, - isProviderNetworkOnline: true, + providerStatus: { + isCurrentProviderOnline: true, + isDefaultProviderOnline: true, + isBackupProviderOnline: true, + isUsingBackupProvider: false, + }, isEIP1559Compatible: {}, }; diff --git a/packages/background/test/mocks/mock-preferences.ts b/packages/background/test/mocks/mock-preferences.ts index c4d8d62f6..2314a5fd8 100644 --- a/packages/background/test/mocks/mock-preferences.ts +++ b/packages/background/test/mocks/mock-preferences.ts @@ -27,12 +27,16 @@ testInitState = { popupTab: 'activity', settings: { hideAddressWarning: false, // Shown by default + hideSendToContractWarning: false, // Shown by default + hideSendToNullWarning: false, // Shown by default subscribedToReleaseaNotes: true, + subscribedToNotifications: true, useAntiPhishingProtection: true, defaultBrowserWallet: true, hideEstimatedGasExceedsThresholdWarning: false, hideDepositsExternalAccountsWarning: false, hideBridgeInsufficientNativeTokenWarning: false, + displayNetWorth: true, }, releaseNotesSettings: { lastVersionUserSawNews: '0.1.3', @@ -41,7 +45,10 @@ testInitState = { filters: { account: [], }, - defaultGasOption: "medium" + defaultGasOption: 'medium', + hotkeysEnabled: true, + tokensSortValue: 'CUSTOM', + hideSmallBalances: false, }; const mockPreferencesController = new PreferencesController({ diff --git a/packages/background/test/mocks/sinonChrome.js b/packages/background/test/mocks/sinonChrome.js index d07b94282..b6ac2b58a 100644 --- a/packages/background/test/mocks/sinonChrome.js +++ b/packages/background/test/mocks/sinonChrome.js @@ -1,2 +1,3 @@ const chrome = require('sinon-chrome/apps'); +chrome.runtime.id = "testid"; global.chrome = chrome; diff --git a/packages/background/test/utils/userPreferences.test.ts b/packages/background/test/utils/userPreferences.test.ts index 24a18b71d..3b952f3b9 100644 --- a/packages/background/test/utils/userPreferences.test.ts +++ b/packages/background/test/utils/userPreferences.test.ts @@ -51,12 +51,16 @@ describe('userPreferences tests', () => { { settings: { subscribedToReleaseaNotes: false, + subscribedToNotifications: true, hideAddressWarning: false, + hideSendToContractWarning: false, + hideSendToNullWarning: false, useAntiPhishingProtection: true, defaultBrowserWallet: true, hideEstimatedGasExceedsThresholdWarning: false, hideDepositsExternalAccountsWarning: false, hideBridgeInsufficientNativeTokenWarning: false, + displayNetWorth: true }, }, '2.0.0' @@ -74,12 +78,17 @@ describe('userPreferences tests', () => { { settings: { subscribedToReleaseaNotes: true, + subscribedToNotifications: true, hideAddressWarning: false, + hideSendToContractWarning: false, + hideSendToNullWarning: false, useAntiPhishingProtection: true, defaultBrowserWallet: true, hideEstimatedGasExceedsThresholdWarning: false, hideDepositsExternalAccountsWarning: false, hideBridgeInsufficientNativeTokenWarning: false, + displayNetWorth: true + }, releaseNotesSettings: { lastVersionUserSawNews: '2.0.0', @@ -101,12 +110,17 @@ describe('userPreferences tests', () => { { settings: { subscribedToReleaseaNotes: true, + subscribedToNotifications: true, hideAddressWarning: false, + hideSendToContractWarning: false, + hideSendToNullWarning: false, useAntiPhishingProtection: true, defaultBrowserWallet: true, hideEstimatedGasExceedsThresholdWarning: false, hideDepositsExternalAccountsWarning: false, hideBridgeInsufficientNativeTokenWarning: false, + displayNetWorth: true + }, releaseNotesSettings: { lastVersionUserSawNews: '1.0.0', diff --git a/packages/background/webpack/webpack.shared.js b/packages/background/webpack/webpack.shared.js index 70ecb32a4..0a33a9982 100644 --- a/packages/background/webpack/webpack.shared.js +++ b/packages/background/webpack/webpack.shared.js @@ -77,6 +77,10 @@ module.exports = (entry) => ({ crypto: require.resolve('crypto-browserify'), stream: require.resolve('stream-browserify'), buffer: require.resolve('buffer/'), + http: require.resolve('stream-http'), + https: require.resolve('https-browserify'), + zlib: require.resolve('browserify-zlib'), + url: require.resolve('url/') }, }, experiments: { @@ -84,4 +88,15 @@ module.exports = (entry) => ({ syncWebAssembly: true, }, plugins, + optimization: { + splitChunks: { + cacheGroups: { + vendor: { + test: /[\\/]node_modules[\\/](@block-wallet)/, + name: 'bw-libs', + chunks: 'initial' + } + } + } + } }); diff --git a/packages/background/yarn.lock b/packages/background/yarn.lock index fa844d4cb..f3de09328 100644 --- a/packages/background/yarn.lock +++ b/packages/background/yarn.lock @@ -2,212 +2,186 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" - "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" -"@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== +"@babel/code-frame@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.25.7.tgz#438f2c524071531d643c6f0188e1e28f130cebc7" + integrity sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g== dependencies: - "@babel/highlight" "^7.18.6" + "@babel/highlight" "^7.25.7" + picocolors "^1.0.0" -"@babel/compat-data@^7.20.0": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.5.tgz#86f172690b093373a933223b4745deeb6049e733" - integrity sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g== +"@babel/compat-data@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.25.7.tgz#b8479fe0018ef0ac87b6b7a5c6916fcd67ae2c9c" + integrity sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw== "@babel/core@^7.7.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.5.tgz#45e2114dc6cd4ab167f81daf7820e8fa1250d113" - integrity sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.5" - "@babel/helper-compilation-targets" "^7.20.0" - "@babel/helper-module-transforms" "^7.20.2" - "@babel/helpers" "^7.20.5" - "@babel/parser" "^7.20.5" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.5" - "@babel/types" "^7.20.5" - convert-source-map "^1.7.0" + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.25.7.tgz#1b3d144157575daf132a3bc80b2b18e6e3ca6ece" + integrity sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.25.7" + "@babel/generator" "^7.25.7" + "@babel/helper-compilation-targets" "^7.25.7" + "@babel/helper-module-transforms" "^7.25.7" + "@babel/helpers" "^7.25.7" + "@babel/parser" "^7.25.7" + "@babel/template" "^7.25.7" + "@babel/traverse" "^7.25.7" + "@babel/types" "^7.25.7" + convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.1" - semver "^6.3.0" - -"@babel/generator@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.5.tgz#cb25abee3178adf58d6814b68517c62bdbfdda95" - integrity sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA== - dependencies: - "@babel/types" "^7.20.5" - "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" - -"@babel/helper-compilation-targets@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" - integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== - dependencies: - "@babel/compat-data" "^7.20.0" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.21.3" - semver "^6.3.0" - -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== - -"@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== - dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" - -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-transforms@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" - integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.2" - -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== - dependencies: - "@babel/types" "^7.20.2" - -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== - -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== - -"@babel/helpers@^7.20.5": - version "7.20.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.6.tgz#e64778046b70e04779dfbdf924e7ebb45992c763" - integrity sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w== - dependencies: - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.5" - "@babel/types" "^7.20.5" - -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.25.7.tgz#de86acbeb975a3e11ee92dd52223e6b03b479c56" + integrity sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA== + dependencies: + "@babel/types" "^7.25.7" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + +"@babel/helper-compilation-targets@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz#11260ac3322dda0ef53edfae6e97b961449f5fa4" + integrity sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A== + dependencies: + "@babel/compat-data" "^7.25.7" + "@babel/helper-validator-option" "^7.25.7" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-module-imports@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz#dba00d9523539152906ba49263e36d7261040472" + integrity sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw== + dependencies: + "@babel/traverse" "^7.25.7" + "@babel/types" "^7.25.7" + +"@babel/helper-module-transforms@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz#2ac9372c5e001b19bc62f1fe7d96a18cb0901d1a" + integrity sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ== + dependencies: + "@babel/helper-module-imports" "^7.25.7" + "@babel/helper-simple-access" "^7.25.7" + "@babel/helper-validator-identifier" "^7.25.7" + "@babel/traverse" "^7.25.7" + +"@babel/helper-simple-access@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz#5eb9f6a60c5d6b2e0f76057004f8dacbddfae1c0" + integrity sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ== + dependencies: + "@babel/traverse" "^7.25.7" + "@babel/types" "^7.25.7" + +"@babel/helper-string-parser@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz#d50e8d37b1176207b4fe9acedec386c565a44a54" + integrity sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g== + +"@babel/helper-validator-identifier@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz#77b7f60c40b15c97df735b38a66ba1d7c3e93da5" + integrity sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg== + +"@babel/helper-validator-option@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz#97d1d684448228b30b506d90cace495d6f492729" + integrity sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ== + +"@babel/helpers@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.25.7.tgz#091b52cb697a171fe0136ab62e54e407211f09c2" + integrity sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA== + dependencies: + "@babel/template" "^7.25.7" + "@babel/types" "^7.25.7" + +"@babel/highlight@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.25.7.tgz#20383b5f442aa606e7b5e3043b0b1aafe9f37de5" + integrity sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw== + dependencies: + "@babel/helper-validator-identifier" "^7.25.7" + chalk "^2.4.2" js-tokens "^4.0.0" + picocolors "^1.0.0" -"@babel/parser@^7.18.10", "@babel/parser@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8" - integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA== +"@babel/parser@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.7.tgz#99b927720f4ddbfeb8cd195a363ed4532f87c590" + integrity sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw== + dependencies: + "@babel/types" "^7.25.7" "@babel/runtime@^7.15.4": - version "7.20.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3" - integrity sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA== - dependencies: - regenerator-runtime "^0.13.11" - -"@babel/template@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" - integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.10" - "@babel/types" "^7.18.10" - -"@babel/traverse@^7.20.1", "@babel/traverse@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133" - integrity sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.5" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.5" - "@babel/types" "^7.20.5" - debug "^4.1.0" + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.7.tgz#7ffb53c37a8f247c8c4d335e89cdf16a2e0d0fb6" + integrity sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/template@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.7.tgz#27f69ce382855d915b14ab0fe5fb4cbf88fa0769" + integrity sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA== + dependencies: + "@babel/code-frame" "^7.25.7" + "@babel/parser" "^7.25.7" + "@babel/types" "^7.25.7" + +"@babel/traverse@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.7.tgz#83e367619be1cab8e4f2892ef30ba04c26a40fa8" + integrity sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg== + dependencies: + "@babel/code-frame" "^7.25.7" + "@babel/generator" "^7.25.7" + "@babel/parser" "^7.25.7" + "@babel/template" "^7.25.7" + "@babel/types" "^7.25.7" + debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84" - integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg== +"@babel/types@^7.25.7": + version "7.25.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.7.tgz#1b7725c1d3a59f328cb700ce704c46371e6eef9b" + integrity sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ== dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-string-parser" "^7.25.7" + "@babel/helper-validator-identifier" "^7.25.7" to-fast-properties "^2.0.0" -"@block-wallet/chains-assets@https://github.com/block-wallet/chains-assets#v0.0.27": - version "0.0.27" - resolved "https://github.com/block-wallet/chains-assets#e85446cac33b09c7e3a9893644ad3091eba806fd" +"@block-wallet/chains-assets@https://github.com/block-wallet/chains-assets#v0.0.63": + version "0.0.63" + resolved "https://github.com/block-wallet/chains-assets#1322b3706cc0fbf434a61f55ab63823bff800452" -"@block-wallet/eth-ledger-bridge-keyring@https://github.com/block-wallet/eth-ledger-bridge-keyring": - version "0.13.0" - resolved "https://github.com/block-wallet/eth-ledger-bridge-keyring#403c27ac0a1d49a4ee9722238f22c3ccf0a926f1" +"@block-wallet/eth-ledger-bridge-keyring@https://github.com/block-wallet/eth-ledger-bridge-keyring#v0.14.1": + version "0.14.0" + resolved "https://github.com/block-wallet/eth-ledger-bridge-keyring#503cca048bf588cf654387113ecd873c64c7386a" dependencies: - "@ethereumjs/tx" "^3.2.0" + "@ethereumjs/tx" "^4.0.2" + "@ethereumjs/util" "^8.0.3" eth-sig-util "^2.0.0" - ethereumjs-util "^7.0.9" hdkey "0.8.0" + rlp "^2.2.4" "@block-wallet/explorer-link@https://github.com/block-wallet/explorer-link#v2.2.2": version "2.2.2" @@ -217,6 +191,27 @@ version "0.0.0" resolved "https://github.com/block-wallet/remote-configs#eca1a737090b224974ede7b47ec44224b31fe2d1" +"@chainsafe/as-sha256@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz#3639df0e1435cab03f4d9870cc3ac079e57a6fc9" + integrity sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg== + +"@chainsafe/persistent-merkle-tree@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.4.2.tgz#4c9ee80cc57cd3be7208d98c40014ad38f36f7ff" + integrity sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ== + dependencies: + "@chainsafe/as-sha256" "^0.3.1" + +"@chainsafe/ssz@0.9.4": + version "0.9.4" + resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.9.4.tgz#696a8db46d6975b600f8309ad3a12f7c0e310497" + integrity sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ== + dependencies: + "@chainsafe/as-sha256" "^0.3.1" + "@chainsafe/persistent-merkle-tree" "^0.4.2" + case "^1.6.3" + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -224,26 +219,43 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@discoveryjs/json-ext@^0.5.0": +"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@eslint/eslintrc@^1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" - integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": + version "4.11.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.1.tgz#a547badfc719eb3e5f4b556325e542fbe9d7a18f" + integrity sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.4.0" - globals "^13.15.0" + espree "^9.6.0" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/js@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" + integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== + "@ethereumjs/common@^2.6.4": version "2.6.5" resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.5.tgz#0a75a22a046272579d91919cb12d84f2756e8d30" @@ -252,12 +264,20 @@ crc-32 "^1.2.0" ethereumjs-util "^7.1.5" -"@ethereumjs/rlp@^4.0.0-beta.2": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.0.tgz#66719891bd727251a7f233f9ca80212d1994f8c8" - integrity sha512-LM4jS5n33bJN60fM5EC8VeyhUgga6/DjCPBV2vWjnfVtobqtOiNC4SQ1MRFqyBSmJGGdB533JZWewyvlcdJtkQ== +"@ethereumjs/common@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-3.2.0.tgz#b71df25845caf5456449163012074a55f048e0a0" + integrity sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA== + dependencies: + "@ethereumjs/util" "^8.1.0" + crc-32 "^1.2.0" + +"@ethereumjs/rlp@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" + integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== -"@ethereumjs/tx@^3.2.0", "@ethereumjs/tx@^3.2.1", "@ethereumjs/tx@^3.5.2": +"@ethereumjs/tx@^3.2.1": version "3.5.2" resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.5.2.tgz#197b9b6299582ad84f9527ca961466fce2296c1c" integrity sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw== @@ -265,24 +285,34 @@ "@ethereumjs/common" "^2.6.4" ethereumjs-util "^7.1.5" -"@ethereumjs/util@^8.0.0": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.0.2.tgz#b7348fc7253649b0f00685a94546c6eee1fad819" - integrity sha512-b1Fcxmq+ckCdoLPhVIBkTcH8szigMapPuEmD8EDakvtI5Na5rzmX1sBW73YQqaPc7iUxGCAzZP1LrFQ7aEMugA== +"@ethereumjs/tx@^4.0.2", "@ethereumjs/tx@^4.1.2", "@ethereumjs/tx@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-4.2.0.tgz#5988ae15daf5a3b3c815493bc6b495e76009e853" + integrity sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw== dependencies: - "@ethereumjs/rlp" "^4.0.0-beta.2" - async "^3.2.4" - ethereum-cryptography "^1.1.2" + "@ethereumjs/common" "^3.2.0" + "@ethereumjs/rlp" "^4.0.1" + "@ethereumjs/util" "^8.1.0" + ethereum-cryptography "^2.0.0" -"@ethereumjs/util@^8.0.2": - version "8.0.3" - resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.0.3.tgz#410c2dc8c6d519b29f1a471aa9b9df9952e41239" - integrity sha512-0apCbwc8xAaie6W7q6QyogfyRS2BMU816a8KwpnpRw9Qrc6Bws+l7J3LfCLMt2iL6Wi8CYb0B29AeIr2N4vHnw== +"@ethereumjs/util@8.0.5": + version "8.0.5" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.0.5.tgz#b9088fc687cc13f0c1243d6133d145dfcf3fe446" + integrity sha512-259rXKK3b3D8HRVdRmlOEi6QFvwxdt304hhrEAmpZhsj7ufXEOTIc9JRZPMnXatKjECokdLNBcDOFBeBSzAIaw== dependencies: - "@ethereumjs/rlp" "^4.0.0-beta.2" - async "^3.2.4" + "@chainsafe/ssz" "0.9.4" + "@ethereumjs/rlp" "^4.0.1" ethereum-cryptography "^1.1.2" +"@ethereumjs/util@^8.0.0", "@ethereumjs/util@^8.0.3", "@ethereumjs/util@^8.0.5", "@ethereumjs/util@^8.0.6", "@ethereumjs/util@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.1.0.tgz#299df97fb6b034e0577ce9f94c7d9d1004409ed4" + integrity sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA== + dependencies: + "@ethereumjs/rlp" "^4.0.1" + ethereum-cryptography "^2.0.0" + micro-ftch "^0.3.1" + "@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.1", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" @@ -625,13 +655,13 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@humanwhocodes/config-array@^0.11.6": - version "0.11.7" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f" - integrity sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw== +"@humanwhocodes/config-array@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" + integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" + "@humanwhocodes/object-schema" "^2.0.3" + debug "^4.3.1" minimatch "^3.0.5" "@humanwhocodes/module-importer@^1.0.1": @@ -639,10 +669,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@humanwhocodes/object-schema@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -660,45 +690,56 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@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" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== +"@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: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@sinclair/typebox" "^0.27.8" + +"@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.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== dependencies: - "@jridgewell/set-array" "^1.0.1" + "@jridgewell/set-array" "^1.2.1" "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/trace-mapping" "^0.3.24" -"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== -"@jridgewell/source-map@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" - integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== "@jridgewell/trace-mapping@0.3.9": version "0.3.9" @@ -708,32 +749,96 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== +"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@keystonehq/alias-sampling@^0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@keystonehq/alias-sampling/-/alias-sampling-0.1.2.tgz#63af931ffe6500aef4c0d87775a5b279189abf8d" + integrity sha512-5ukLB3bcgltgaFfQfYKYwHDUbwHicekYo53fSEa7xhVkAEqsA74kxdIwoBIURmGUtXe3EVIRm4SYlgcrt2Ri0w== + +"@keystonehq/base-eth-keyring@^0.14.1": + version "0.14.1" + resolved "https://registry.yarnpkg.com/@keystonehq/base-eth-keyring/-/base-eth-keyring-0.14.1.tgz#b838524678e5d3e70c0f1e9e1089baece6ef5f87" + integrity sha512-rhsbN7YlwWEcaUcwapApZe4EC/xQFJVnU0CpzLN0r9b2nqyEp8q9oz42jPr8W6vXHD72bezAZKMvqU/6rCecQQ== + dependencies: + "@ethereumjs/tx" "^4.0.2" + "@ethereumjs/util" "^8.0.0" + "@keystonehq/bc-ur-registry-eth" "^0.19.1" + hdkey "^2.0.1" + rlp "^3.0.0" + uuid "^8.3.2" + +"@keystonehq/bc-ur-registry-eth@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@keystonehq/bc-ur-registry-eth/-/bc-ur-registry-eth-0.19.1.tgz#eac508b9d15d17c0abd00b107691585f9c789ffc" + integrity sha512-5+skb1zsmMEIGZCbk+4KssZTpLMTriaFlt+Lc6pZLmxexXrX8a/9aHoho3asOqf7GeXXqkB9YKs8i8TN/hbaHA== + dependencies: + "@ethereumjs/util" "^8.0.0" + "@keystonehq/bc-ur-registry" "^0.6.0" + hdkey "^2.0.1" + uuid "^8.3.2" + +"@keystonehq/bc-ur-registry@^0.6.0": + version "0.6.4" + resolved "https://registry.yarnpkg.com/@keystonehq/bc-ur-registry/-/bc-ur-registry-0.6.4.tgz#9c57ff9687cafdc0d2bbd04dc36676d3a38c1485" + integrity sha512-j8Uy44DuAkvYkbf0jMxRY3UizJfn8wsEQr7GS3miRF44vcq7k0/yemVkftbn3jQ+0JYaUXf5wY7lVpLhAeW5nQ== + dependencies: + "@ngraveio/bc-ur" "^1.1.5" + bs58check "^2.1.2" + tslib "^2.3.0" + +"@keystonehq/metamask-airgapped-keyring@^0.13.1": + version "0.13.1" + resolved "https://registry.yarnpkg.com/@keystonehq/metamask-airgapped-keyring/-/metamask-airgapped-keyring-0.13.1.tgz#286be5bd2fa1b2a43dc749c4501270cc70701e9d" + integrity sha512-muEBn/EXHCMtW2EJpLYRCW+3QVyLVCn/BHLPOxqx+rEOJnOZZexMR91fmCLSYo86v27DQMs76nlsnHc2WcGBng== + dependencies: + "@ethereumjs/tx" "^4.0.2" + "@keystonehq/base-eth-keyring" "^0.14.1" + "@keystonehq/bc-ur-registry-eth" "^0.19.1" + "@metamask/obs-store" "^7.0.0" + rlp "^2.2.6" + uuid "^8.3.2" + +"@metamask/abi-utils@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@metamask/abi-utils/-/abi-utils-1.2.0.tgz#068e1b0f5e423dfae96961e0e5276a7c1babc03a" + integrity sha512-Hf7fnBDM9ptCPDtq/wQffWbw859CdVGMwlpWUEsTH6gLXhXONGrRXHA2piyYPRuia8YYTdJvRC/zSK1/nyLvYg== dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" + "@metamask/utils" "^3.4.1" + superstruct "^1.0.3" -"@metamask/browser-passworder@^4.0.2", "@metamask/browser-passworder@https://github.com/block-wallet/browser-passworder#v1.0.1": +"@metamask/browser-passworder@^4.0.2": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@metamask/browser-passworder/-/browser-passworder-4.3.0.tgz#62c200750efcea864bd31d685120331859e1ab1e" + integrity sha512-RU1TVVV5DkbZRr6zPYg0NkexZ0/T2LCKNvF3A50jvUweyxDFuoNbSTN6z8K3Fy8O6/X2JQ1yyAbVzxZLq0qrGg== + dependencies: + "@metamask/utils" "^8.2.0" + +"@metamask/browser-passworder@https://github.com/block-wallet/browser-passworder#v1.0.1": version "4.0.2" resolved "https://github.com/block-wallet/browser-passworder#f8fa5a8a9dfdba84afc8b67c481e6c9374e99d13" "@metamask/eth-hd-keyring@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@metamask/eth-hd-keyring/-/eth-hd-keyring-6.0.0.tgz#a46788d4bbc7aa5d8263ed6e4348101a80519a69" - integrity sha512-dEj/I6Ag9FyCmjPcRXeXCkRXkVJE/uElhDVRcLBU6mT/GsXKgzVWXC/k0dhE8rEDrQbidhl+8wEElSJ2LI1InA== + version "6.0.2" + resolved "https://registry.yarnpkg.com/@metamask/eth-hd-keyring/-/eth-hd-keyring-6.0.2.tgz#e51067272d87d868cea90844a49296c0bb524b75" + integrity sha512-NcScEf+PieuWymSLFd2vCcM99NZ3QxwpyODzNNd0iNB8+aftUNfqrZEgcro8Dqh9DYlq8tGBYyQJIZh74NABGA== dependencies: - "@ethereumjs/util" "^8.0.2" - "@metamask/eth-sig-util" "^5.0.2" - "@metamask/scure-bip39" "^2.0.3" - ethereum-cryptography "^1.1.2" + "@ethereumjs/util" "8.0.5" + "@metamask/eth-sig-util" "^6.0.0" + "@metamask/scure-bip39" "^2.1.0" + "@metamask/utils" "^5.0.2" + ethereum-cryptography "^1.2.0" "@metamask/eth-keyring-controller@^10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@metamask/eth-keyring-controller/-/eth-keyring-controller-10.0.0.tgz#71e8fd84a02fae70e7afe75f09c4551114cad742" - integrity sha512-Ypox0BunIQO6x0E3KUzhw125zoWKwkpZl9xw4rnVNbZOHrsD+/uXGTAZRSPLdoKbq0DYdkQAvcds8FeoJEWEYw== + version "10.0.1" + resolved "https://registry.yarnpkg.com/@metamask/eth-keyring-controller/-/eth-keyring-controller-10.0.1.tgz#732ccd7156b0139da352b028cd921dfbed05992e" + integrity sha512-oLjBT/UG4N3IjSWW/OZGkZRsUBtUstSS2yOPQJGgWKn4SL5r8sj6XZMUNRipMLBdXZTYPOuAFxhc+2pSlWh8Sw== dependencies: "@metamask/browser-passworder" "^4.0.2" "@metamask/eth-hd-keyring" "^6.0.0" @@ -741,7 +846,7 @@ "@metamask/eth-simple-keyring" "^5.0.0" obs-store "^4.0.3" -"@metamask/eth-sig-util@5.0.2", "@metamask/eth-sig-util@^5.0.1", "@metamask/eth-sig-util@^5.0.2": +"@metamask/eth-sig-util@5.0.2": version "5.0.2" resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-5.0.2.tgz#c518279a6e17a88135a13d53a0b970f145ff8bce" integrity sha512-RU6fG/H6/UlBol221uBkq5C7w3TwLK611nEZliO2u+kO0vHKGBXnIPlhI0tzKUigjhUeOd9mhCNbNvhh0LKt9Q== @@ -764,38 +869,148 @@ tweetnacl "^1.0.3" tweetnacl-util "^0.15.1" +"@metamask/eth-sig-util@^5.0.2": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-5.1.0.tgz#a47f62800ee1917fef976ba67544a0ccd7d1bd6b" + integrity sha512-mlgziIHYlA9pi/XZerChqg4NocdOgBPB9NmxgXWQO2U2hH8RGOJQrz6j/AIKkYxgCMIE2PY000+joOwXfzeTDQ== + dependencies: + "@ethereumjs/util" "^8.0.6" + bn.js "^4.12.0" + ethereum-cryptography "^2.0.0" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + +"@metamask/eth-sig-util@^6.0.0", "@metamask/eth-sig-util@^6.0.1": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-6.0.2.tgz#d81dc87e0cd5a6580010911501976b48821746ad" + integrity sha512-D6IIefM2vS+4GUGGtezdBbkwUYQC4bCosYx/JteUuF0zfe6lyxR4cruA8+2QHoUg7F7edNH1xymYpqYq1BeOkw== + dependencies: + "@ethereumjs/util" "^8.1.0" + "@metamask/abi-utils" "^1.2.0" + "@metamask/utils" "^5.0.2" + ethereum-cryptography "^2.1.2" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + "@metamask/eth-simple-keyring@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@metamask/eth-simple-keyring/-/eth-simple-keyring-5.0.0.tgz#307772d1aa3298e41a2444b428cd8f4522b7bf5c" - integrity sha512-UJfP36Z9g1eeD8mSHWaVqUvkgbgYm3S7YuzlMzQi+WgPnWu81CdbldMMtvreTlu4I1mTyljXLDMjIp65P0bygQ== + version "5.1.1" + resolved "https://registry.yarnpkg.com/@metamask/eth-simple-keyring/-/eth-simple-keyring-5.1.1.tgz#ce569258a0567848c276dc32b6af0cec360fc664" + integrity sha512-5q+DEx/7v0fk/OzyRH7jdNxG2VKDIWkWvXfwPxUAqHvu9G8sj6jDh3zRbREVNGi19VbPwJshyGUnM7HY8ibdCg== dependencies: - "@ethereumjs/util" "^8.0.0" - "@metamask/eth-sig-util" "^5.0.1" - ethereum-cryptography "^1.1.2" + "@ethereumjs/util" "^8.0.5" + "@metamask/eth-sig-util" "^6.0.1" + "@metamask/utils" "^5.0.2" + ethereum-cryptography "^1.2.0" randombytes "^2.1.0" -"@metamask/scure-bip39@^2.0.3": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@metamask/scure-bip39/-/scure-bip39-2.1.0.tgz#13456884736e56ede15e471bd93c0aa0acdedd0b" - integrity sha512-Ndwdnld0SI6YaftEUUVq20sdoWcWNXsJXxvQkbiY42FKmrA16U6WoSh9Eq+NpugpKKwK6f5uvaTDusjndiEDGQ== +"@metamask/obs-store@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@metamask/obs-store/-/obs-store-7.0.0.tgz#6cae5f28306bb3e83a381bc9ae22682316095bd3" + integrity sha512-Tr61Uu9CGXkCg5CZwOYRMQERd+y6fbtrtLd/PzDTPHO5UJpmSbU+7MPcQK7d1DwZCOCeCIvhmZSUCvYliC8uGw== dependencies: - "@noble/hashes" "~1.1.1" - "@scure/base" "~1.1.0" + "@metamask/safe-event-emitter" "^2.0.0" + through2 "^2.0.3" -"@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== +"@metamask/safe-event-emitter@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz#af577b477c683fad17c619a78208cede06f9605c" + integrity sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q== -"@noble/hashes@~1.1.1": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.3.tgz#360afc77610e0a61f3417e497dcf36862e4f8111" - integrity sha512-CE0FCR57H2acVI5UOzIGSSIYxZ6v/HOhDR0Ro9VLyhnzLwx0o8W1mmgaqlEUx4049qJDlIBRztv5k+MM8vbO3A== +"@metamask/scure-bip39@^2.1.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@metamask/scure-bip39/-/scure-bip39-2.1.1.tgz#071ddbaea7afe13886996c3bec22f472d79a4d34" + integrity sha512-1K8aBsAqr6+8jWhguVl06n8e+zjV9sUnys+5PLyVU4mb8LbulQ60F6cq7iQys3xX/yCwKt1+7c7j2nuTEpW+ZQ== + dependencies: + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.3" -"@noble/secp256k1@1.6.3", "@noble/secp256k1@~1.6.0": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94" - integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ== +"@metamask/superstruct@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@metamask/superstruct/-/superstruct-3.1.0.tgz#148f786a674fba3ac885c1093ab718515bf7f648" + integrity sha512-N08M56HdOgBfRKkrgCMZvQppkZGcArEop3kixNEtVbJKm6P9Cfg0YkI6X0s1g78sNrj2fWUwvJADdZuzJgFttA== + +"@metamask/utils@^3.4.1": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-3.6.0.tgz#b218b969a05ca7a8093b5d1670f6625061de707d" + integrity sha512-9cIRrfkWvHblSiNDVXsjivqa9Ak0RYo/1H6tqTqTbAx+oBK2Sva0lWDHxGchOqA7bySGUJKAWSNJvH6gdHZ0gQ== + dependencies: + "@types/debug" "^4.1.7" + debug "^4.3.4" + semver "^7.3.8" + superstruct "^1.0.3" + +"@metamask/utils@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-5.0.2.tgz#140ba5061d90d9dac0280c19cab101bc18c8857c" + integrity sha512-yfmE79bRQtnMzarnKfX7AEJBwFTxvTyw3nBQlu/5rmGXrjAeAMltoGxO62TFurxrQAFMNa/fEjIHNvungZp0+g== + dependencies: + "@ethereumjs/tx" "^4.1.2" + "@types/debug" "^4.1.7" + debug "^4.3.4" + semver "^7.3.8" + superstruct "^1.0.3" + +"@metamask/utils@^8.2.0": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-8.5.0.tgz#ddd0d4012d5191809404c97648a837ea9962cceb" + integrity sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ== + dependencies: + "@ethereumjs/tx" "^4.2.0" + "@metamask/superstruct" "^3.0.0" + "@noble/hashes" "^1.3.1" + "@scure/base" "^1.1.3" + "@types/debug" "^4.1.7" + debug "^4.3.4" + pony-cause "^2.1.10" + semver "^7.5.4" + uuid "^9.0.1" + +"@ngraveio/bc-ur@^1.1.5": + version "1.1.13" + resolved "https://registry.yarnpkg.com/@ngraveio/bc-ur/-/bc-ur-1.1.13.tgz#27719fd3e745ccdbe97a7950905edcd1fed4844b" + integrity sha512-j73akJMV4+vLR2yQ4AphPIT5HZmxVjn/LxpL7YHoINnXoH6ccc90Zzck6/n6a3bCXOVZwBxq+YHwbAKRV+P8Zg== + dependencies: + "@keystonehq/alias-sampling" "^0.1.1" + assert "^2.0.0" + bignumber.js "^9.0.1" + cbor-sync "^1.0.4" + crc "^3.8.0" + jsbi "^3.1.5" + sha.js "^2.4.11" + +"@noble/curves@1.4.2", "@noble/curves@~1.4.0": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9" + integrity sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw== + dependencies: + "@noble/hashes" "1.4.0" + +"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" + integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== + +"@noble/hashes@1.4.0", "@noble/hashes@~1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + +"@noble/hashes@^1.2.0", "@noble/hashes@^1.3.1": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.5.0.tgz#abadc5ca20332db2b1b2aa3e496e9af1213570b0" + integrity sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA== + +"@noble/hashes@~1.3.2": + 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.7.1", "@noble/secp256k1@~1.7.0": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -818,10 +1033,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@polka/url@^1.0.0-next.20": - version "1.0.0-next.21" - resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" - integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== +"@polka/url@^1.0.0-next.24": + version "1.0.0-next.28" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.28.tgz#d45e01c4a56f143ee69c54dd6b12eade9e270a73" + integrity sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -876,41 +1091,77 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== -"@scure/base@~1.1.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" - integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== +"@scure/base@^1.1.3", "@scure/base@~1.1.0", "@scure/base@~1.1.3", "@scure/base@~1.1.6": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" + integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== -"@scure/bip32@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.0.tgz#dea45875e7fbc720c2b4560325f1cf5d2246d95b" - integrity sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q== +"@scure/bip32@1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.5.tgz#d2ccae16dcc2e75bc1d75f5ef3c66a338d1ba300" + integrity sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw== dependencies: - "@noble/hashes" "~1.1.1" - "@noble/secp256k1" "~1.6.0" + "@noble/hashes" "~1.2.0" + "@noble/secp256k1" "~1.7.0" "@scure/base" "~1.1.0" -"@scure/bip39@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.0.tgz#92f11d095bae025f166bef3defcc5bf4945d419a" - integrity sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w== +"@scure/bip32@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67" + integrity sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg== dependencies: - "@noble/hashes" "~1.1.1" + "@noble/curves" "~1.4.0" + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" + +"@scure/bip39@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" + integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg== + dependencies: + "@noble/hashes" "~1.2.0" "@scure/base" "~1.1.0" -"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.5.tgz#e280c94c95f206dcfd5aca00a43f2156b758c764" - integrity sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA== +"@scure/bip39@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.3.0.tgz#0f258c16823ddd00739461ac31398b4e7d6a18c3" + integrity sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ== + dependencies: + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" + +"@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", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.7.0": + version "1.8.6" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" + integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" - integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== +"@sinonjs/commons@^3.0.0", "@sinonjs/commons@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: - "@sinonjs/commons" "^1.7.0" + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.3.0": + 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@^11.2.2": + version "11.3.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz#51d6e8d83ca261ff02c0ab0e68e9db23d5cd5999" + integrity sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA== + dependencies: + "@sinonjs/commons" "^3.0.1" "@sinonjs/formatio@^3.2.1": version "3.2.2" @@ -929,19 +1180,19 @@ array-from "^2.1.1" lodash "^4.17.15" -"@sinonjs/samsam@^5.3.1": - version "5.3.1" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.1.tgz#375a45fe6ed4e92fca2fb920e007c48232a6507f" - integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg== +"@sinonjs/samsam@^8.0.0": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-8.0.2.tgz#e4386bf668ff36c95949e55a38dc5f5892fc2689" + integrity sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw== dependencies: - "@sinonjs/commons" "^1.6.0" + "@sinonjs/commons" "^3.0.1" lodash.get "^4.4.2" - type-detect "^4.0.8" + type-detect "^4.1.0" -"@sinonjs/text-encoding@^0.7.1": - version "0.7.2" - resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz#5981a8db18b56ba38ef0efb7d995b12aa7b51918" - integrity sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ== +"@sinonjs/text-encoding@^0.7.1", "@sinonjs/text-encoding@^0.7.2": + version "0.7.3" + resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz#282046f03e886e352b2d5f5da5eb755e01457f3f" + integrity sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA== "@tootallnate/once@2": version "2.0.0" @@ -1012,9 +1263,9 @@ wif "^2.0.6" "@tsconfig/node10@^1.0.7": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" - integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== "@tsconfig/node12@^1.0.7": version "1.0.11" @@ -1027,9 +1278,9 @@ integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" - integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== "@types/bn.js@^4.11.3": version "4.11.6" @@ -1038,151 +1289,173 @@ dependencies: "@types/node" "*" -"@types/bn.js@^5.1.0": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" - integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== +"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.1": + version "5.1.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.6.tgz#9ba818eec0c85e4d3c679518428afdf611d03203" + integrity sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w== dependencies: "@types/node" "*" -"@types/chai@^4.2.14": - version "4.3.4" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.4.tgz#e913e8175db8307d78b4e8fa690408ba6b65dee4" - integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw== +"@types/chai@^4.3.4": + version "4.3.20" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.20.tgz#cb291577ed342ca92600430841a00329ba05cecc" + integrity sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ== -"@types/chrome@^0.0.197": - version "0.0.197" - resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.197.tgz#c1b50cdb72ee40f9bc1411506031a9f8a925ab35" - integrity sha512-m1NfS5bOjaypyqQfaX6CxmJodZVcvj5+Mt/K94EBHkflYjPNmXHAzbxfifdLMa0YM3PDyOxohoTS5ug/e6p5jA== +"@types/chrome@^0.0.206": + version "0.0.206" + resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.206.tgz#ad1fd9799f368b5993d7c240492d4adaf5efbd8c" + integrity sha512-fQnTFjghPB9S4UzbfublUB6KmsBkvvJeGXGaaoD5Qu+ZxrDUfgJnKN5egLSzDcGAH5YxQubDgbCdNwwUGewQHg== dependencies: "@types/filesystem" "*" "@types/har-format" "*" "@types/create-hash@^1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@types/create-hash/-/create-hash-1.2.2.tgz#e87247083df8478f6b83655592bde0d709028235" - integrity sha512-Fg8/kfMJObbETFU/Tn+Y0jieYewryLrbKwLCEIwPyklZZVY2qB+64KFjhplGSw+cseZosfFXctXO+PyIYD8iZQ== + version "1.2.6" + resolved "https://registry.yarnpkg.com/@types/create-hash/-/create-hash-1.2.6.tgz#ef9de147f4bf7d3fcfded4862726de949d8d44f4" + integrity sha512-/VIViZZAK3rAFvfGbWmcLaxwKfmU213W/XL2cr5VE0ac44jE5ky7+sHl54OJhd+bTz7sqi+Ev//8RU1F/S/ZJQ== dependencies: "@types/node" "*" -"@types/eslint-scope@^3.7.3": - version "3.7.4" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" - integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== +"@types/debug@^4.1.7": + version "4.1.12" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" + integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== dependencies: - "@types/eslint" "*" - "@types/estree" "*" + "@types/ms" "*" -"@types/eslint@*", "@types/eslint@^7.29.0 || ^8.4.1": - version "8.4.10" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.10.tgz#19731b9685c19ed1552da7052b6f668ed7eb64bb" - integrity sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw== +"@types/eslint@^8.56.10": + version "8.56.12" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.12.tgz#1657c814ffeba4d2f84c0d4ba0f44ca7ea1ca53a" + integrity sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g== dependencies: "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" - integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== - -"@types/estree@^0.0.51": - version "0.0.51" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" - integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== +"@types/estree@*", "@types/estree@^1.0.5": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== "@types/filesystem@*": - version "0.0.32" - resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.32.tgz#307df7cc084a2293c3c1a31151b178063e0a8edf" - integrity sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ== + version "0.0.36" + resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.36.tgz#7227c2d76bfed1b21819db310816c7821d303857" + integrity sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA== dependencies: "@types/filewriter" "*" "@types/filewriter@*": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.29.tgz#a48795ecadf957f6c0d10e0c34af86c098fa5bee" - integrity sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ== + version "0.0.33" + resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.33.tgz#d9d611db9d9cd99ae4e458de420eeb64ad604ea8" + integrity sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g== "@types/har-format@*": - version "1.2.9" - resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.9.tgz#b9b3a9bfc33a078e7d898a00b09662910577f4a4" - integrity sha512-rffW6MhQ9yoa75bdNi+rjZBAvu2HhehWJXlhuWXnWdENeuKe82wUgAwxYOb7KRKKmxYN+D/iRKd2NDQMLqlUmg== + version "1.2.16" + resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.16.tgz#b71ede8681400cc08b3685f061c31e416cf94944" + integrity sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" "@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" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/lodash@^4.14.136", "@types/lodash@^4.14.168": - version "4.14.190" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.190.tgz#d8e99647af141c63902d0ca53cf2b34d2df33545" - integrity sha512-5iJ3FBJBvQHQ8sFhEhJfjUP+G+LalhavTkYyrAYqz5MEJG+erSv0k9KJLb6q7++17Lafk1scaTIFXcMJlwK8Mw== +"@types/lodash@^4.14.136", "@types/lodash@^4.14.191": + version "4.17.10" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.10.tgz#64f3edf656af2fe59e7278b73d3e62404144a6e6" + integrity sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ== "@types/long@^4.0.1": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== -"@types/mocha@^8.2.0": - version "8.2.3" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.3.tgz#bbeb55fbc73f28ea6de601fbfa4613f58d785323" - integrity sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw== +"@types/mocha@^10.0.1": + version "10.0.9" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.9.tgz#101e9da88d2c02e5ac8952982c23b224524d662a" + integrity sha512-sicdRoWtYevwxjOHNMPTl3vSfJM6oyW8o1wXeI7uww6b6xHg8eBznQDNSGBCDJmsE8UMxP05JgZRtsKbTqt//Q== -"@types/node@*", "@types/node@>=13.7.0": - version "18.11.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" - integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== +"@types/ms@*": + version "0.7.34" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" + integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== -"@types/node@11.11.6": - version "11.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a" - integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ== +"@types/node@*", "@types/node@>=13.7.0": + version "22.7.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b" + integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== + dependencies: + undici-types "~6.19.2" -"@types/node@^16.9.1": - version "16.18.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.3.tgz#d7f7ba828ad9e540270f01ce00d391c54e6e0abc" - integrity sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg== +"@types/node@^20.6.3": + version "20.16.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.16.11.tgz#9b544c3e716b1577ac12e70f9145193f32750b33" + integrity sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw== + dependencies: + undici-types "~6.19.2" "@types/pbkdf2@^3.0.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" - integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + version "3.1.2" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.2.tgz#2dc43808e9985a2c69ff02e2d2027bd4fe33e8dc" + integrity sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew== dependencies: "@types/node" "*" "@types/secp256k1@^4.0.1": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c" - integrity sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w== + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.6.tgz#d60ba2349a51c2cbc5e816dcd831a42029d376bf" + integrity sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ== dependencies: "@types/node" "*" -"@types/semver@^7.3.12": - version "7.3.13" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" - integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== +"@types/semver@^7.3.12", "@types/semver@^7.3.13": + version "7.5.8" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" + integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== -"@types/sinon@^9.0.10": - version "9.0.11" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.11.tgz#7af202dda5253a847b511c929d8b6dda170562eb" - integrity sha512-PwP4UY33SeeVKodNE37ZlOsR9cReypbMJOhZ7BVE0lB+Hix3efCOxiJWiE5Ia+yL9Cn2Ch72EjFTRze8RZsNtg== +"@types/sinon@^10.0.13": + version "10.0.20" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.20.tgz#f1585debf4c0d99f9938f4111e5479fb74865146" + integrity sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg== dependencies: "@types/sinonjs__fake-timers" "*" "@types/sinonjs__fake-timers@*": - version "8.1.2" - resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz#bf2e02a3dbd4aecaf95942ecd99b7402e03fad5e" - integrity sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA== + version "8.1.5" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz#5fd3592ff10c1e9695d377020c033116cc2889f2" + integrity sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ== -"@types/uuid@^8.3.4": - version "8.3.4" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" - integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== +"@types/uuid@^9.0.0": + version "9.0.8" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" + integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== + +"@types/webextension-polyfill@^0.10.1": + version "0.10.7" + resolved "https://registry.yarnpkg.com/@types/webextension-polyfill/-/webextension-polyfill-0.10.7.tgz#de059250599733a60ed26c8a0c81e21e11183b90" + integrity sha512-10ql7A0qzBmFB+F+qAke/nP1PIonS0TXZAOMVOxEUsm+lGSW6uwVcISFNa0I4Oyj0884TZVWGGMIWeXOVSNFHw== "@types/ws@^7.2.0": version "7.4.7" @@ -1191,93 +1464,111 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^5.36.2": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.44.0.tgz#105788f299050c917eb85c4d9fd04b089e3740de" - integrity sha512-j5ULd7FmmekcyWeArx+i8x7sdRHzAtXTkmDPthE4amxZOWKFK7bomoJ4r7PJ8K7PoMzD16U8MmuZFAonr1ERvw== +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.8": + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^5.48.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== dependencies: - "@typescript-eslint/scope-manager" "5.44.0" - "@typescript-eslint/type-utils" "5.44.0" - "@typescript-eslint/utils" "5.44.0" + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" + graphemer "^1.4.0" ignore "^5.2.0" natural-compare-lite "^1.4.0" - regexpp "^3.2.0" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/parser@^5.36.2": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.44.0.tgz#99e2c710a2252191e7a79113264f438338b846ad" - integrity sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA== +"@typescript-eslint/parser@^5.48.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== dependencies: - "@typescript-eslint/scope-manager" "5.44.0" - "@typescript-eslint/types" "5.44.0" - "@typescript-eslint/typescript-estree" "5.44.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.44.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.44.0.tgz#988c3f34b45b3474eb9ff0674c18309dedfc3e04" - integrity sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g== +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== dependencies: - "@typescript-eslint/types" "5.44.0" - "@typescript-eslint/visitor-keys" "5.44.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/type-utils@5.44.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.44.0.tgz#bc5a6e8a0269850714a870c9268c038150dfb3c7" - integrity sha512-A1u0Yo5wZxkXPQ7/noGkRhV4J9opcymcr31XQtOzcc5nO/IHN2E2TPMECKWYpM3e6olWEM63fq/BaL1wEYnt/w== +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== dependencies: - "@typescript-eslint/typescript-estree" "5.44.0" - "@typescript-eslint/utils" "5.44.0" + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.44.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.44.0.tgz#f3f0b89aaff78f097a2927fe5688c07e786a0241" - integrity sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ== +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/typescript-estree@5.44.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.44.0.tgz#0461b386203e8d383bb1268b1ed1da9bc905b045" - integrity sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw== +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== dependencies: - "@typescript-eslint/types" "5.44.0" - "@typescript-eslint/visitor-keys" "5.44.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.44.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.44.0.tgz#d733da4d79d6c30f1a68b531cdda1e0c1f00d52d" - integrity sha512-fMzA8LLQ189gaBjS0MZszw5HBdZgVwxVFShCO3QN+ws3GlPkcy9YuS3U4wkT6su0w+Byjq3mS3uamy9HE4Yfjw== +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== dependencies: + "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.44.0" - "@typescript-eslint/types" "5.44.0" - "@typescript-eslint/typescript-estree" "5.44.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" eslint-scope "^5.1.1" - eslint-utils "^3.0.0" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.44.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.44.0.tgz#10740dc28902bb903d12ee3a005cc3a70207d433" - integrity sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ== +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== dependencies: - "@typescript-eslint/types" "5.44.0" + "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" -"@unstoppabledomains/resolution@^8.3.3": - version "8.3.3" - resolved "https://registry.yarnpkg.com/@unstoppabledomains/resolution/-/resolution-8.3.3.tgz#599c5e2f068a36e24bd19c0f9e2f753036264a3b" - integrity sha512-jQ9757Lvx/xqU7Lg7JT4L9WQZuElHkMiJmtph0e6TSwfguIlkv1i5UgzURgVXDvVizVKpDejGS60kuvICkz9ZQ== +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +"@unstoppabledomains/resolution@^8.5.0": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@unstoppabledomains/resolution/-/resolution-8.5.0.tgz#7f65146fb3bc69bfae8699c4ed1c61f3d6f0096c" + integrity sha512-csqExbiK8F5mRKoHlDZjGuIEuvi63O8PSyhUcGhnTi76Il5fCREAGNVdTiRxagPPYoxCO+Xmf6kThwtmiws1Ow== dependencies: "@ethersproject/abi" "^5.0.1" bn.js "^4.4.0" @@ -1285,143 +1576,141 @@ crypto-js "^4.1.1" elliptic "^6.5.4" -"@webassemblyjs/ast@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" - integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" + integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== dependencies: - "@webassemblyjs/helper-numbers" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" -"@webassemblyjs/floating-point-hex-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" - integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== -"@webassemblyjs/helper-api-error@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" - integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== -"@webassemblyjs/helper-buffer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" - integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== +"@webassemblyjs/helper-buffer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" + integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== -"@webassemblyjs/helper-numbers@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" - integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" "@xtuc/long" "4.2.2" -"@webassemblyjs/helper-wasm-bytecode@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" - integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== -"@webassemblyjs/helper-wasm-section@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" - integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== +"@webassemblyjs/helper-wasm-section@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" + integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.12.1" -"@webassemblyjs/ieee754@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" - integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" - integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" - integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== - -"@webassemblyjs/wasm-edit@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" - integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/helper-wasm-section" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-opt" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - "@webassemblyjs/wast-printer" "1.11.1" - -"@webassemblyjs/wasm-gen@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" - integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wasm-opt@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" - integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - -"@webassemblyjs/wasm-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" - integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wast-printer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" - integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== - dependencies: - "@webassemblyjs/ast" "1.11.1" +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" + integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-opt" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wast-printer" "1.12.1" + +"@webassemblyjs/wasm-gen@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" + integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" + integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" + integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" + integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== + dependencies: + "@webassemblyjs/ast" "1.12.1" "@xtuc/long" "4.2.2" -"@webpack-cli/configtest@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" - integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== +"@webpack-cli/configtest@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== -"@webpack-cli/info@^1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" - integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== - dependencies: - envinfo "^7.7.3" +"@webpack-cli/info@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== -"@webpack-cli/serve@^1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" - integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== +"@webpack-cli/serve@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -1446,10 +1735,10 @@ acorn-globals@^7.0.0: 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" - integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== acorn-jsx@^5.3.2: version "5.3.2" @@ -1457,14 +1746,16 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== 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== + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" -acorn@^8.0.4, acorn@^8.1.0, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.1: - version "8.8.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" - integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== +acorn@^8.0.4, acorn@^8.1.0, acorn@^8.11.0, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.12.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== aes-js@3.0.0: version "3.0.0" @@ -1503,14 +1794,14 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv-keywords@^5.0.0: +ajv-keywords@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== dependencies: fast-deep-equal "^3.1.3" -ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1520,20 +1811,20 @@ ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.8.0: - version "8.11.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.2.tgz#aecb20b50607acf2569b6382167b65a96008bb78" - integrity sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg== +ajv@^8.0.0, ajv@^8.9.0: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== dependencies: - fast-deep-equal "^3.1.1" + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" json-schema-traverse "^1.0.0" require-from-string "^2.0.2" - uri-js "^4.2.2" -ansi-colors@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ansi-colors@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== ansi-regex@^5.0.1: version "5.0.1" @@ -1591,6 +1882,14 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + array-from@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" @@ -1601,81 +1900,82 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== -asn1.js@^5.2.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" - integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== +asn1.js@^4.10.1: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== dependencies: bn.js "^4.0.0" inherits "^2.0.1" minimalistic-assert "^1.0.0" - safer-buffer "^2.1.0" assert@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32" - integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== + version "2.1.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd" + integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw== dependencies: - es6-object-assign "^1.1.0" - is-nan "^1.2.1" - object-is "^1.0.1" - util "^0.12.0" + call-bind "^1.0.2" + is-nan "^1.3.2" + object-is "^1.1.5" + object.assign "^4.1.4" + util "^0.12.5" assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== -async-mutex@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.2.tgz#1485eda5bda1b0ec7c8df1ac2e815757ad1831df" - integrity sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA== +async-mutex@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.1.tgz#bccf55b96f2baf8df90ed798cb5544a1f6ee4c2c" + integrity sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA== dependencies: - tslib "^2.3.1" - -async@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + tslib "^2.4.0" asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base-x@3.0.9, base-x@^3.0.2: - version "3.0.9" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" - integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== +base-x@^3.0.2, base-x@^3.0.9: + version "3.0.10" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.10.tgz#62de58653f8762b5d6f8d9fe30fa75f7b2585a75" + integrity sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ== dependencies: safe-buffer "^5.0.1" -base64-arraybuffer-es6@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.7.0.tgz#dbe1e6c87b1bf1ca2875904461a7de40f21abc86" - integrity sha512-ESyU/U1CFZDJUdr+neHRhNozeCv72Y7Vm0m1DCbjX3KBjT6eYocvAJlSk6+8+HkVwXlT1FNxhGW6q3UKAlCvvw== - -base64-js@^1.0.2, base64-js@^1.3.1: +base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -1706,24 +2006,19 @@ big-integer@1.6.36: integrity sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg== big-integer@^1.6.48: - version "1.6.51" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" - integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== - -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + version "1.6.52" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" + integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== bignumber.js@^9.0.0, bignumber.js@^9.0.1: - version "9.1.0" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.0.tgz#8d340146107fe3a6cb8d40699643c302e8773b62" - integrity sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A== + version "9.1.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== bindings@^1.3.0, bindings@^1.5.0: version "1.5.0" @@ -1732,15 +2027,12 @@ bindings@^1.3.0, bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -bip39@^3.0.3: - version "3.0.4" - resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.4.tgz#5b11fed966840b5e1b8539f0f54ab6392969b2a0" - integrity sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw== +bip39@^3.0.4: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.1.0.tgz#c55a418deaf48826a6ceb34ac55b3ee1577e18a3" + integrity sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A== dependencies: - "@types/node" "11.11.6" - create-hash "^1.1.0" - pbkdf2 "^3.0.9" - randombytes "^2.0.1" + "@noble/hashes" "^1.2.0" bip66@^1.1.5: version "1.1.5" @@ -1768,12 +2060,12 @@ blakejs@^1.1.0: resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0: +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.12.0, bn.js@^4.4.0: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: +bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== @@ -1798,24 +2090,31 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" brorand@^1.0.1, brorand@^1.0.5, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== -browser-stdout@1.3.1: +browser-passworder@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/browser-passworder/-/browser-passworder-2.0.3.tgz#6fdd2082e516a176edbcb3dcee0b7f9fce4f7917" + integrity sha512-8mTcGjsVqYkRW0qLmdussBjf/5joBUpvZfR8jUojITBJVVZIS5BL41Qt/xehS+n2ChA2YJHHLPhlkXziK+gvsw== + dependencies: + browserify-unibabel "^3.0.0" + +browser-stdout@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6, browserify-aes@^1.2.0: +browserify-aes@^1.0.4, browserify-aes@^1.0.6, browserify-aes@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== @@ -1846,38 +2145,52 @@ browserify-des@^1.0.0: inherits "^2.0.1" safe-buffer "^5.1.2" -browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" - integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== +browserify-rsa@^4.0.0, browserify-rsa@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.1.tgz#06e530907fe2949dc21fc3c2e2302e10b1437238" + integrity sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ== dependencies: - bn.js "^5.0.0" - randombytes "^2.0.1" + bn.js "^5.2.1" + randombytes "^2.1.0" + safe-buffer "^5.2.1" browserify-sign@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" - integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + version "4.2.3" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.3.tgz#7afe4c01ec7ee59a89a558a4b75bd85ae62d4208" + integrity sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw== dependencies: - bn.js "^5.1.1" - browserify-rsa "^4.0.1" + bn.js "^5.2.1" + browserify-rsa "^4.1.0" create-hash "^1.2.0" create-hmac "^1.1.7" - elliptic "^6.5.3" + elliptic "^6.5.5" + hash-base "~3.0" inherits "^2.0.4" - parse-asn1 "^5.1.5" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" + parse-asn1 "^5.1.7" + readable-stream "^2.3.8" + safe-buffer "^5.2.1" -browserslist@^4.14.5, browserslist@^4.21.3: - version "4.21.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" - integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== +browserify-unibabel@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/browserify-unibabel/-/browserify-unibabel-3.0.0.tgz#5a6b8f0f704ce388d3927df47337e25830f71dda" + integrity sha512-j3MX0k2dC1/DEo9jSUyj7zpv5wLd1+klpFwYlM0E5mr7MX6LblQOWb+jkBEKV2iRszmhJuLHAtNuqranlGnpNQ== + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== dependencies: - caniuse-lite "^1.0.30001400" - electron-to-chromium "^1.4.251" - node-releases "^2.0.6" - update-browserslist-db "^1.0.9" + pako "~1.0.5" + +browserslist@^4.21.10, browserslist@^4.24.0: + version "4.24.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.0.tgz#a1325fe4bc80b64fda169629fc01b3d6cecd38d4" + integrity sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A== + dependencies: + caniuse-lite "^1.0.30001663" + electron-to-chromium "^1.5.28" + node-releases "^2.0.18" + update-browserslist-db "^1.1.0" bs58@^2.0.1: version "2.0.1" @@ -1910,15 +2223,7 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== -buffer@5.6.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" - integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - -buffer@^6.0.3: +buffer@6.0.3, buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== @@ -1926,6 +2231,19 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +buffer@^5.1.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== + bytebuffer@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd" @@ -1943,13 +2261,16 @@ caching-transform@^4.0.0: package-hash "^4.0.0" write-file-atomic "^3.0.0" -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" callsites@^3.0.0: version "3.1.0" @@ -1966,10 +2287,15 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001400: - version "1.0.30001434" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz#ec1ec1cfb0a93a34a0600d37903853030520a4e5" - integrity sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA== +caniuse-lite@^1.0.30001663: + version "1.0.30001667" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz#99fc5ea0d9c6e96897a104a8352604378377f949" + integrity sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw== + +case@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9" + integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== cashaddrjs@0.4.4: version "0.4.4" @@ -1978,25 +2304,30 @@ cashaddrjs@0.4.4: dependencies: big-integer "1.6.36" +cbor-sync@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cbor-sync/-/cbor-sync-1.0.4.tgz#5a11a1ab75c2a14d1af1b237fd84aa8c1593662f" + integrity sha512-GWlXN4wiz0vdWWXBU71Dvc1q3aBo0HytqwAZnXF1wOwjqNnDWA1vZ1gDMFLlqohak31VQzmhiYfiCX5QSSfagA== + cbor-web@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cbor-web/-/cbor-web-7.0.6.tgz#6e23a0c58db4c38e485e395de511b9e2f628961c" integrity sha512-A6ZH12jcDJG9PS7StugO78G+ok23SAjxMugGInPBy4IItiH6hYzybi6HQkGjWw9jBEGQpIBkleB2mizxYZIpLw== -chai@^4.2.0: - version "4.3.7" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" - integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== +chai@^4.3.7: + version "4.5.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.5.0.tgz#707e49923afdd9b13a8b0b47d33d732d13812fd8" + integrity sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw== dependencies: assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^4.1.2" - get-func-name "^2.0.0" - loupe "^2.3.1" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" pathval "^1.1.1" - type-detect "^4.0.5" + type-detect "^4.1.0" -chalk@^2.0.0, chalk@^2.4.1: +chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2013,15 +2344,17 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== +check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" -chokidar@3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== +chokidar@^3.5.3: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== dependencies: anymatch "~3.1.2" braces "~3.0.2" @@ -2034,9 +2367,14 @@ chokidar@3.5.3: fsevents "~2.3.2" chrome-trace-event@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + version "1.0.4" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== + +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" @@ -2079,9 +2417,9 @@ clone-deep@^4.0.1: shallow-clone "^3.0.0" coinstring@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/coinstring/-/coinstring-2.3.0.tgz#cdb63363a961502404a25afb82c2e26d5ff627a4" - integrity sha512-2xMhQ++4ETUPiy2oqOlfydsuQArNLB6TExNF33Jmv+IgpmV8Hf6v6yICQAwH4uEHTnkJ3DscSyeKFrg37ljIOw== + version "2.3.1" + resolved "https://registry.yarnpkg.com/coinstring/-/coinstring-2.3.1.tgz#cfb3b38d47090923aa6fd267a3a000b364d935ee" + integrity sha512-gLvivqtntteG2kOd7jpVQzKbIirJP7ijDEU+boVZTLj6V4tjVLBlUXGlijhBOcoWM7S/epqHVikQCD6x2J+E/Q== dependencies: bs58 "^2.0.1" create-hash "^1.1.1" @@ -2111,9 +2449,9 @@ color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^2.0.14: - version "2.0.19" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== combined-stream@^1.0.8: version "1.0.8" @@ -2122,12 +2460,17 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^7.0.0, commander@^7.2.0: +commander@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== @@ -2137,10 +2480,10 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== -compare-versions@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" - integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== +compare-versions@^6.0.0-rc.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-6.1.1.tgz#7af3cc1099ba37d244b3145a9af5201b629148a9" + integrity sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg== concat-map@0.0.1: version "0.0.1" @@ -2152,16 +2495,23 @@ convert-source-map@^1.7.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== crc-32@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== +crc@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6" + integrity sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ== + dependencies: + buffer "^5.1.0" + create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" @@ -2199,11 +2549,11 @@ create-require@^1.1.0: integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-fetch@^3.1.4, cross-fetch@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== + version "3.1.8" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" + integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== dependencies: - node-fetch "2.6.7" + node-fetch "^2.6.12" cross-spawn@^6.0.5: version "6.0.5" @@ -2243,9 +2593,9 @@ crypto-browserify@^3.12.0: randomfill "^1.0.3" crypto-js@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" - integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== + version "4.2.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== cssom@^0.5.0: version "0.5.0" @@ -2273,12 +2623,44 @@ data-urls@^3.0.2: whatwg-mimetype "^3.0.0" whatwg-url "^11.0.0" -debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +debounce@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" + integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== + +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== dependencies: - ms "2.1.2" + ms "^2.1.3" decamelize@^1.2.0: version "1.2.0" @@ -2291,18 +2673,18 @@ decamelize@^4.0.0: integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== decimal.js@^10.2.0, decimal.js@^10.4.2: - version "10.4.2" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.2.tgz#0341651d1d997d86065a2ce3a441fbd0d8e8b98e" - integrity sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA== + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== -deep-eql@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.2.tgz#270ceb902f87724077e6f6449aed81463f42fc1c" - integrity sha512-gT18+YW4CcW/DBNTwAmqTtkJh7f9qqScu2qFVlx7kCoeY9tlBu9cUcr7+I+Z/noG8INehS3xQgLpTtd/QUTn4w== +deep-eql@^4.1.3: + version "4.1.4" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.4.tgz#d0d3912865911bb8fac5afb4e3acfa6a28dc72b7" + integrity sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg== dependencies: type-detect "^4.0.0" -deep-is@^0.1.3, deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -2314,11 +2696,21 @@ default-require-extensions@^3.0.0: dependencies: strip-bom "^4.0.0" -define-properties@^1.1.3, define-properties@^1.1.4: +define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== dependencies: + define-data-property "^1.0.1" has-property-descriptors "^1.0.0" object-keys "^1.1.1" @@ -2328,28 +2720,28 @@ delayed-stream@~1.0.0: integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== des.js@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" - integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + version "1.1.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" + integrity sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg== dependencies: inherits "^2.0.1" minimalistic-assert "^1.0.0" -diff@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== - diff@^3.1.0, diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -diff@^4.0.1, diff@^4.0.2: +diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +diff@^5.1.0, diff@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" + integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -2373,13 +2765,6 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -domexception@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" - integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== - dependencies: - webidl-conversions "^4.0.2" - domexception@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" @@ -2395,16 +2780,16 @@ dotenv-defaults@^2.0.2: dotenv "^8.2.0" dotenv-webpack@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/dotenv-webpack/-/dotenv-webpack-8.0.1.tgz#6656550460a8076fab20e5ac2eac867e72478645" - integrity sha512-CdrgfhZOnx4uB18SgaoP9XHRN2v48BbjuXQsZY5ixs5A8579NxQkmMxRtI7aTwSiSQcM2ao12Fdu+L3ZS3bG4w== + version "8.1.0" + resolved "https://registry.yarnpkg.com/dotenv-webpack/-/dotenv-webpack-8.1.0.tgz#4d66abc4a30395b46a030ebcd125320232b54873" + integrity sha512-owK1JcsPkIobeqjVrk6h7jPED/W6ZpdFsMPR+5ursB7/SdgDyO+VzAU+szK8C8u3qUhtENyYnj8eyXMR5kkGag== dependencies: dotenv-defaults "^2.0.2" -dotenv@8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" - integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== +dotenv@16.0.3: + version "16.0.3" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" + integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== dotenv@^8.2.0: version "8.6.0" @@ -2425,12 +2810,12 @@ duplexer@^0.1.2: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== -electron-to-chromium@^1.4.251: - version "1.4.284" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" - integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== +electron-to-chromium@^1.5.28: + version "1.5.33" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.33.tgz#8f64698661240e70fdbc4b032e6085e391f05e09" + integrity sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA== -elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: +elliptic@6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== @@ -2443,33 +2828,41 @@ elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5 minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6.5.5: + version "6.5.7" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== - -enhanced-resolve@^5.0.0, enhanced-resolve@^5.10.0: - version "5.12.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" - integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== +enhanced-resolve@^5.0.0, enhanced-resolve@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" entities@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" - integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== envinfo@^7.7.3: - version "7.8.1" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" - integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + version "7.14.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae" + integrity sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg== error-ex@^1.3.1: version "1.3.2" @@ -2478,40 +2871,90 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.20.4" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" - integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== - dependencies: - call-bind "^1.0.2" +es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: + version "1.23.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-property-descriptors "^1.0.0" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" has-symbols "^1.0.3" - internal-slot "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" is-callable "^1.2.7" - is-negative-zero "^2.0.2" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" + is-shared-array-buffer "^1.0.3" is-string "^1.0.7" + is-typed-array "^1.1.13" is-weakref "^1.0.2" - object-inspect "^1.12.2" + object-inspect "^1.13.1" object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" - safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^1.2.1: + version "1.5.4" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" + integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== -es-module-lexer@^0.9.0: - version "0.9.3" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" - integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" es-to-primitive@^1.2.1: version "1.2.1" @@ -2527,47 +2970,41 @@ es6-error@^4.0.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -es6-object-assign@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" - integrity sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw== - es6-promise@^4.2.8: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + 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" - optionator "^0.8.1" optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@^8.3.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" - integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== +eslint-config-prettier@^8.6.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" + integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== eslint-scope@5.1.1, eslint-scope@^5.1.1: version "5.1.1" @@ -2577,85 +3014,72 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint-webpack-plugin@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz#1978cdb9edc461e4b0195a20da950cf57988347c" - integrity sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w== +eslint-webpack-plugin@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-webpack-plugin/-/eslint-webpack-plugin-4.2.0.tgz#41f54b25379908eb9eca8645bc997c90cfdbd34e" + integrity sha512-rsfpFQ01AWQbqtjgPRr2usVRxhWDuG0YDYcG8DJOteD3EFnpeuYuOwk0PQiN7PRBTqS6ElNdtPZPggj8If9WnA== dependencies: - "@types/eslint" "^7.29.0 || ^8.4.1" - jest-worker "^28.0.2" + "@types/eslint" "^8.56.10" + jest-worker "^29.7.0" micromatch "^4.0.5" normalize-path "^3.0.0" - schema-utils "^4.0.0" - -eslint@^8.22.0: - version "8.28.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.28.0.tgz#81a680732634677cc890134bcdd9fdfea8e63d6e" - integrity sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ== - dependencies: - "@eslint/eslintrc" "^1.3.3" - "@humanwhocodes/config-array" "^0.11.6" + schema-utils "^4.2.0" + +eslint@^8.31.0: + version "8.57.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" + integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.1" + "@humanwhocodes/config-array" "^0.13.0" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.4.0" - esquery "^1.4.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" find-up "^5.0.0" glob-parent "^6.0.2" - globals "^13.15.0" - grapheme-splitter "^1.0.4" + globals "^13.19.0" + graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" is-path-inside "^3.0.3" - js-sdsl "^4.1.4" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" - regexpp "^3.2.0" + optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" esm@^3.2.25: @@ -2663,24 +3087,24 @@ esm@^3.2.25: resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== -espree@^9.4.0: - version "9.4.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd" - integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^8.8.0" + acorn "^8.9.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.1" 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== -esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== +esquery@^1.4.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== dependencies: estraverse "^5.1.0" @@ -2762,15 +3186,25 @@ ethereum-cryptography@^0.1.3: secp256k1 "^4.0.1" setimmediate "^1.0.5" -ethereum-cryptography@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz#74f2ac0f0f5fe79f012c889b3b8446a9a6264e6d" - integrity sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ== +ethereum-cryptography@^1.1.2, ethereum-cryptography@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz#5ccfa183e85fdaf9f9b299a79430c044268c9b3a" + integrity sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw== + dependencies: + "@noble/hashes" "1.2.0" + "@noble/secp256k1" "1.7.1" + "@scure/bip32" "1.1.5" + "@scure/bip39" "1.1.1" + +ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz#58f2810f8e020aecb97de8c8c76147600b0b8ccf" + integrity sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg== dependencies: - "@noble/hashes" "1.1.2" - "@noble/secp256k1" "1.6.3" - "@scure/bip32" "1.1.0" - "@scure/bip39" "1.1.0" + "@noble/curves" "1.4.2" + "@noble/hashes" "1.4.0" + "@scure/bip32" "1.4.0" + "@scure/bip39" "1.3.0" ethereumjs-abi@0.6.8, ethereumjs-abi@^0.6.8: version "0.6.8" @@ -2806,7 +3240,7 @@ ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: ethjs-util "0.1.6" rlp "^2.2.3" -ethereumjs-util@^7.0.7, ethereumjs-util@^7.0.9, ethereumjs-util@^7.1.2, ethereumjs-util@^7.1.5: +ethereumjs-util@^7.0.9, ethereumjs-util@^7.1.2, ethereumjs-util@^7.1.5: version "7.1.5" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== @@ -2817,7 +3251,7 @@ ethereumjs-util@^7.0.7, ethereumjs-util@^7.0.9, ethereumjs-util@^7.1.2, ethereum ethereum-cryptography "^0.1.3" rlp "^2.2.4" -ethereumjs-wallet@^1.0.1: +ethereumjs-wallet@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-1.0.2.tgz#2c000504b4c71e8f3782dabe1113d192522e99b6" integrity sha512-CCWV4RESJgRdHIvFciVQFnCHfqyhXWchTPlkfp28Qc53ufs+doi5I/cV2+xeK9+qEo25XCWfP9MiL+WEPAZfdA== @@ -2831,7 +3265,7 @@ ethereumjs-wallet@^1.0.1: utf8 "^3.0.0" uuid "^8.3.2" -ethers@^5.4.0: +ethers@^5.7.0: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -2888,22 +3322,15 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -fake-indexeddb@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-4.0.0.tgz#1dfb2023a3be175e35a6d84975218b432041934d" - integrity sha512-oCfWSJ/qvQn1XPZ8SHX6kY3zr1t+bN7faZ/lltGY0SBGhFOPXnWf0+pbO/MOAgfMx6khC2gK3S/bvAgQpuQHDQ== - dependencies: - realistic-structured-clone "^3.0.0" - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@^3.2.9: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -2916,7 +3343,7 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== @@ -2926,15 +3353,20 @@ fast-safe-stringify@^2.0.6: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== +fast-uri@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.2.tgz#d78b298cf70fd3b752fd951175a3da6a7b48f024" + integrity sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row== + fastest-levenshtein@^1.0.12: version "1.0.16" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== dependencies: reusify "^1.0.4" @@ -2950,10 +3382,10 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" @@ -2966,14 +3398,6 @@ find-cache-dir@^3.2.0: make-dir "^3.0.2" pkg-dir "^4.1.0" -find-up@5.0.0, find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -2982,12 +3406,21 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== dependencies: - flatted "^3.1.0" + flatted "^3.2.9" + keyv "^4.5.3" rimraf "^3.0.2" flat@^5.0.2: @@ -2995,10 +3428,10 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -flatted@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== for-each@^0.3.3: version "0.3.3" @@ -3029,12 +3462,11 @@ fromentries@^1.2.0: resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg== -fs-extra@^9.0.1: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== +fs-extra@^11.1.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== dependencies: - at-least-node "^1.0.0" graceful-fs "^4.2.0" jsonfile "^6.0.1" universalify "^2.0.0" @@ -3045,26 +3477,26 @@ fs.realpath@^1.0.0: integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" -functions-have-names@^1.2.2: +functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -3084,32 +3516,35 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== +get-func-name@^2.0.1, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" - integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== dependencies: - function-bind "^1.1.1" - has "^1.0.3" + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" has-symbols "^1.0.3" + hasown "^2.0.0" get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" @@ -3130,42 +3565,49 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== +glob@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.1.1" + minimatch "^5.0.1" once "^1.3.0" - path-is-absolute "^1.0.0" globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.15.0: - version "13.18.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.18.0.tgz#fb224daeeb2bb7d254cd2c640f003528b8d0c1dc" - integrity sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A== +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== dependencies: type-fest "^0.20.2" +globalthis@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -3185,15 +3627,15 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== gzip-size@^6.0.0: version "6.0.0" @@ -3217,31 +3659,29 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: - get-intrinsic "^1.1.1" + es-define-property "^1.0.0" + +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: - function-bind "^1.1.1" + has-symbols "^1.0.3" hash-base@^3.0.0: version "3.1.0" @@ -3252,6 +3692,14 @@ hash-base@^3.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" +hash-base@~3.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" @@ -3268,6 +3716,13 @@ hasha@^5.0.0: is-stream "^2.0.0" type-fest "^0.8.0" +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + hdkey@0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-0.8.0.tgz#08c9a9fcb6a42d9724d669f81c53a3c507f87bf1" @@ -3277,7 +3732,17 @@ hdkey@0.8.0: safe-buffer "^5.1.1" secp256k1 "^3.0.1" -he@1.2.0: +hdkey@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-2.1.0.tgz#755b30b73f54e93c31919c1b2f19205a8e57cb92" + integrity sha512-i9Wzi0Dy49bNS4tXXeGeu0vIcn86xXdPQUpEYg+SO1YiO8HtomjmmRMaRyqL0r59QfcD4PfVbSF3qmsWFwAemA== + dependencies: + bs58check "^2.1.2" + ripemd160 "^2.0.2" + safe-buffer "^5.1.1" + secp256k1 "^4.0.0" + +he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== @@ -3303,7 +3768,7 @@ html-encoding-sniffer@^3.0.0: dependencies: whatwg-encoding "^2.0.0" -html-escaper@^2.0.0: +html-escaper@^2.0.0, html-escaper@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== @@ -3317,6 +3782,11 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" +https-browserify@^1.0.0: + version "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.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -3339,17 +3809,17 @@ idna-uts46-hx@^2.3.1: dependencies: punycode "2.1.0" -ieee754@^1.1.4, ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.1.tgz#c2b1f76cb999ede1502f3a226a9310fdfe88d46c" - integrity sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA== + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -3358,9 +3828,9 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: resolve-from "^4.0.0" import-local@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" - integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" @@ -3383,7 +3853,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -3393,19 +3863,19 @@ int64-buffer@^1.0.1: resolved "https://registry.yarnpkg.com/int64-buffer/-/int64-buffer-1.0.1.tgz#c78d841b444cadf036cd04f8683696c740f15dca" integrity sha512-+3azY4pXrjAupJHU1V9uGERWlhoqNswJNji6aD/02xac7oxol508AsMC5lxKhEqyZeDFy3enq5OGWXF4u75hiw== -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== +internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== dependencies: - get-intrinsic "^1.1.0" - has "^1.0.3" + es-errors "^1.3.0" + hasown "^2.0.0" side-channel "^1.0.4" -interpret@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" - integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== is-arguments@^1.0.4: version "1.1.1" @@ -3415,6 +3885,14 @@ is-arguments@^1.0.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3447,12 +3925,19 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== +is-core-module@^2.13.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== + dependencies: + hasown "^2.0.2" + +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== dependencies: - has "^1.0.3" + is-typed-array "^1.1.13" is-date-object@^1.0.1: version "1.0.5" @@ -3490,7 +3975,7 @@ is-hex-prefixed@1.0.0: resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== -is-nan@^1.2.1: +is-nan@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== @@ -3498,10 +3983,10 @@ is-nan@^1.2.1: call-bind "^1.0.0" define-properties "^1.1.3" -is-negative-zero@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== is-number-object@^1.0.4: version "1.0.7" @@ -3550,12 +4035,12 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-shared-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" - integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== dependencies: - call-bind "^1.0.2" + call-bind "^1.0.7" is-stream@^2.0.0: version "2.0.1" @@ -3576,16 +4061,12 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.10, is-typed-array@^1.1.3: - version "1.1.10" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== +is-typed-array@^1.1.13, is-typed-array@^1.1.3: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" + which-typed-array "^1.1.14" is-typedarray@^1.0.0: version "1.0.0" @@ -3614,10 +4095,10 @@ isarray@0.0.1: resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== isexe@^2.0.0: version "2.0.0" @@ -3638,9 +4119,9 @@ isomorphic-fetch@^3.0.0: whatwg-fetch "^3.4.1" istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" - integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== istanbul-lib-hook@^3.0.0: version "3.0.0" @@ -3672,12 +4153,12 @@ istanbul-lib-processinfo@^2.0.2: uuid "^8.3.2" istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== dependencies: istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" + make-dir "^4.0.0" supports-color "^7.1.0" istanbul-lib-source-maps@^4.0.0: @@ -3690,13 +4171,25 @@ istanbul-lib-source-maps@^4.0.0: source-map "^0.6.1" istanbul-reports@^3.0.2: - version "3.1.5" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" - integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +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-worker@^27.4.5: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" @@ -3706,20 +4199,16 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^28.0.2: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" - integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: "@types/node" "*" + jest-util "^29.7.0" merge-stream "^2.0.0" supports-color "^8.0.0" -js-sdsl@^4.1.4: - version "4.2.0" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.2.0.tgz#278e98b7bea589b8baaf048c20aeb19eb7ad09d0" - integrity sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ== - js-sha3@0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -3735,13 +4224,6 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@4.1.0, js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" @@ -3750,12 +4232,24 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsbi@^3.1.5: + version "3.2.5" + resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.2.5.tgz#b37bb90e0e5c2814c1c2a1bcd8c729888a2e37d6" + integrity sha512-aBE4n43IPvjaddScbvWRA2YlTzKEynHzu7MqOyTipdHucf/VxS63ViCjxYRg86M8Rxwbt/GfzHl1kKERkt45fQ== + jsdom-global@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/jsdom-global/-/jsdom-global-3.0.2.tgz#6bd299c13b0c4626b2da2c0393cd4385d606acb9" integrity sha512-t1KMcBkz/pT5JrvcJbpUR2u/w1kO9jXctaaGJ0vZDzwFnIvGWw9IDSRciT83kIs8Bnw4qpOl8bQK08V01YgMPg== -jsdom@^20.0.0: +jsdom@^20.0.3: version "20.0.3" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== @@ -3787,10 +4281,15 @@ jsdom@^20.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" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== json-parse-better-errors@^1.0.1: version "1.0.2" @@ -3818,23 +4317,26 @@ json-stable-stringify-without-jsonify@^1.0.1: integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json-stable-stringify@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz#e06f23128e0bbe342dc996ed5a19e28b57b580e0" - integrity sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g== + version "1.1.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz#52d4361b47d49168bcc4e564189a42e5a7439454" + integrity sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg== dependencies: + call-bind "^1.0.5" + isarray "^2.0.5" jsonify "^0.0.1" + object-keys "^1.1.1" -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" -json5@^2.1.2, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonfile@^6.0.1: version "6.1.0" @@ -3860,15 +4362,27 @@ just-extend@^4.0.2: resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== +just-extend@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-6.2.0.tgz#b816abfb3d67ee860482e7401564672558163947" + integrity sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw== + keccak@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" - integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== + version "3.0.4" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" + integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== dependencies: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" readable-stream "^3.6.0" +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" @@ -3882,14 +4396,6 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -3905,14 +4411,10 @@ loader-runner@^4.2.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== -loader-utils@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" - integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^2.1.2" +loader-utils@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.3.1.tgz#735b9a19fd63648ca7adbd31c2327dfe281304e5" + integrity sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg== locate-path@^5.0.0: version "5.0.0" @@ -3943,12 +4445,12 @@ 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.16.3, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: +lodash@^4.16.3, lodash@^4.17.15, 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== -log-symbols@4.1.0: +log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -3956,10 +4458,10 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -loglevel@^1.7.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" - integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== +loglevel@^1.8.1: + version "1.9.2" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.9.2.tgz#c2e028d6c757720107df4e64508530db6621ba08" + integrity sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg== lolex@^4.2.0: version "4.2.0" @@ -3983,19 +4485,19 @@ long@~3: resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" integrity sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg== -loupe@^2.3.1: - version "2.3.6" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" - integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== +loupe@^2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" + integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== dependencies: - get-func-name "^2.0.0" + get-func-name "^2.0.1" -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: - yallist "^4.0.0" + yallist "^3.0.2" make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" @@ -4004,6 +4506,13 @@ make-dir@^3.0.0, make-dir@^3.0.2: dependencies: semver "^6.0.0" +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -4033,12 +4542,17 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +micro-ftch@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/micro-ftch/-/micro-ftch-0.3.1.tgz#6cb83388de4c1f279a034fb0cf96dfc050853c5f" + integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg== + micromatch@^4.0.0, micromatch@^4.0.4, micromatch@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: - braces "^3.0.2" + braces "^3.0.3" picomatch "^2.3.1" miller-rabin@^4.0.0: @@ -4071,13 +4585,6 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -minimatch@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" - integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== - dependencies: - brace-expansion "^2.0.1" - minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -4085,10 +4592,17 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1, minimatch@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.6: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== mkdirp@^0.5.1: version "0.5.6" @@ -4097,32 +4611,31 @@ mkdirp@^0.5.1: dependencies: minimist "^1.2.6" -mocha@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.1.0.tgz#dbf1114b7c3f9d0ca5de3133906aea3dfc89ef7a" - integrity sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg== - dependencies: - ansi-colors "4.1.1" - browser-stdout "1.3.1" - chokidar "3.5.3" - debug "4.3.4" - diff "5.0.0" - escape-string-regexp "4.0.0" - find-up "5.0.0" - glob "7.2.0" - he "1.2.0" - js-yaml "4.1.0" - log-symbols "4.1.0" - minimatch "5.0.1" - ms "2.1.3" - nanoid "3.3.3" - serialize-javascript "6.0.0" - strip-json-comments "3.1.1" - supports-color "8.1.1" - workerpool "6.2.1" - yargs "16.2.0" - yargs-parser "20.2.4" - yargs-unparser "2.0.0" +mocha@^10.2.0: + version "10.7.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.3.tgz#ae32003cabbd52b59aece17846056a68eb4b0752" + integrity sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A== + dependencies: + ansi-colors "^4.1.3" + browser-stdout "^1.3.1" + chokidar "^3.5.3" + debug "^4.3.5" + diff "^5.2.0" + escape-string-regexp "^4.0.0" + find-up "^5.0.0" + glob "^8.1.0" + he "^1.2.0" + js-yaml "^4.1.0" + log-symbols "^4.1.0" + minimatch "^5.1.6" + ms "^2.1.3" + serialize-javascript "^6.0.2" + strip-json-comments "^3.1.1" + supports-color "^8.1.1" + workerpool "^6.5.1" + yargs "^16.2.0" + yargs-parser "^20.2.9" + yargs-unparser "^2.0.0" mock-require@^3.0.3: version "3.0.3" @@ -4132,30 +4645,20 @@ mock-require@^3.0.3: get-caller-file "^1.0.2" normalize-path "^2.1.1" -mrmime@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" - integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +mrmime@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" + integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== -ms@2.1.3: +ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== nan@^2.13.2, nan@^2.14.0: - version "2.17.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" - integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== - -nanoid@3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" - integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== + version "2.20.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3" + integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw== natural-compare-lite@^1.4.0: version "1.4.0" @@ -4188,16 +4691,16 @@ nise@^1.5.2: lolex "^5.0.1" path-to-regexp "^1.7.0" -nise@^4.0.4: - version "4.1.0" - resolved "https://registry.yarnpkg.com/nise/-/nise-4.1.0.tgz#8fb75a26e90b99202fa1e63f448f58efbcdedaf6" - integrity sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA== +nise@^5.1.4: + version "5.1.9" + resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.9.tgz#0cb73b5e4499d738231a473cd89bd8afbb618139" + integrity sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww== dependencies: - "@sinonjs/commons" "^1.7.0" - "@sinonjs/fake-timers" "^6.0.0" - "@sinonjs/text-encoding" "^0.7.1" - just-extend "^4.0.2" - path-to-regexp "^1.7.0" + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers" "^11.2.2" + "@sinonjs/text-encoding" "^0.7.2" + just-extend "^6.2.0" + path-to-regexp "^6.2.1" node-addon-api@^2.0.0: version "2.0.2" @@ -4209,17 +4712,17 @@ node-addon-api@^3.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== -node-fetch@2.6.7, node-fetch@^2.6.1: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== +node-fetch@^2.6.1, 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.2.0, node-gyp-build@^4.2.2: - version "4.5.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + version "4.8.2" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.2.tgz#4f802b71c1ab2ca16af830e6c1ea7dd1ad9496fa" + integrity sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw== node-preload@^0.2.1: version "0.2.1" @@ -4228,10 +4731,10 @@ node-preload@^0.2.1: dependencies: process-on-spawn "^1.0.0" -node-releases@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" - integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== normalize-package-data@^2.3.2: version "2.5.0" @@ -4271,9 +4774,9 @@ npm-run-all@^4.1.5: string.prototype.padend "^3.0.0" nwsapi@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" - integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== + version "2.2.13" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.13.tgz#e56b4e98960e7a040e5474536587e599c4ff4655" + integrity sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ== nyc@^15.1.0: version "15.1.0" @@ -4308,31 +4811,31 @@ nyc@^15.1.0: test-exclude "^6.0.0" yargs "^15.0.2" -object-inspect@^1.12.2, object-inspect@^1.9.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== -object-is@^1.0.1: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== +object-is@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" + integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" + call-bind "^1.0.7" + define-properties "^1.2.1" object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" - integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== +object.assign@^4.1.4, object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" + call-bind "^1.0.5" + define-properties "^1.2.1" has-symbols "^1.0.3" object-keys "^1.1.1" @@ -4358,29 +4861,17 @@ opener@^1.5.2: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== dependencies: deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" + word-wrap "^1.2.5" p-limit@^2.2.0: version "2.3.0" @@ -4432,6 +4923,11 @@ package-hash@^4.0.0: lodash.flattendeep "^4.4.0" release-zalgo "^1.0.0" +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -4439,16 +4935,17 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-asn1@^5.0.0, parse-asn1@^5.1.5: - version "5.1.6" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" - integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== +parse-asn1@^5.0.0, parse-asn1@^5.1.7: + version "5.1.7" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.7.tgz#73cdaaa822125f9647165625eb45f8a051d2df06" + integrity sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg== dependencies: - asn1.js "^5.2.0" - browserify-aes "^1.0.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" + asn1.js "^4.10.1" + browserify-aes "^1.2.0" + evp_bytestokey "^1.0.3" + hash-base "~3.0" + pbkdf2 "^3.1.2" + safe-buffer "^5.2.1" parse-json@^4.0.0: version "4.0.0" @@ -4459,9 +4956,9 @@ parse-json@^4.0.0: json-parse-better-errors "^1.0.1" parse-uri@^1.0.3: - version "1.0.7" - resolved "https://registry.yarnpkg.com/parse-uri/-/parse-uri-1.0.7.tgz#287629a09328a97e398468f21b8a00c4a2d9cc73" - integrity sha512-eWuZCMKNlVkXrEoANdXxbmqhu2SQO9jUMCSpdbJDObin0JxISn6e400EWsSRbr/czdKvWKkhZnMKEGUwf/Plmg== + version "1.0.9" + resolved "https://registry.yarnpkg.com/parse-uri/-/parse-uri-1.0.9.tgz#813c2a1107f9e833a12490cb1cb0408a67294b8f" + integrity sha512-YZfRHHkEZa6qTfPF/xgZ1ErQYCABfud/Vcqp1Q1GNa7RKwv6Oe0YaxXfQQMnQsGdNTo3fwaT0GbVEX7dMAr7tw== parse5@^7.1.1: version "7.1.2" @@ -4496,12 +4993,17 @@ path-parse@^1.0.7: integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + version "1.9.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.9.0.tgz#5dc0753acbf8521ca2e0f137b4578b917b10cf24" + integrity sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g== dependencies: isarray "0.0.1" +path-to-regexp@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4" + integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ== + path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -4519,7 +5021,7 @@ pathval@^1.1.1: resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== -pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.0.9: +pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== @@ -4530,12 +5032,12 @@ pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.0.9: safe-buffer "^5.0.1" sha.js "^2.4.8" -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.0.0, picocolors@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" + integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -4557,25 +5059,25 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pony-cause@^2.1.10: + version "2.1.11" + resolved "https://registry.yarnpkg.com/pony-cause/-/pony-cause-2.1.11.tgz#d69a20aaccdb3bdb8f74dd59e5c68d8e6772e4bd" + integrity sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg== + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== - -prettier@^2.7.1: - version "2.8.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.0.tgz#c7df58393c9ba77d6fba3921ae01faf994fb9dc9" - integrity sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA== - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +prettier@^2.8.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== process-on-spawn@^1.0.0: version "1.0.0" @@ -4585,9 +5087,9 @@ process-on-spawn@^1.0.0: fromentries "^1.2.0" protobufjs@^6.11.2: - version "6.11.3" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" - integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== + version "6.11.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" + integrity sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -4625,10 +5127,15 @@ punycode@2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" integrity sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA== +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== pushdata-bitcoin@^1.0.1: version "1.0.1" @@ -4637,6 +5144,13 @@ pushdata-bitcoin@^1.0.1: dependencies: bitcoin-ops "^1.3.0" +qs@^6.12.3: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + dependencies: + side-channel "^1.0.6" + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" @@ -4671,23 +5185,10 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -readable-stream@^2.2.2, readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.5.0, readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== +readable-stream@^2.2.2, readable-stream@^2.3.8, readable-stream@^3.5.0, readable-stream@^3.6.0, readable-stream@~2.3.6: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -4700,40 +5201,27 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -realistic-structured-clone@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/realistic-structured-clone/-/realistic-structured-clone-3.0.0.tgz#7b518049ce2dad41ac32b421cd297075b00e3e35" - integrity sha512-rOjh4nuWkAqf9PWu6JVpOWD4ndI+JHfgiZeMmujYcPi+fvILUu7g6l26TC1K5aBIp34nV+jE1cDO75EKOfHC5Q== - dependencies: - domexception "^1.0.1" - typeson "^6.1.0" - typeson-registry "^1.0.0-alpha.20" - -rechoir@^0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" - integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== dependencies: - resolve "^1.9.0" + resolve "^1.20.0" -regenerator-runtime@^0.13.11: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== -regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== +regexp.prototype.flags@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42" + integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" - -regexpp@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + call-bind "^1.0.7" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.2" release-zalgo@^1.0.0: version "1.0.0" @@ -4784,12 +5272,12 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve@^1.10.0, resolve@^1.9.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== +resolve@^1.10.0, resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -4805,7 +5293,7 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -ripemd160@^2.0.0, ripemd160@^2.0.1: +ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== @@ -4813,36 +5301,36 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -ripple-address-codec@^4.1.1, ripple-address-codec@^4.2.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-4.2.4.tgz#a56c2168c8bb81269ea4d15ed96d6824c5a866f8" - integrity sha512-roAOjKz94+FboTItey1XRh5qynwt4xvfBLvbbcx+FiR94Yw2x3LrKLF2GVCMCSAh5I6PkcpADg6AbYsUbGN3nA== +ripple-address-codec@^4.1.1, ripple-address-codec@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-4.3.1.tgz#68fbaf646bb8567f70743af7f1ce4479f73efbf6" + integrity sha512-Qa3+9wKVvpL/xYtT6+wANsn0A1QcC5CT6IMZbRJZ/1lGt7gmwIfsrCuz1X0+LCEO7zgb+3UT1I1dc0k/5dwKQQ== dependencies: - base-x "3.0.9" + base-x "^3.0.9" create-hash "^1.1.2" ripple-binary-codec@^1.1.3: - version "1.4.2" - resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-1.4.2.tgz#cdc35353e4bc7c3a704719247c82b4c4d0b57dd3" - integrity sha512-EDKIyZMa/6Ay/oNgCwjD9b9CJv0zmBreeHVQeG4BYwy+9GPnIQjNeT5e/aB6OjAnhcmpgbPeBmzwmNVwzxlt0w== + version "1.11.0" + resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-1.11.0.tgz#d99c848c51a19746b738785001fb7208704bfe30" + integrity sha512-g7+gs3T+NfoeW6vIq5dcN0CkIT4t/zwRzFxz8X2RzfbrWRnewPUKqQbmBgs05tXLX5NuWPaneiaAVpFpYBcdfw== dependencies: assert "^2.0.0" big-integer "^1.6.48" - buffer "5.6.0" + buffer "6.0.3" create-hash "^1.2.0" decimal.js "^10.2.0" - ripple-address-codec "^4.2.4" + ripple-address-codec "^4.3.1" ripple-keypairs@^1.0.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/ripple-keypairs/-/ripple-keypairs-1.1.4.tgz#4486fca703b8a2fc4f30cfd568478f3d12c1a911" - integrity sha512-PMMjTOxZmCSBOvHPj6bA+V/HGx7oFgDtGGI8VcZYuaFO2H87UX0X0jhfHy+LA2Xy31WYlD7GaDIDDt2QO+AMtw== + version "1.3.1" + resolved "https://registry.yarnpkg.com/ripple-keypairs/-/ripple-keypairs-1.3.1.tgz#7fa531df36b138134afb53555a87d7f5eb465b2e" + integrity sha512-dmPlraWKJciFJxHcoubDahGnoIalG5e/BtV6HNDUs7wLXmtnLMHt6w4ed9R8MTL2zNrVPiIdI/HCtMMo0Tm7JQ== dependencies: bn.js "^5.1.1" brorand "^1.0.5" elliptic "^6.5.4" hash.js "^1.0.3" - ripple-address-codec "^4.2.4" + ripple-address-codec "^4.3.1" ripple-lib-transactionparser@0.8.2: version "0.8.2" @@ -4869,13 +5357,18 @@ ripple-lib@1.10.0: ripple-lib-transactionparser "0.8.2" ws "^7.2.0" -rlp@^2.0.0, rlp@^2.2.3, rlp@^2.2.4: +rlp@^2.0.0, rlp@^2.2.3, rlp@^2.2.4, rlp@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== dependencies: bn.js "^5.2.0" +rlp@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-3.0.0.tgz#5a60725ca4314a3a165feecca1836e4f2c1e2343" + integrity sha512-PD6U2PGk6Vq2spfgiWZdomLvRGDreBLxi5jv5M8EpRo3pU6VEm31KO+HFxE18Q3vgqfDrQ9pZA3FP95rkijNKw== + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -4884,20 +5377,25 @@ run-parallel@^1.1.9: queue-microtask "^1.2.2" runtypes@^6.5.1: - version "6.6.0" - resolved "https://registry.yarnpkg.com/runtypes/-/runtypes-6.6.0.tgz#48e353d8b0f641ab5ec5d80fa96dd7bd41ea3281" - integrity sha512-ddM7sgB3fyboDlBzEYFQ04L674sKjbs4GyW2W32N/5Ae47NRd/GyMASPC2PFw8drPHYGEcZ0mZ26r5RcB8msfQ== + version "6.7.0" + resolved "https://registry.yarnpkg.com/runtypes/-/runtypes-6.7.0.tgz#5ffc7e3a51142639d8a8e0155ef4bb8c2ae4c54b" + integrity sha512-3TLdfFX8YHNFOhwHrSJza6uxVBmBrEjnNQlNXvXCdItS0Pdskfg5vVXUTWIN+Y23QR09jWpSl99UHkA83m4uWA== + +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - safe-event-emitter@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" @@ -4905,16 +5403,16 @@ safe-event-emitter@^1.0.1: dependencies: events "^3.0.0" -safe-regex-test@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" - integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" + call-bind "^1.0.6" + es-errors "^1.3.0" is-regex "^1.1.4" -"safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.0: +"safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -4926,28 +5424,28 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" -schema-utils@^3.0.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" - integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== +schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== dependencies: "@types/json-schema" "^7.0.8" ajv "^6.12.5" ajv-keywords "^3.5.2" -schema-utils@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" - integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== +schema-utils@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== dependencies: "@types/json-schema" "^7.0.9" - ajv "^8.8.0" + ajv "^8.9.0" ajv-formats "^2.1.1" - ajv-keywords "^5.0.0" + ajv-keywords "^5.1.0" "schema-validator@git+https://github.com/block-wallet/schema-validator.git": version "1.0.0" - resolved "git+https://github.com/block-wallet/schema-validator.git#e16bdd27daa38453f09674dd8fd01ea49a508c8a" + resolved "git+https://github.com/block-wallet/schema-validator.git#6630206a816c8074e22ebb730f7a62c66959705c" scrypt-js@3.0.1, scrypt-js@^3.0.0, scrypt-js@^3.0.1: version "3.0.1" @@ -4968,7 +5466,7 @@ secp256k1@^3.0.1: nan "^2.14.0" safe-buffer "^5.1.2" -secp256k1@^4.0.1: +secp256k1@^4.0.0, secp256k1@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== @@ -4978,26 +5476,24 @@ secp256k1@^4.0.1: node-gyp-build "^4.2.0" "semver@2 || 3 || 4 || 5", semver@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.4, semver@^7.3.7: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== - dependencies: - lru-cache "^6.0.0" +semver@^7.3.4, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== -serialize-javascript@6.0.0, serialize-javascript@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" - integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== +serialize-javascript@^6.0.1, serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" @@ -5006,6 +5502,28 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + set-value@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/set-value/-/set-value-4.1.0.tgz#aa433662d87081b75ad88a4743bd450f044e7d09" @@ -5019,7 +5537,7 @@ setimmediate@^1.0.5: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== -sha.js@^2.4.0, sha.js@^2.4.8: +sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== @@ -5059,18 +5577,19 @@ shebang-regex@^3.0.0: integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@^1.6.1: - version "1.7.4" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.4.tgz#33fe15dee71ab2a81fcbd3a52106c5cfb9fb75d8" - integrity sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw== + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== +side-channel@^1.0.4, side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" signal-exit@^3.0.2: version "3.0.7" @@ -5086,6 +5605,18 @@ sinon-chrome@^3.0.1: sinon "^7.2.3" urijs "^1.18.2" +sinon@^15.0.1: + version "15.2.0" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-15.2.0.tgz#5e44d4bc5a9b5d993871137fd3560bebfac27565" + integrity sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers" "^10.3.0" + "@sinonjs/samsam" "^8.0.0" + diff "^5.1.0" + nise "^5.1.4" + supports-color "^7.2.0" + sinon@^7.2.3: version "7.5.0" resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.5.0.tgz#e9488ea466070ea908fd44a3d6478fd4923c67ec" @@ -5099,26 +5630,14 @@ sinon@^7.2.3: nise "^1.5.2" supports-color "^5.5.0" -sinon@^9.2.3: - version "9.2.4" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b" - integrity sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg== - dependencies: - "@sinonjs/commons" "^1.8.1" - "@sinonjs/fake-timers" "^6.0.1" - "@sinonjs/samsam" "^5.3.1" - diff "^4.0.2" - nise "^4.0.4" - supports-color "^7.1.0" - -sirv@^1.0.7: - version "1.0.19" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" - integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== +sirv@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0" + integrity sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ== dependencies: - "@polka/url" "^1.0.0-next.20" - mrmime "^1.0.0" - totalist "^1.0.0" + "@polka/url" "^1.0.0-next.24" + mrmime "^2.0.0" + totalist "^3.0.0" slash@^3.0.0: version "3.0.0" @@ -5138,6 +5657,11 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +source-map@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + spawn-wrap@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-2.0.0.tgz#103685b8b8f9b79771318827aa78650a610d457e" @@ -5151,17 +5675,17 @@ spawn-wrap@^2.0.0: which "^2.0.1" spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + version "2.5.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== spdx-expression-parse@^3.0.0: version "3.0.1" @@ -5172,9 +5696,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.12" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz#69077835abe2710b65f03969898b6637b505a779" - integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA== + version "3.0.20" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz#e44ed19ed318dd1e5888f93325cee800f0f51b89" + integrity sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw== sprintf-js@~1.0.2: version "1.0.3" @@ -5189,6 +5713,16 @@ stream-browserify@^3.0.0: inherits "~2.0.4" readable-stream "^3.5.0" +stream-http@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.2.0.tgz#1872dfcf24cb15752677e40e5c3f9cc1926028b5" + integrity sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.4" + readable-stream "^3.6.0" + xtend "^4.0.2" + string-width@^4.1.0, string-width@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -5199,31 +5733,42 @@ string-width@^4.1.0, string-width@^4.2.0: strip-ansi "^6.0.1" string.prototype.padend@^3.0.0: - version "3.1.4" - resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.4.tgz#2c43bb3a89eb54b6750de5942c123d6c98dd65b6" - integrity sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw== + version "3.1.6" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz#ba79cf8992609a91c872daa47c6bb144ee7f62a5" + integrity sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" -string.prototype.trimend@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" - integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" -string.prototype.trimstart@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" - integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" string_decoder@^1.1.1: version "1.3.0" @@ -5232,13 +5777,6 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -5263,17 +5801,15 @@ strip-hex-prefix@1.0.0: dependencies: is-hex-prefixed "1.0.0" -strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -supports-color@8.1.1, supports-color@^8.0.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" +superstruct@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-1.0.4.tgz#0adb99a7578bd2f1c526220da6571b2d485d91ca" + integrity sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ== supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" @@ -5282,13 +5818,20 @@ supports-color@^5.3.0, supports-color@^5.5.0: dependencies: has-flag "^3.0.0" -supports-color@^7.1.0: +supports-color@^7.1.0, supports-color@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" +supports-color@^8.0.0, supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -5304,24 +5847,24 @@ tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -terser-webpack-plugin@^5.1.3: - version "5.3.6" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c" - integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ== +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== dependencies: - "@jridgewell/trace-mapping" "^0.3.14" + "@jridgewell/trace-mapping" "^0.3.20" jest-worker "^27.4.5" schema-utils "^3.1.1" - serialize-javascript "^6.0.0" - terser "^5.14.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" -terser@^5.14.1: - version "5.16.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.0.tgz#29362c6f5506e71545c73b069ccd199bb28f7f54" - integrity sha512-KjTV81QKStSfwbNiwlBXfcgMcOloyuRdb62/iLFPGBcVNF4EXjhdYBhYHmbJpiBrVxZhDvltE11j+LBQUxEEJg== +terser@^5.26.0: + version "5.34.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.34.1.tgz#af40386bdbe54af0d063e0670afd55c3105abeb6" + integrity sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA== dependencies: - "@jridgewell/source-map" "^0.3.2" - acorn "^8.5.0" + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" commander "^2.20.0" source-map-support "~0.5.20" @@ -5377,28 +5920,21 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -totalist@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" - integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== tough-cookie@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" - integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== + version "4.1.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== dependencies: psl "^1.1.33" punycode "^2.1.1" universalify "^0.2.0" url-parse "^1.5.3" -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== - dependencies: - punycode "^2.1.1" - tr46@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" @@ -5430,15 +5966,16 @@ trezor-connect@8.2.6-extended: parse-uri "^1.0.3" tiny-worker "^2.3.0" -ts-loader@^9.3.1: - version "9.4.1" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.4.1.tgz#b6f3d82db0eac5a8295994f8cb5e4940ff6b1060" - integrity sha512-384TYAqGs70rn9F0VBnh6BPTfhga7yFNdC5gXbQpDrBj9/KsT4iRkGqKXhziofHOlE2j6YEaiTYVGKKvPhGWvw== +ts-loader@^9.4.2: + version "9.5.1" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.1.tgz#63d5912a86312f1fbe32cef0859fb8b2193d9b89" + integrity sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg== dependencies: chalk "^4.1.0" enhanced-resolve "^5.0.0" micromatch "^4.0.0" semver "^7.3.4" + source-map "^0.7.4" ts-mocha@^10.0.0: version "10.0.0" @@ -5464,9 +6001,9 @@ ts-node@7.0.1: yn "^2.0.0" ts-node@^10.9.1: - version "10.9.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" - integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== dependencies: "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" @@ -5488,12 +6025,12 @@ ts-pnp@1.2.0: integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== tsconfig-paths@^3.5.0: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== dependencies: "@types/json5" "^0.0.29" - json5 "^1.0.1" + json5 "^1.0.2" minimist "^1.2.6" strip-bom "^3.0.0" @@ -5502,10 +6039,10 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.3.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== +tslib@^2.3.0, tslib@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== tsutils@^3.21.0: version "3.21.0" @@ -5531,18 +6068,16 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== - dependencies: - prelude-ls "~1.1.2" - -type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: +type-detect@4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-detect@^4.0.0, type-detect@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c" + integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== + type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" @@ -5553,6 +6088,50 @@ type-fest@^0.8.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -5565,24 +6144,10 @@ typeforce@^1.18.0: resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== -typescript@^4.0.3: - version "4.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db" - integrity sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA== - -typeson-registry@^1.0.0-alpha.20: - version "1.0.0-alpha.39" - resolved "https://registry.yarnpkg.com/typeson-registry/-/typeson-registry-1.0.0-alpha.39.tgz#9e0f5aabd5eebfcffd65a796487541196f4b1211" - integrity sha512-NeGDEquhw+yfwNhguLPcZ9Oj0fzbADiX4R0WxvoY8nGhy98IbzQy1sezjoEFWOywOboj/DWehI+/aUlRVrJnnw== - dependencies: - base64-arraybuffer-es6 "^0.7.0" - typeson "^6.0.0" - whatwg-url "^8.4.0" - -typeson@^6.0.0, typeson@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/typeson/-/typeson-6.1.0.tgz#5b2a53705a5f58ff4d6f82f965917cabd0d7448b" - integrity sha512-6FTtyGr8ldU0pfbvW/eOZrEtEkczHRUtduBnA90Jh9kMPCiFNnXIon3vF41N0S4tV1HHQt4Hk1j4srpESziCaA== +typescript@^4.9.4: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== unbox-primitive@^1.0.2: version "1.0.2" @@ -5594,23 +6159,28 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + 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" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== -update-browserslist-db@^1.0.9: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== +update-browserslist-db@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" + escalade "^3.2.0" + picocolors "^1.1.0" uri-js@^4.2.2: version "4.4.1" @@ -5632,17 +6202,25 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +url@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.4.tgz#adca77b3562d56b72746e76b330b7f27b6721f3c" + integrity sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg== + dependencies: + punycode "^1.4.1" + qs "^6.12.3" + utf8@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -util@^0.12.0: +util@^0.12.5: version "0.12.5" resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== @@ -5658,6 +6236,11 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0, uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -5685,73 +6268,73 @@ w3c-xmlserializer@^4.0.0: dependencies: xml-name-validator "^4.0.0" -watchpack@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" - integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== +watchpack@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" + integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" +webextension-polyfill@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz#ccb28101c910ba8cf955f7e6a263e662d744dbb8" + integrity sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== - 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.6.1: - version "4.7.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz#33c1c485a7fcae8627c547b5c3328b46de733c66" - integrity sha512-j9b8ynpJS4K+zfO5GGwsAcQX4ZHpWV+yRiHDiL+bE0XHJ8NiPYLTNVQdlFYWxtpg9lfAQNlwJg16J9AJtFSXRg== +webpack-bundle-analyzer@^4.7.0: + version "4.10.2" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz#633af2862c213730be3dbdf40456db171b60d5bd" + integrity sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw== dependencies: + "@discoveryjs/json-ext" "0.5.7" acorn "^8.0.4" acorn-walk "^8.0.0" - chalk "^4.1.0" commander "^7.2.0" + debounce "^1.2.1" + escape-string-regexp "^4.0.0" gzip-size "^6.0.0" - lodash "^4.17.20" + html-escaper "^2.0.2" opener "^1.5.2" - sirv "^1.0.7" + picocolors "^1.0.0" + sirv "^2.0.3" ws "^7.3.1" -webpack-cli@^4.10.0: - version "4.10.0" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" - integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== +webpack-cli@^5.0.1: + version "5.1.4" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== dependencies: "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^1.2.0" - "@webpack-cli/info" "^1.5.0" - "@webpack-cli/serve" "^1.7.0" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" colorette "^2.0.14" - commander "^7.0.0" + commander "^10.0.1" cross-spawn "^7.0.3" + envinfo "^7.7.3" fastest-levenshtein "^1.0.12" import-local "^3.0.2" - interpret "^2.2.0" - rechoir "^0.7.0" + interpret "^3.1.1" + rechoir "^0.8.0" webpack-merge "^5.7.3" webpack-merge@^5.7.3: - version "5.8.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" - integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== dependencies: clone-deep "^4.0.1" + flat "^5.0.2" wildcard "^2.0.0" webpack-sources@^3.2.3: @@ -5759,34 +6342,33 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.74.0: - version "5.75.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.75.0.tgz#1e440468647b2505860e94c9ff3e44d5b582c152" - integrity sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ== +webpack@^5.75.0: + version "5.95.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.95.0.tgz#8fd8c454fa60dad186fbe36c400a55848307b4c0" + integrity sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q== dependencies: - "@types/eslint-scope" "^3.7.3" - "@types/estree" "^0.0.51" - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/wasm-edit" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" acorn "^8.7.1" - acorn-import-assertions "^1.7.6" - browserslist "^4.14.5" + acorn-import-attributes "^1.9.5" + browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.10.0" - es-module-lexer "^0.9.0" + enhanced-resolve "^5.17.1" + es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" + graceful-fs "^4.2.11" json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" - schema-utils "^3.1.0" + schema-utils "^3.2.0" tapable "^2.1.1" - terser-webpack-plugin "^5.1.3" - watchpack "^2.4.0" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" webpack-sources "^3.2.3" whatwg-encoding@^2.0.0: @@ -5797,9 +6379,9 @@ whatwg-encoding@^2.0.0: iconv-lite "0.6.3" whatwg-fetch@^3.4.1: - version "3.6.2" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" - integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== + version "3.6.20" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" + integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== whatwg-mimetype@^3.0.0: version "3.0.0" @@ -5822,15 +6404,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^8.4.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== - dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -5843,21 +6416,20 @@ which-boxed-primitive@^1.0.2: is-symbol "^1.0.3" which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== + version "2.0.1" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== -which-typed-array@^1.1.2: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== +which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.2: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" for-each "^0.3.3" gopd "^1.0.1" - has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" + has-tostringtag "^1.0.2" which@^1.2.9: version "1.3.1" @@ -5881,27 +6453,19 @@ wif@^2.0.6: bs58check "<3.0.0" wildcard@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" - integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== - -word-wrap@^1.2.3, word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== -worker-loader@^3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-3.0.8.tgz#5fc5cda4a3d3163d9c274a4e3a811ce8b60dbb37" - integrity sha512-XQyQkIFeRVC7f7uRhFdNMe/iJOdO6zxAaR3EWbDp45v3mDhrTi+++oswKNxShUNjPC/1xUp5DB29YKLhFo129g== - dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -workerpool@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" - integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== +workerpool@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" + integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== wrap-ansi@^6.2.0: version "6.2.0" @@ -5942,14 +6506,14 @@ ws@7.4.6: integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== ws@^7.2.0, ws@^7.3.1, ws@^7.4.0: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== ws@^8.11.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" - integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== xml-name-validator@^4.0.0: version "4.0.0" @@ -5961,7 +6525,7 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.1, xtend@~4.0.1: +xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -5976,15 +6540,10 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yargs-parser@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yargs-parser@^18.1.2: version "18.1.3" @@ -5994,12 +6553,12 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.2: +yargs-parser@^20.2.2, yargs-parser@^20.2.9: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-unparser@2.0.0: +yargs-unparser@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== @@ -6009,19 +6568,6 @@ yargs-unparser@2.0.0: flat "^5.0.2" is-plain-obj "^2.1.0" -yargs@16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - yargs@^15.0.2: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" @@ -6039,6 +6585,19 @@ yargs@^15.0.2: y18n "^4.0.0" yargs-parser "^18.1.2" +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" diff --git a/packages/provider/.nvmrc b/packages/provider/.nvmrc index 9f4189900..b714151ef 100644 --- a/packages/provider/.nvmrc +++ b/packages/provider/.nvmrc @@ -1 +1 @@ -v14.17.6 \ No newline at end of file +v18.18.0 \ No newline at end of file diff --git a/packages/provider/LICENSE b/packages/provider/LICENSE index ef487ce39..0d52353d8 100644 --- a/packages/provider/LICENSE +++ b/packages/provider/LICENSE @@ -1,4 +1,4 @@ -All copyrights (including licenses) are owned by Virtual Privacy AG (“BlockWallet”). +All copyrights (including licenses) are owned by Business Variety Limited (3189974). The BlockWallet owns all legal rights, title, and interest in and to the work, software, application, source code, documentation, and any other documents in this repository (collectively, the “Software”), including any intellectual property rights which subsist in the Software (whether those rights happen to be registered or not, and wherever in the world those rights may exist), whether in source code or any other form. diff --git a/packages/provider/Makefile b/packages/provider/Makefile index b7b7d53af..d3b8ac725 100644 --- a/packages/provider/Makefile +++ b/packages/provider/Makefile @@ -1,6 +1,8 @@ TS_NODE_PROJECT := tsconfig.json BRANCH := $(shell git symbolic-ref --short -q HEAD | sed 's/[\.\/]/-/g') ENVIRONMENT ?= dev +BROWSER ?= chrome + build/provider: ifeq ($(ENVIRONMENT), dev) @@ -12,6 +14,11 @@ else npx webpack --config ./webpack/webpack.provider.js npx webpack --config ./webpack/webpack.content.js endif +ifeq ($(BROWSER), firefox) + @mkdir -p ../../dist-firefox + @cp ../../dist/content.js ../../dist-firefox/content.js + @cp ../../dist/blankProvider.js ../../dist-firefox/blankProvider.js +endif test/provider: yarn nyc -a --reporter=html --reporter=text mocha './test' --require esm --require isomorphic-fetch --require jsdom-global/register --require ts-node/register --require tsconfig-paths/register 'test/**/*.test.ts' --timeout 10000 --exit diff --git a/packages/provider/package.json b/packages/provider/package.json index 8d5f9f33d..56093107a 100644 --- a/packages/provider/package.json +++ b/packages/provider/package.json @@ -1,40 +1,42 @@ { "name": "@block-wallet/provider", - "version": "0.3.2", + "version": "0.5.0", "private": true, "dependencies": { "@block-wallet/remote-configs": "https://github.com/block-wallet/remote-configs#v1.1.0", "@metamask/safe-event-emitter": "^2.0.0", "async-mutex": "^0.4.0", "eth-rpc-errors": "^4.0.3", - "loglevel": "^1.8.0" + "loglevel": "^1.8.1", + "webextension-polyfill": "^0.10.0" }, "devDependencies": { - "@types/chai": "^4.2.21", - "@types/chrome": "^0.0.137", - "@types/mocha": "^9.0.0", - "@types/node": "^18.7.16", - "@typescript-eslint/eslint-plugin": "^5.36.2", - "@typescript-eslint/parser": "^5.36.2", - "chai": "^4.3.4", - "dotenv": "^16.0.1", - "dotenv-webpack": "^7.0.3", - "eslint": "^7.32.0", - "eslint-config-prettier": "^8.5.0", - "eslint-webpack-plugin": "^3.0.1", + "@types/chai": "^4.3.4", + "@types/chrome": "^0.0.206", + "@types/mocha": "^10.0.1", + "@types/node": "^18.11.18", + "@types/webextension-polyfill": "^0.10.1", + "@typescript-eslint/eslint-plugin": "^5.48.0", + "@typescript-eslint/parser": "^5.48.0", + "chai": "^4.3.7", + "dotenv": "^16.0.3", + "dotenv-webpack": "^8.0.1", + "eslint": "^8.31.0", + "eslint-config-prettier": "^8.6.0", + "eslint-webpack-plugin": "^3.2.0", "esm": "^3.2.25", "isomorphic-fetch": "^3.0.0", - "jsdom": "^20.0.0", + "jsdom": "^20.0.3", "jsdom-global": "^3.0.2", - "mocha": "^9.1.1", + "mocha": "^10.2.0", "nyc": "^15.1.0", - "prettier": "^2.7.1", - "ts-loader": "^9.1.2", - "ts-node": "^10.2.1", - "tsconfig-paths": "^4.1.0", - "typescript": "^4.4.3", - "webpack": "^5.53.0", - "webpack-cli": "^4.10.0", + "prettier": "^2.8.1", + "ts-loader": "^9.4.2", + "ts-node": "^10.9.1", + "tsconfig-paths": "^4.1.2", + "typescript": "^4.9.4", + "webpack": "^5.75.0", + "webpack-cli": "^5.0.1", "webpack-merge": "^5.8.0" }, "scripts": { diff --git a/packages/provider/src/content.ts b/packages/provider/src/content.ts index bad4aa40e..ede77cc87 100644 --- a/packages/provider/src/content.ts +++ b/packages/provider/src/content.ts @@ -8,6 +8,7 @@ import { Mutex } from 'async-mutex'; import log from 'loglevel'; import { SignalMessage, Signals } from './types'; import { checkScriptLoad } from './utils/site'; +import browser from 'webextension-polyfill'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore @@ -19,10 +20,13 @@ const EXTENSION_CONTEXT_INVALIDATED_CHROMIUM_ERROR = let providerOverridden = false; +console.log('content.ts'); + function injectProvider() { + console.log('injectProvider'); if (!isManifestV3()) { const injectableScript = blankProvider; - const injectableScriptSourceMapURL = `//# sourceURL=${chrome.runtime.getURL( + const injectableScriptSourceMapURL = `//# sourceURL=${browser.runtime.getURL( 'blankProvider.js' )}\n`; const BUNDLE = injectableScript + injectableScriptSourceMapURL; @@ -52,21 +56,22 @@ const SW_KEEP_ALIVE_INTERVAL = 1000; let SW_ALIVE = false; let EXTENSION_CONTEXT_VALID = true; let portReinitialized = false; -let timeoutRef: NodeJS.Timer; +let timeoutRef: NodeJS.Timeout; function swKeepAlive() { + console.log('swKeepAlive'); return new Promise((resolve) => { try { - chrome.runtime.sendMessage( - { message: CONTENT.SW_KEEP_ALIVE }, - () => { - if (chrome.runtime.lastError) { + browser.runtime + .sendMessage({ message: CONTENT.SW_KEEP_ALIVE }) + .then(() => { + if (browser.runtime.lastError) { log.info( 'Error keeping alive:', - chrome.runtime.lastError.message || - chrome.runtime.lastError + browser.runtime.lastError.message || + browser.runtime.lastError ); - const err = chrome.runtime.lastError.message || ''; + const err = browser.runtime.lastError.message || ''; SW_ALIVE = !err.includes( 'Receiving end does not exist' ); @@ -75,8 +80,7 @@ function swKeepAlive() { SW_ALIVE = true; } resolve(); - } - ); + }); } catch (e) { let message = `BlockWallet: ${e}`; if (e.message === EXTENSION_CONTEXT_INVALIDATED_CHROMIUM_ERROR) { @@ -90,6 +94,7 @@ function swKeepAlive() { } async function keepExtensionAlive() { + console.log('keepExtensionAlive'); await swKeepAlive(); if (EXTENSION_CONTEXT_VALID) { timeoutRef = setTimeout(keepExtensionAlive, SW_KEEP_ALIVE_INTERVAL); @@ -106,14 +111,14 @@ function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } -let port: chrome.runtime.Port | undefined = undefined; +let port: browser.Runtime.Port | undefined = undefined; const initMutex: Mutex = new Mutex(); // Check background settings for script load -chrome.runtime.sendMessage( - { message: CONTENT.SHOULD_INJECT }, - (response: { shouldInject: boolean }): void => { - const error = chrome.runtime.lastError; +browser.runtime + .sendMessage({ message: CONTENT.SHOULD_INJECT }) + .then((response: { shouldInject: boolean }): void => { + const error = browser.runtime.lastError; const shouldLoad = checkScriptLoad(); if ( port && @@ -130,8 +135,7 @@ chrome.runtime.sendMessage( } else if (providerOverridden) { injectProvider(); } - } -); + }); // Setup window listener const windowListener = async ({ @@ -141,8 +145,13 @@ const windowListener = async ({ // Only allow messages from our window, by the inject if ( source !== window || + source.origin === 'null' || data.origin !== Origin.PROVIDER || - !Object.values(EXTERNAL).includes(data.message) + !Object.values(EXTERNAL).includes(data.message) || + // data.id should match the format indicated on BlankProvider.js because it could be set maliciously by a web page + // Regex validates the following format + // `${Date.now()}.${++this._requestId}` --> 1694708163916.8 + !/^(\d+)\.\d+$/.test(data.id) ) { return; } @@ -151,17 +160,22 @@ const windowListener = async ({ const postMessage = async ( data: WindowTransportRequestMessage ): Promise => { + const message = + data && typeof data !== undefined + ? JSON.parse(JSON.stringify(data)) + : data; try { if (!SW_ALIVE || !port) { // Port was reinitialized, force retry throw new Error(); } - port.postMessage(data); + port.postMessage(message); } catch (error) { + log.warn(message, error); // If this fails due to SW being inactive, retry await sleep(30); log.debug('waiting for SW to startup...'); - return postMessage(data); + return postMessage(message); } }; @@ -174,15 +188,24 @@ window.addEventListener('message', (message) => { // Init function const init = () => { + console.log('init'); // Setup port connection - port = chrome.runtime.connect({ name: Origin.PROVIDER }); + port = browser.runtime.connect({ name: Origin.PROVIDER }); // Set callback to send any messages from the extension back to the page - port.onMessage.addListener((message): void => { - window.postMessage( - { ...message, origin: Origin.BACKGROUND }, - window.location.href - ); + port.onMessage.addListener((message: any): void => { + const nmessage = { + ...(message && typeof message !== undefined + ? JSON.parse(JSON.stringify(message)) + : message), + origin: Origin.BACKGROUND, + }; + try { + window.postMessage(nmessage, window.location.href); + } catch (error: any) { + log.warn(nmessage, error); + throw error; + } }); if (isManifestV3()) { diff --git a/packages/provider/src/index.ts b/packages/provider/src/index.ts index 75607f8bc..b37abee2d 100644 --- a/packages/provider/src/index.ts +++ b/packages/provider/src/index.ts @@ -5,7 +5,7 @@ import { } from '@block-wallet/background/utils/types/communication'; import log, { LogLevelDesc } from 'loglevel'; import shimWeb3 from './utils/shimWeb3'; -import { InjectedWindow, SignalMessage } from './types'; +import { EIP6963ProviderInfo, InjectedWindow, SignalMessage } from './types'; // Setting the default log level: log.setLevel((process.env.LOG_LEVEL as LogLevelDesc) || 'warn'); @@ -16,6 +16,25 @@ const provider = new Proxy(blankProvider, { deleteProperty: () => true, }); +function announceProvider() { + const info: EIP6963ProviderInfo = { + uuid: '350670db-19fa-4704-a166-e52e178b59d2', + name: 'BlockWallet', + icon: "data:image/svg+xml;charset=UTF-8,%3csvg width='788' height='789' viewBox='0 0 788 789' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cg clip-path='url(%23clip0_1266_227)'%3e%3cpath d='M393.94 0.340332C176.37 0.340332 0 176.71 0 394.28C0 611.85 176.37 788.22 393.94 788.22C611.51 788.22 787.88 611.85 787.88 394.28C787.88 176.71 611.5 0.340332 393.94 0.340332ZM588.35 625.46L176.33 496.77L244.17 111.03L656.19 239.72L588.34 625.47L588.35 625.46Z' fill='%23327100'/%3e%3cpath d='M51.92 198.68C18.89 256.31 0 323.09 0 394.28C0 399.23 0.09 404.16 0.28 409.07L77.37 331.39L117 152.23L51.93 198.68H51.92Z' fill='url(%23paint0_linear_1266_227)'/%3e%3cpath d='M180.387 475.35L81.5572 367.07L2.61719 437.18C17.1572 571.44 99.1572 685.47 214.027 744.67L327.147 636.14L209.257 506.98L176.627 496.79L180.397 475.36L180.387 475.35Z' fill='url(%23paint1_linear_1266_227)'/%3e%3cpath d='M286.113 531.33L210.513 507.72L91.2031 647.35C163.463 733.47 271.893 788.22 393.103 788.22C412.893 788.22 432.343 786.75 451.343 783.94L521.363 687.46L286.113 531.34V531.33Z' fill='url(%23paint2_linear_1266_227)'/%3e%3cpath d='M666.137 227.99L621.997 280.15L209.977 151.46L254.127 99.3003L666.137 227.99Z' fill='%23122600'/%3e%3cpath d='M612.519 211.021L606.039 261.46L622.379 266.46L655.889 224.741L612.519 211.021Z' fill='url(%23paint3_linear_1266_227)'/%3e%3cpath d='M597.617 613.63L553.477 665.79L621.327 280.03L665.467 227.87L597.617 613.63Z' fill='%2354C900'/%3e%3cpath d='M501.908 15.3201C724.798 81.3401 820.238 298.46 777.478 481.06C734.248 665.71 550.658 773.99 348.608 720.96C146.448 667.92 27.2882 477.89 58.4882 272.82C91.1982 57.9001 294.478 -40.3299 501.908 15.3201ZM600.188 615.41L668.038 229.66L256.018 100.98L188.168 486.73L600.188 615.42' fill='url(%23paint4_linear_1266_227)'/%3e%3cpath d='M451.418 735.86C417.848 735.86 383.508 731.43 349.188 722.42C299.518 709.38 253.918 687.92 213.678 658.61C174.698 630.23 141.748 595.27 115.728 554.71C63.2481 472.88 42.7281 372.68 57.9581 272.59C65.6481 222.06 82.9081 176.58 109.258 137.41L111.748 139.08C85.6481 177.88 68.5481 222.95 60.9281 273.04C45.8081 372.41 66.1681 471.87 118.258 553.09C144.068 593.34 176.768 628.03 215.448 656.19C255.388 685.27 300.638 706.58 349.948 719.52C462.468 749.05 575.268 729.03 659.418 664.6L661.238 666.98C602.038 712.31 528.788 735.86 451.418 735.86Z' fill='url(%23paint5_linear_1266_227)'/%3e%3cpath d='M600.153 617.11L594.243 616.07L661.633 232.91L252.133 105.01L253.923 99.2803L665.943 227.97C667.383 228.42 668.263 229.87 668.003 231.35L600.153 617.1V617.11Z' fill='url(%23paint6_linear_1266_227)'/%3e%3cpath d='M591.889 618.67L181.469 490.48L249.779 102.13L255.679 103.17L188.299 486.33L593.679 612.94L591.889 618.67Z' fill='url(%23paint7_linear_1266_227)'/%3e%3cpath d='M618.672 295.76L621.322 280.24L661.712 232.51L658.962 248.15L618.672 295.76Z' fill='url(%23paint8_linear_1266_227)'/%3e%3cpath d='M623.383 290.21L650.313 297.5L658.993 248.13L623.383 290.21Z' fill='url(%23paint9_linear_1266_227)'/%3e%3cpath d='M502.279 15.3203C467.739 6.06031 433.229 1.36031 399.539 0.820312C608.029 11.0003 773.939 183.26 773.939 394.28C773.939 489.56 740.109 576.93 683.819 645.06C729.149 603.63 762.259 547.66 777.849 481.07C820.609 298.46 725.169 81.3503 502.279 15.3303V15.3203Z' fill='url(%23paint10_linear_1266_227)'/%3e%3c/g%3e%3cdefs%3e%3clinearGradient id='paint0_linear_1266_227' x1='-9.49' y1='208.51' x2='90.13' y2='314' gradientUnits='userSpaceOnUse'%3e%3cstop offset='0.29' stop-color='%23327100'/%3e%3cstop offset='0.3' stop-color='%233C7B06'/%3e%3cstop offset='0.34' stop-color='%235B9B1B'/%3e%3cstop offset='0.38' stop-color='%2373B42C'/%3e%3cstop offset='0.43' stop-color='%2385C637'/%3e%3cstop offset='0.47' stop-color='%238FD03E'/%3e%3cstop offset='0.53' stop-color='%2393D441'/%3e%3cstop offset='0.74' stop-color='%2391D23F'/%3e%3cstop offset='0.82' stop-color='%238ACB3B'/%3e%3cstop offset='0.87' stop-color='%237EBF33'/%3e%3cstop offset='0.92' stop-color='%236EAE28'/%3e%3cstop offset='0.95' stop-color='%2358981A'/%3e%3cstop offset='0.99' stop-color='%233E7E08'/%3e%3cstop offset='1' stop-color='%23327100'/%3e%3c/linearGradient%3e%3clinearGradient id='paint1_linear_1266_227' x1='14.3272' y1='446.77' x2='261.337' y2='662.91' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='%23327100'/%3e%3cstop offset='0.05' stop-color='%234A8A10'/%3e%3cstop offset='0.11' stop-color='%2364A422'/%3e%3cstop offset='0.18' stop-color='%2379B92F'/%3e%3cstop offset='0.26' stop-color='%2387C839'/%3e%3cstop offset='0.35' stop-color='%2390D13F'/%3e%3cstop offset='0.49' stop-color='%2393D441'/%3e%3cstop offset='0.72' stop-color='%2391D23F'/%3e%3cstop offset='0.8' stop-color='%238ACB3B'/%3e%3cstop offset='0.86' stop-color='%237EBF33'/%3e%3cstop offset='0.91' stop-color='%236EAE28'/%3e%3cstop offset='0.95' stop-color='%2358981A'/%3e%3cstop offset='0.99' stop-color='%233E7E08'/%3e%3cstop offset='1' stop-color='%23327100'/%3e%3c/linearGradient%3e%3clinearGradient id='paint2_linear_1266_227' x1='171.203' y1='571.83' x2='450.293' y2='782.37' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='%23A2D159' stop-opacity='0'/%3e%3cstop offset='0.06' stop-color='%23749B3C' stop-opacity='0.31'/%3e%3cstop offset='0.17' stop-color='%233C581A' stop-opacity='0.7'/%3e%3cstop offset='0.29' stop-color='%231C3106' stop-opacity='0.93'/%3e%3cstop offset='0.49' stop-color='%23122600'/%3e%3cstop offset='0.73' stop-color='%23122700'/%3e%3cstop offset='0.82' stop-color='%23152E00'/%3e%3cstop offset='0.88' stop-color='%231A3A00'/%3e%3cstop offset='0.93' stop-color='%23214B00'/%3e%3cstop offset='0.98' stop-color='%232B6100'/%3e%3cstop offset='1' stop-color='%23327100'/%3e%3c/linearGradient%3e%3clinearGradient id='paint3_linear_1266_227' x1='650.229' y1='171.721' x2='618.349' y2='263.531' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='%23C7FF00'/%3e%3cstop offset='1' stop-color='%2353C200' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint4_linear_1266_227' x1='199.508' y1='446.98' x2='709.368' y2='260.42' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='%23C7FF00'/%3e%3cstop offset='1' stop-color='%2353C200'/%3e%3c/linearGradient%3e%3clinearGradient id='paint5_linear_1266_227' x1='1.92809' y1='186.14' x2='568.998' y2='814.6' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='%23C7FF00' stop-opacity='0'/%3e%3cstop offset='0.06' stop-color='%238EBA00' stop-opacity='0.31'/%3e%3cstop offset='0.17' stop-color='%23476600' stop-opacity='0.7'/%3e%3cstop offset='0.29' stop-color='%231E3500' stop-opacity='0.93'/%3e%3cstop offset='0.49' stop-color='%23122600'/%3e%3cstop offset='0.77' stop-color='%231A3000' stop-opacity='0.95'/%3e%3cstop offset='0.87' stop-color='%233B5700' stop-opacity='0.77'/%3e%3cstop offset='0.94' stop-color='%23739A00' stop-opacity='0.46'/%3e%3cstop offset='1' stop-color='%23C7FF00' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint6_linear_1266_227' x1='511.543' y1='308.96' x2='361.963' y2='-101.83' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='%23C7FF00'/%3e%3cstop offset='1' stop-color='%2353C200' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint7_linear_1266_227' x1='232.839' y1='550.62' x2='515.809' y2='264.3' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='%2343A000'/%3e%3cstop offset='0.41' stop-color='%2343A000' stop-opacity='0.99'/%3e%3cstop offset='0.56' stop-color='%2345A103' stop-opacity='0.95'/%3e%3cstop offset='0.66' stop-color='%2349A307' stop-opacity='0.87'/%3e%3cstop offset='0.75' stop-color='%234EA70D' stop-opacity='0.77'/%3e%3cstop offset='0.82' stop-color='%2355AB15' stop-opacity='0.63'/%3e%3cstop offset='0.88' stop-color='%235DB01E' stop-opacity='0.46'/%3e%3cstop offset='0.94' stop-color='%2367B72A' stop-opacity='0.26'/%3e%3cstop offset='0.99' stop-color='%2372BE37' stop-opacity='0.03'/%3e%3cstop offset='1' stop-color='%2374BF39' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint8_linear_1266_227' x1='678.332' y1='235.81' x2='537.702' y2='340.26' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='%23C7FF00'/%3e%3cstop offset='1' stop-color='%2353C200' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint9_linear_1266_227' x1='648.793' y1='237.84' x2='635.543' y2='299.96' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='%2374BF39' stop-opacity='0'/%3e%3cstop offset='0.06' stop-color='%23558E27' stop-opacity='0.31'/%3e%3cstop offset='0.17' stop-color='%232F5310' stop-opacity='0.7'/%3e%3cstop offset='0.29' stop-color='%23183003' stop-opacity='0.93'/%3e%3cstop offset='0.49' stop-color='%23122600'/%3e%3cstop offset='0.77' stop-color='%23162D02' stop-opacity='0.95'/%3e%3cstop offset='0.87' stop-color='%2328480C' stop-opacity='0.77'/%3e%3cstop offset='0.94' stop-color='%2346781E' stop-opacity='0.46'/%3e%3cstop offset='1' stop-color='%2374BF39' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint10_linear_1266_227' x1='594.389' y1='200.88' x2='621.179' y2='587.11' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='%2374BF39' stop-opacity='0'/%3e%3cstop offset='0.01' stop-color='%236FBC34' stop-opacity='0.09'/%3e%3cstop offset='0.06' stop-color='%2363B426' stop-opacity='0.33'/%3e%3cstop offset='0.11' stop-color='%2359AE1A' stop-opacity='0.54'/%3e%3cstop offset='0.17' stop-color='%2351A910' stop-opacity='0.71'/%3e%3cstop offset='0.23' stop-color='%234BA509' stop-opacity='0.84'/%3e%3cstop offset='0.29' stop-color='%2346A204' stop-opacity='0.93'/%3e%3cstop offset='0.37' stop-color='%2343A000' stop-opacity='0.98'/%3e%3cstop offset='0.49' stop-color='%2343A000'/%3e%3cstop offset='0.7' stop-color='%2343A000' stop-opacity='0.99'/%3e%3cstop offset='0.77' stop-color='%2345A103' stop-opacity='0.95'/%3e%3cstop offset='0.83' stop-color='%2349A307' stop-opacity='0.87'/%3e%3cstop offset='0.87' stop-color='%234EA70D' stop-opacity='0.77'/%3e%3cstop offset='0.91' stop-color='%2355AB15' stop-opacity='0.63'/%3e%3cstop offset='0.94' stop-color='%235DB01E' stop-opacity='0.46'/%3e%3cstop offset='0.97' stop-color='%2367B72A' stop-opacity='0.26'/%3e%3cstop offset='1' stop-color='%2374BF39' stop-opacity='0'/%3e%3c/linearGradient%3e%3cclipPath id='clip0_1266_227'%3e%3crect width='787.87' height='788.21' fill='white'/%3e%3c/clipPath%3e%3c/defs%3e%3c/svg%3e ", + rdns: 'io.blockwallet.extension', + }; + window.dispatchEvent( + new CustomEvent('eip6963:announceProvider', { + detail: Object.freeze({ info, provider }), + }) + ); +} + +window.addEventListener('eip6963:requestProvider', () => { + announceProvider(); +}); +announceProvider(); + shimWeb3(provider); (window as Window & InjectedWindow).ethereum = provider; diff --git a/packages/provider/src/provider/BlankProvider.ts b/packages/provider/src/provider/BlankProvider.ts index a291be76e..d4cd9db00 100644 --- a/packages/provider/src/provider/BlankProvider.ts +++ b/packages/provider/src/provider/BlankProvider.ts @@ -75,6 +75,10 @@ export default class BlankProvider isUnlocked: () => Promise; }; + private _blockWallet: { + isEnrolled: (campaignId: string) => Promise; + }; + constructor() { super(); @@ -105,6 +109,11 @@ export default class BlankProvider isUnlocked: async () => true, }; + this._blockWallet = { + isEnrolled: (campaignId: string) => + this._checkCampaignEnrollment(campaignId), + }; + // Bind non arrow functions this.send = this.send.bind(this); this.sendAsync = this.sendAsync.bind(this); @@ -137,6 +146,22 @@ export default class BlankProvider this.isMetaMask = !isBlockWallet; } + /** + * Checks whether a BlockWallet instance is enrolled in the given campaign. + * @param campaignId + * @returns true if the instance has already been registered in that campaign + */ + private async _checkCampaignEnrollment( + campaignId: string + ): Promise { + const isEnrolled = await this._postMessage( + Messages.EXTERNAL.IS_ENROLLED, + { campaignId } + ); + + return isEnrolled; + } + private async reInitializeSubscriptions() { log.trace('reInitializeSubscriptions', 'init', this._ethSubscriptions); for (const reqId in this._ethSubscriptions) { @@ -463,15 +488,21 @@ export default class BlankProvider id ); - window.postMessage( - { + const nmessage = JSON.parse( + JSON.stringify({ id, message, origin: Origin.PROVIDER, request: updatedReq ?? (request || {}), - } as WindowTransportRequestMessage, - window.location.href - ); + }) + ) as WindowTransportRequestMessage; + + try { + window.postMessage(nmessage, window.location.href); + } catch (error: any) { + log.warn(nmessage, error); + throw error; + } }); }; diff --git a/packages/provider/src/types.ts b/packages/provider/src/types.ts index 207e9813d..db3351c3b 100644 --- a/packages/provider/src/types.ts +++ b/packages/provider/src/types.ts @@ -224,3 +224,21 @@ export type SignalMessage = { origin: Origin; signal: Signals; }; + +export interface EIP6963ProviderInfo { + uuid: string; + name: string; + icon: string; + rdns: string; +} + +export interface EIP6963ProviderDetail { + info: EIP6963ProviderInfo; + provider: BlankProvider; +} + +// Announce Event dispatched by a Wallet +export interface EIP6963AnnounceProviderEvent extends CustomEvent { + type: 'eip6963:announceProvider'; + detail: EIP6963ProviderDetail; +} diff --git a/packages/provider/yarn.lock b/packages/provider/yarn.lock index f54d5ae65..fb32eed00 100644 --- a/packages/provider/yarn.lock +++ b/packages/provider/yarn.lock @@ -2,261 +2,264 @@ # yarn lockfile v1 -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: - "@babel/highlight" "^7.10.4" + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" - integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== dependencies: - "@babel/highlight" "^7.14.5" + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" -"@babel/compat-data@^7.15.0": - version "7.15.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176" - integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA== +"@babel/compat-data@^7.22.9": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.20.tgz#8df6e96661209623f1975d66c35ffca66f3306d0" + integrity sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw== "@babel/core@^7.7.5": - version "7.15.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.15.5.tgz#f8ed9ace730722544609f90c9bb49162dc3bf5b9" - integrity sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.4" - "@babel/helper-compilation-targets" "^7.15.4" - "@babel/helper-module-transforms" "^7.15.4" - "@babel/helpers" "^7.15.4" - "@babel/parser" "^7.15.5" - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - convert-source-map "^1.7.0" + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.0.tgz#f8259ae0e52a123eb40f552551e647b506a94d83" + integrity sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-module-transforms" "^7.23.0" + "@babel/helpers" "^7.23.0" + "@babel/parser" "^7.23.0" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.0" + "@babel/types" "^7.23.0" + convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - source-map "^0.5.0" + json5 "^2.2.3" + semver "^6.3.1" -"@babel/generator@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.4.tgz#85acb159a267ca6324f9793986991ee2022a05b0" - integrity sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw== +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== dependencies: - "@babel/types" "^7.15.4" + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" - source-map "^0.5.0" -"@babel/helper-compilation-targets@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz#cf6d94f30fbefc139123e27dd6b02f65aeedb7b9" - integrity sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ== - dependencies: - "@babel/compat-data" "^7.15.0" - "@babel/helper-validator-option" "^7.14.5" - browserslist "^4.16.6" - semver "^6.3.0" - -"@babel/helper-function-name@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz#845744dafc4381a4a5fb6afa6c3d36f98a787ebc" - integrity sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw== - dependencies: - "@babel/helper-get-function-arity" "^7.15.4" - "@babel/template" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helper-get-function-arity@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz#098818934a137fce78b536a3e015864be1e2879b" - integrity sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-hoist-variables@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz#09993a3259c0e918f99d104261dfdfc033f178df" - integrity sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-member-expression-to-functions@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz#bfd34dc9bba9824a4658b0317ec2fd571a51e6ef" - integrity sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-module-imports@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz#e18007d230632dea19b47853b984476e7b4e103f" - integrity sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-module-transforms@^7.15.4": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz#7da80c8cbc1f02655d83f8b79d25866afe50d226" - integrity sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw== - dependencies: - "@babel/helper-module-imports" "^7.15.4" - "@babel/helper-replace-supers" "^7.15.4" - "@babel/helper-simple-access" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - "@babel/helper-validator-identifier" "^7.15.7" - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.6" - -"@babel/helper-optimise-call-expression@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz#f310a5121a3b9cc52d9ab19122bd729822dee171" - integrity sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-replace-supers@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz#52a8ab26ba918c7f6dee28628b07071ac7b7347a" - integrity sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.15.4" - "@babel/helper-optimise-call-expression" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helper-simple-access@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz#ac368905abf1de8e9781434b635d8f8674bcc13b" - integrity sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-split-export-declaration@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz#aecab92dcdbef6a10aa3b62ab204b085f776e257" - integrity sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.15.7": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" - integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== - -"@babel/helper-validator-option@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" - integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== - -"@babel/helpers@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.15.4.tgz#5f40f02050a3027121a3cf48d497c05c555eaf43" - integrity sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ== - dependencies: - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - chalk "^2.0.0" +"@babel/helper-compilation-targets@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" + integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.15" + browserslist "^4.21.9" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-transforms@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz#3ec246457f6c842c0aee62a01f60739906f7047e" + integrity sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" + integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== + +"@babel/helpers@^7.23.0": + version "7.23.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.1.tgz#44e981e8ce2b9e99f8f0b703f3326a4636c16d15" + integrity sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA== + dependencies: + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.0" + "@babel/types" "^7.23.0" + +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.15.4", "@babel/parser@^7.15.5": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.7.tgz#0c3ed4a2eb07b165dfa85b3cc45c727334c4edae" - integrity sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g== - -"@babel/template@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" - integrity sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/parser" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/traverse@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d" - integrity sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.4" - "@babel/helper-function-name" "^7.15.4" - "@babel/helper-hoist-variables" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - "@babel/parser" "^7.15.4" - "@babel/types" "^7.15.4" +"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.0.tgz#18196ddfbcf4ccea324b7f6d3ada00d8c5a99c53" + integrity sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.15.4", "@babel/types@^7.15.6": - version "7.15.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f" - integrity sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig== +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== dependencies: - "@babel/helper-validator-identifier" "^7.14.9" + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" "@block-wallet/remote-configs@https://github.com/block-wallet/remote-configs#v1.1.0": version "0.0.0" resolved "https://github.com/block-wallet/remote-configs#eca1a737090b224974ede7b47ec44224b31fe2d1" -"@cspotcode/source-map-consumer@0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" - integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== - -"@cspotcode/source-map-support@0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz#118511f316e2e87ee4294761868e254d3da47960" - integrity sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== dependencies: - "@cspotcode/source-map-consumer" "0.8.0" + "@jridgewell/trace-mapping" "0.3.9" "@discoveryjs/json-ext@^0.5.0": - version "0.5.5" - resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz#9283c9ce5b289a3c4f61c12757469e59377f81f3" - integrity sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA== + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.9.0.tgz#7ccb5f58703fa61ffdcbf39e2c604a109e781162" + integrity sha512-zJmuCWj2VLBt4c25CfBIbMZLGLyhkvs7LznyVX5HfpzeocThgIj5XQK4L+g3U36mMcx8bPMhGyPpwCATamC4jQ== -"@eslint/eslintrc@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" - integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== +"@eslint/eslintrc@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" + integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== dependencies: ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^13.9.0" - ignore "^4.0.6" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" + js-yaml "^4.1.0" + minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@humanwhocodes/config-array@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" - integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== +"@eslint/js@8.50.0": + version "8.50.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.50.0.tgz#9e93b850f0f3fa35f5fa59adfd03adae8488e484" + integrity sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ== + +"@humanwhocodes/config-array@^0.11.11": + version "0.11.11" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844" + integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA== dependencies: - "@humanwhocodes/object-schema" "^1.2.0" + "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" - minimatch "^3.0.4" + minimatch "^3.0.5" -"@humanwhocodes/object-schema@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" - integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -274,6 +277,54 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@metamask/safe-event-emitter@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz#af577b477c683fad17c619a78208cede06f9605c" @@ -292,7 +343,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -306,324 +357,325 @@ integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== "@tsconfig/node10@^1.0.7": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" - integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== "@tsconfig/node12@^1.0.7": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" - integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== "@tsconfig/node14@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" - integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" - integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== -"@types/chai@^4.2.21": - version "4.2.22" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.22.tgz#47020d7e4cf19194d43b5202f35f75bd2ad35ce7" - integrity sha512-tFfcE+DSTzWAgifkjik9AySNqIyNoYwmR+uecPwwD/XRNfvOjmC/FjCxpiUGDkDVDphPfCUecSQVFw+lN3M3kQ== +"@types/chai@^4.3.4": + version "4.3.6" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.6.tgz#7b489e8baf393d5dd1266fb203ddd4ea941259e6" + integrity sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw== -"@types/chrome@^0.0.137": - version "0.0.137" - resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.137.tgz#5ea73ee6e874880ecdfaccd5b378c541c7da87ec" - integrity sha512-rMJOMqdX+RvVcl/F8VV6amYkZVkGj9KXM1EWGi5XxB/xdNB47CwBwInaMKFm+mgftdO9XWR1m4zgHiR6nrIX5w== +"@types/chrome@^0.0.206": + version "0.0.206" + resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.206.tgz#ad1fd9799f368b5993d7c240492d4adaf5efbd8c" + integrity sha512-fQnTFjghPB9S4UzbfublUB6KmsBkvvJeGXGaaoD5Qu+ZxrDUfgJnKN5egLSzDcGAH5YxQubDgbCdNwwUGewQHg== dependencies: "@types/filesystem" "*" "@types/har-format" "*" -"@types/eslint-scope@^3.7.0": - version "3.7.1" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.1.tgz#8dc390a7b4f9dd9f1284629efce982e41612116e" - integrity sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g== +"@types/eslint-scope@^3.7.3": + version "3.7.5" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.5.tgz#e28b09dbb1d9d35fdfa8a884225f00440dfc5a3e" + integrity sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA== dependencies: "@types/eslint" "*" "@types/estree" "*" -"@types/eslint@*", "@types/eslint@^7.2.14": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.28.0.tgz#7e41f2481d301c68e14f483fe10b017753ce8d5a" - integrity sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A== +"@types/eslint@*", "@types/eslint@^7.29.0 || ^8.4.1": + version "8.44.3" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.3.tgz#96614fae4875ea6328f56de38666f582d911d962" + integrity sha512-iM/WfkwAhwmPff3wZuPLYiHX18HI24jU8k1ZSH7P8FHwxTjZ2P6CoX2wnF43oprR+YXJM6UUxATkNvyv/JHd+g== dependencies: "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@^0.0.50": - version "0.0.50" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" - integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.2.tgz#ff02bc3dc8317cd668dfec247b750ba1f1d62453" + integrity sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA== "@types/filesystem@*": - version "0.0.32" - resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.32.tgz#307df7cc084a2293c3c1a31151b178063e0a8edf" - integrity sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ== + version "0.0.33" + resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.33.tgz#808e0048221426b30341a6b19a8193213c72ca55" + integrity sha512-2KedRPzwu2K528vFkoXnnWdsG0MtUwPjuA7pRy4vKxlxHEe8qUDZibYHXJKZZr2Cl/ELdCWYqyb/MKwsUuzBWw== dependencies: "@types/filewriter" "*" "@types/filewriter@*": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.29.tgz#a48795ecadf957f6c0d10e0c34af86c098fa5bee" - integrity sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ== + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.30.tgz#1be7733877589d57c653b8356cefe5fe7c74b89e" + integrity sha512-lB98tui0uxc7erbj0serZfJlHKLNJHwBltPnbmO1WRpL5T325GOHRiQfr2E29V2q+S1brDO63Fpdt6vb3bES9Q== "@types/har-format@*": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.7.tgz#debfe36378f26c4fc2abca1df99f00a8ff94fd29" - integrity sha512-/TPzUG0tJn5x1TUcVLlDx2LqbE58hyOzDVAc9kf8SpOEmguHjU6bKUyfqb211AdqLOmU/SNyXvLKPNP5qTlfRw== + version "1.2.13" + resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.13.tgz#7ef32562d433166e1ad14e3eb6fcf5c00c8a0ead" + integrity sha512-PwBsCBD3lDODn4xpje3Y1di0aDJp4Ww7aSfMRVw6ysnxD4I7Wmq2mBkSKaDtN403hqH5sp6c9xQUvFYY3+lkBg== -"@types/json-schema@*", "@types/json-schema@^7.0.8": - version "7.0.9" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" - integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.13" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85" + integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ== -"@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" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== - -"@types/mocha@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.0.0.tgz#3205bcd15ada9bc681ac20bef64e9e6df88fd297" - integrity sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA== +"@types/mocha@^10.0.1": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.2.tgz#96d63314255540a36bf24da094cce7a13668d73b" + integrity sha512-NaHL0+0lLNhX6d9rs+NSt97WH/gIlRHmszXbQ/8/MV/eVcFNdeJ/GYhrFuUc8K7WuPhRhTSdMkCp8VMzhUq85w== "@types/node@*": - version "16.9.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.6.tgz#040a64d7faf9e5d9e940357125f0963012e66f04" - integrity sha512-YHUZhBOMTM3mjFkXVcK+WwAcYmyhe1wL4lfqNtzI0b3qAy7yuSetnM7QJazgE5PFmgVTNGiLOgRFfJMqW7XpSQ== - -"@types/node@^18.7.16": - version "18.7.16" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.16.tgz#0eb3cce1e37c79619943d2fd903919fc30850601" - integrity sha512-EQHhixfu+mkqHMZl1R2Ovuvn47PUw18azMJOTwSZr9/fhzHNGXAJ0ma0dayRVchprpCj0Kc1K1xKoWaATWF1qg== - -"@typescript-eslint/eslint-plugin@^5.36.2": - version "5.36.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.36.2.tgz#6df092a20e0f9ec748b27f293a12cb39d0c1fe4d" - integrity sha512-OwwR8LRwSnI98tdc2z7mJYgY60gf7I9ZfGjN5EjCwwns9bdTuQfAXcsjSB2wSQ/TVNYSGKf4kzVXbNGaZvwiXw== - dependencies: - "@typescript-eslint/scope-manager" "5.36.2" - "@typescript-eslint/type-utils" "5.36.2" - "@typescript-eslint/utils" "5.36.2" + version "20.7.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.7.1.tgz#06d732ead0bd5ad978ef0ea9cbdeb24dc8717514" + integrity sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg== + +"@types/node@^18.11.18": + version "18.18.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.0.tgz#bd19d5133a6e5e2d0152ec079ac27c120e7f1763" + integrity sha512-3xA4X31gHT1F1l38ATDIL9GpRLdwVhnEFC8Uikv5ZLlXATwrCYyPq7ZWHxzxc3J/30SUiwiYT+bQe0/XvKlWbw== + +"@types/semver@^7.3.12": + version "7.5.3" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.3.tgz#9a726e116beb26c24f1ccd6850201e1246122e04" + integrity sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw== + +"@types/webextension-polyfill@^0.10.1": + version "0.10.4" + resolved "https://registry.yarnpkg.com/@types/webextension-polyfill/-/webextension-polyfill-0.10.4.tgz#09feeb2d8f04ac0a28818ade8aeeb4ab9fbafebb" + integrity sha512-pvEIqAZEbJRzaqTaWq3xlUoMWa3+euZHHz+VZHCzHWW+jOf8qLOq9wXy38U+WiPG3108SJC/wNc1X6vPC5TcjQ== + +"@typescript-eslint/eslint-plugin@^5.48.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" - functional-red-black-tree "^1.0.1" + graphemer "^1.4.0" ignore "^5.2.0" - regexpp "^3.2.0" + natural-compare-lite "^1.4.0" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/parser@^5.36.2": - version "5.36.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.36.2.tgz#3ddf323d3ac85a25295a55fcb9c7a49ab4680ddd" - integrity sha512-qS/Kb0yzy8sR0idFspI9Z6+t7mqk/oRjnAYfewG+VN73opAUvmYL3oPIMmgOX6CnQS6gmVIXGshlb5RY/R22pA== +"@typescript-eslint/parser@^5.48.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== dependencies: - "@typescript-eslint/scope-manager" "5.36.2" - "@typescript-eslint/types" "5.36.2" - "@typescript-eslint/typescript-estree" "5.36.2" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.36.2": - version "5.36.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.36.2.tgz#a75eb588a3879ae659514780831370642505d1cd" - integrity sha512-cNNP51L8SkIFSfce8B1NSUBTJTu2Ts4nWeWbFrdaqjmn9yKrAaJUBHkyTZc0cL06OFHpb+JZq5AUHROS398Orw== +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== dependencies: - "@typescript-eslint/types" "5.36.2" - "@typescript-eslint/visitor-keys" "5.36.2" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/type-utils@5.36.2": - version "5.36.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.36.2.tgz#752373f4babf05e993adf2cd543a763632826391" - integrity sha512-rPQtS5rfijUWLouhy6UmyNquKDPhQjKsaKH0WnY6hl/07lasj8gPaH2UD8xWkePn6SC+jW2i9c2DZVDnL+Dokw== +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== dependencies: - "@typescript-eslint/typescript-estree" "5.36.2" - "@typescript-eslint/utils" "5.36.2" + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.36.2": - version "5.36.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.36.2.tgz#a5066e500ebcfcee36694186ccc57b955c05faf9" - integrity sha512-9OJSvvwuF1L5eS2EQgFUbECb99F0mwq501w0H0EkYULkhFa19Qq7WFbycdw1PexAc929asupbZcgjVIe6OK/XQ== +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/typescript-estree@5.36.2": - version "5.36.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.36.2.tgz#0c93418b36c53ba0bc34c61fe9405c4d1d8fe560" - integrity sha512-8fyH+RfbKc0mTspfuEjlfqA4YywcwQK2Amcf6TDOwaRLg7Vwdu4bZzyvBZp4bjt1RRjQ5MDnOZahxMrt2l5v9w== +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== dependencies: - "@typescript-eslint/types" "5.36.2" - "@typescript-eslint/visitor-keys" "5.36.2" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.36.2": - version "5.36.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.36.2.tgz#b01a76f0ab244404c7aefc340c5015d5ce6da74c" - integrity sha512-uNcopWonEITX96v9pefk9DC1bWMdkweeSsewJ6GeC7L6j2t0SJywisgkr9wUTtXk90fi2Eljj90HSHm3OGdGRg== +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== dependencies: + "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.36.2" - "@typescript-eslint/types" "5.36.2" - "@typescript-eslint/typescript-estree" "5.36.2" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" eslint-scope "^5.1.1" - eslint-utils "^3.0.0" + semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.36.2": - version "5.36.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.36.2.tgz#2f8f78da0a3bad3320d2ac24965791ac39dace5a" - integrity sha512-BtRvSR6dEdrNt7Net2/XDjbYKU5Ml6GqJgVfXT0CxTCJlnIqK7rAGreuWKMT2t8cFUT2Msv5oxw0GMRD7T5J7A== +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== dependencies: - "@typescript-eslint/types" "5.36.2" + "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== - -"@webassemblyjs/ast@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" - integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== - dependencies: - "@webassemblyjs/helper-numbers" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - -"@webassemblyjs/floating-point-hex-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" - integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== - -"@webassemblyjs/helper-api-error@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" - integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== - -"@webassemblyjs/helper-buffer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" - integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== - -"@webassemblyjs/helper-numbers@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" - integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== - dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" +"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" + integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" + integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" "@xtuc/long" "4.2.2" -"@webassemblyjs/helper-wasm-bytecode@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" - integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== -"@webassemblyjs/helper-wasm-section@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" - integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== +"@webassemblyjs/helper-wasm-section@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" + integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" -"@webassemblyjs/ieee754@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" - integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" - integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" - integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== - -"@webassemblyjs/wasm-edit@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" - integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/helper-wasm-section" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-opt" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - "@webassemblyjs/wast-printer" "1.11.1" - -"@webassemblyjs/wasm-gen@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" - integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wasm-opt@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" - integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - -"@webassemblyjs/wasm-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" - integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wast-printer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" - integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== - dependencies: - "@webassemblyjs/ast" "1.11.1" +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" + integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-opt" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/wast-printer" "1.11.6" + +"@webassemblyjs/wasm-gen@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" + integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" + integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + +"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" + integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" + integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== + dependencies: + "@webassemblyjs/ast" "1.11.6" "@xtuc/long" "4.2.2" -"@webpack-cli/configtest@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" - integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== +"@webpack-cli/configtest@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== -"@webpack-cli/info@^1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" - integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== - dependencies: - envinfo "^7.7.3" +"@webpack-cli/info@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== -"@webpack-cli/serve@^1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" - integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== +"@webpack-cli/serve@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -640,48 +692,33 @@ abab@^2.0.6: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== +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 "^7.1.1" - acorn-walk "^7.1.1" + acorn "^8.1.0" + acorn-walk "^8.0.2" -acorn-import-assertions@^1.7.6: - version "1.7.6" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz#580e3ffcae6770eebeec76c3b9723201e9d01f78" - integrity sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA== +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== -acorn-jsx@^5.3.1: +acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - -acorn-walk@^8.1.1: +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== -acorn@^7.1.1, acorn@^7.4.0: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.4.1: - version "8.5.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" - integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== - -acorn@^8.7.1: - version "8.8.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" - integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== +acorn@^8.1.0, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== agent-base@6: version "6.0.2" @@ -698,12 +735,26 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -713,27 +764,22 @@ ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.1: - version "8.6.3" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.3.tgz#11a66527761dc3e9a3845ea775d2d3c0414e8764" - integrity sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw== +ajv@^8.0.0, ajv@^8.9.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" require-from-string "^2.0.2" uri-js "^4.2.2" -ansi-colors@4.1.1, ansi-colors@^4.1.1: +ansi-colors@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -ansi-regex@^5.0.0: +ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -753,9 +799,9 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: color-convert "^2.0.1" anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -770,7 +816,7 @@ append-transform@^2.0.0: archy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== arg@^4.1.0: version "4.1.3" @@ -799,11 +845,6 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - async-mutex@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.0.tgz#ae8048cd4d04ace94347507504b3cf15e631c25f" @@ -814,7 +855,7 @@ async-mutex@^0.4.0: asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== balanced-match@^1.0.0: version "1.0.2" @@ -834,33 +875,34 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.1, braces@~3.0.2: +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^4.14.5, browserslist@^4.16.6: - version "4.17.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.1.tgz#a98d104f54af441290b7d592626dd541fa642eb9" - integrity sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ== +browserslist@^4.14.5, browserslist@^4.21.9: + version "4.22.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.0.tgz#6adc8116589ccea8a99d0df79c5de2436199abdb" + integrity sha512-v+Jcv64L2LbfTC6OnRcaxtqJNJuQAVhZKSJfR/6hn7lhnChUXl4amwVviqN1k411BB+3rRoKMitELRn1CojeRA== dependencies: - caniuse-lite "^1.0.30001259" - electron-to-chromium "^1.3.846" - escalade "^3.1.1" - nanocolors "^0.1.5" - node-releases "^1.1.76" + caniuse-lite "^1.0.30001539" + electron-to-chromium "^1.4.530" + node-releases "^2.0.13" + update-browserslist-db "^1.0.13" buffer-from@^1.0.0: version "1.1.2" @@ -888,30 +930,29 @@ camelcase@^5.0.0, camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.0.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" - integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001259: - version "1.0.30001260" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001260.tgz#e3be3f34ddad735ca4a2736fa9e768ef34316270" - integrity sha512-Fhjc/k8725ItmrvW5QomzxLeojewxvqiYCKeFcfFEhut28IVLdpHU19dneOmltZQIE5HNbawj1HYD+1f2bM1Dg== - dependencies: - nanocolors "^0.1.0" +caniuse-lite@^1.0.30001539: + version "1.0.30001550" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001550.tgz#6ec6a2239eb2a8123cc26cfe0571db5c79eb8669" + integrity sha512-p82WjBYIypO0ukTsd/FG3Xxs+4tFeaY9pfT4amQL8KWtYH7H9nYwReGAbMTJ0hsmRO8IfDtsS6p3ZWj8+1c2RQ== -chai@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" - integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== +chai@^4.3.7: + version "4.3.10" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384" + integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g== dependencies: assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^3.0.1" - get-func-name "^2.0.0" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" pathval "^1.1.1" - type-detect "^4.0.5" + type-detect "^4.0.8" -chalk@^2.0.0: +chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -928,15 +969,17 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= +check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" -chokidar@3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: anymatch "~3.1.2" braces "~3.0.2" @@ -1002,7 +1045,7 @@ color-convert@^2.0.1: color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" @@ -1010,9 +1053,9 @@ color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^2.0.14: - version "2.0.19" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== combined-stream@^1.0.8: version "1.0.8" @@ -1021,32 +1064,35 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^7.0.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== - commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== create-require@^1.1.0: version "1.1.1" @@ -1088,21 +1134,7 @@ data-urls@^3.0.2: whatwg-mimetype "^3.0.0" whatwg-url "^11.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - -debug@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -1112,41 +1144,41 @@ debug@^4.3.4: decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== decamelize@^4.0.0: version "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.3.1: - version "10.3.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" - integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== +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== -deep-eql@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" - integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== +deep-eql@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== dependencies: type-detect "^4.0.0" -deep-is@^0.1.3, deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== default-require-extensions@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.0.tgz#e03f93aac9b2b6443fc52e5e4a37b3ad9ad8df96" - integrity sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.1.tgz#bfae00feeaeada68c2ae256c62540f60b80625bd" + integrity sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw== dependencies: strip-bom "^4.0.0" delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== diff@5.0.0: version "5.0.0" @@ -1186,62 +1218,55 @@ dotenv-defaults@^2.0.2: dependencies: dotenv "^8.2.0" -dotenv-webpack@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/dotenv-webpack/-/dotenv-webpack-7.0.3.tgz#f50ec3c7083a69ec6076e110566720003b7b107b" - integrity sha512-O0O9pOEwrk+n1zzR3T2uuXRlw64QxHSPeNN1GaiNBloQFNaCUL9V8jxSVz4jlXXFP/CIqK8YecWf8BAvsSgMjw== +dotenv-webpack@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/dotenv-webpack/-/dotenv-webpack-8.0.1.tgz#6656550460a8076fab20e5ac2eac867e72478645" + integrity sha512-CdrgfhZOnx4uB18SgaoP9XHRN2v48BbjuXQsZY5ixs5A8579NxQkmMxRtI7aTwSiSQcM2ao12Fdu+L3ZS3bG4w== dependencies: dotenv-defaults "^2.0.2" -dotenv@^16.0.1: - version "16.0.2" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.2.tgz#0b0f8652c016a3858ef795024508cddc4bffc5bf" - integrity sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA== +dotenv@^16.0.3: + version "16.3.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== dotenv@^8.2.0: version "8.6.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== -electron-to-chromium@^1.3.846: - version "1.3.848" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.848.tgz#94cc196e496f33c0d71cd98561448f10018584cc" - integrity sha512-wchRyBcdcmibioggdO7CbMT5QQ4lXlN/g7Mkpf1K2zINidnqij6EVu94UIZ+h5nB2S9XD4bykqFv9LonAWLFyw== +electron-to-chromium@^1.4.530: + version "1.4.532" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.532.tgz#44454731e26f2c8c14e88cca0d073f0761784f5e" + integrity sha512-piIR0QFdIGKmOJTSNg5AwxZRNWQSXlRYycqDB9Srstx4lip8KpcmRxVP6zuFWExWziHYZpJ0acX7TxqX95KBpg== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -enhanced-resolve@^5.0.0, enhanced-resolve@^5.8.0: - version "5.8.3" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz#6d552d465cce0423f5b3d718511ea53826a7b2f0" - integrity sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA== +enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" -enquirer@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" - integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== - dependencies: - ansi-colors "^4.1.1" - entities@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" - integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== envinfo@^7.7.3: - version "7.8.1" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" - integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + version "7.10.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.10.0.tgz#55146e3909cc5fe63c22da63fb15b05aeac35b13" + integrity sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw== -es-module-lexer@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.7.1.tgz#c2c8e0f46f2df06274cdaf0dd3f3b33e0a0b267d" - integrity sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw== +es-module-lexer@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.1.tgz#c1b0dd5ada807a3b3155315911f364dc4e909db1" + integrity sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q== es6-error@^4.0.1: version "4.1.1" @@ -1261,24 +1286,23 @@ escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + 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" - optionator "^0.8.1" optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@^8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" - integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== +eslint-config-prettier@^8.6.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" + integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== eslint-scope@5.1.1, eslint-scope@^5.1.1: version "5.1.1" @@ -1288,115 +1312,96 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: - eslint-visitor-keys "^2.0.0" - -eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + esrecurse "^4.3.0" + estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint-webpack-plugin@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/eslint-webpack-plugin/-/eslint-webpack-plugin-3.0.1.tgz#0990a80e9d5927e7e68365f93426cb340679e88c" - integrity sha512-PAHHDjCg2yWBNoiBPYLZWcv+M83urkslQKER7XvK84lo5YLcihJK6qwnCH2Fkt3eVdX+G1iyGZRlKsIhTiczHw== +eslint-webpack-plugin@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz#1978cdb9edc461e4b0195a20da950cf57988347c" + integrity sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w== dependencies: - "@types/eslint" "^7.2.14" - jest-worker "^27.0.6" - micromatch "^4.0.4" + "@types/eslint" "^7.29.0 || ^8.4.1" + jest-worker "^28.0.2" + micromatch "^4.0.5" normalize-path "^3.0.0" - schema-utils "^3.1.0" - -eslint@^7.32.0: - version "7.32.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" - integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== - dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.3" - "@humanwhocodes/config-array" "^0.5.0" - ajv "^6.10.0" + schema-utils "^4.0.0" + +eslint@^8.31.0: + version "8.50.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.50.0.tgz#2ae6015fee0240fcd3f83e1e25df0287f487d6b2" + integrity sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.2" + "@eslint/js" "8.50.0" + "@humanwhocodes/config-array" "^0.11.11" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" - debug "^4.0.1" + debug "^4.3.2" doctrine "^3.0.0" - enquirer "^2.3.5" escape-string-regexp "^4.0.0" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - esquery "^1.4.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.1.2" - globals "^13.6.0" - ignore "^4.0.6" - import-fresh "^3.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - js-yaml "^3.13.1" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" - minimatch "^3.0.4" + minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" - strip-json-comments "^3.1.0" - table "^6.0.9" + optionator "^0.9.3" + strip-ansi "^6.0.1" text-table "^0.2.0" - v8-compile-cache "^2.0.3" esm@^3.2.25: version "3.2.25" resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^7.4.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" 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== -esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" @@ -1413,9 +1418,9 @@ estraverse@^4.1.1: integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0, estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" @@ -1440,9 +1445,9 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@^3.2.9: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -1455,10 +1460,10 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fast-safe-stringify@^2.0.6: version "2.1.1" @@ -1466,14 +1471,14 @@ fast-safe-stringify@^2.0.6: integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== fastest-levenshtein@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" - integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== dependencies: reusify "^1.0.4" @@ -1500,7 +1505,7 @@ find-cache-dir@^3.2.0: make-dir "^3.0.2" pkg-dir "^4.1.0" -find-up@5.0.0: +find-up@5.0.0, find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== @@ -1517,11 +1522,12 @@ find-up@^4.0.0, find-up@^4.1.0: path-exists "^4.0.0" flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + version "3.1.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.1.0.tgz#0e54ab4a1a60fe87e2946b6b00657f1c99e1af3f" + integrity sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew== dependencies: - flatted "^3.1.0" + flatted "^3.2.7" + keyv "^4.5.3" rimraf "^3.0.2" flat@^5.0.2: @@ -1529,10 +1535,10 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -flatted@^3.1.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" - integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== +flatted@^3.2.7: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== foreground-child@^2.0.0: version "2.0.0" @@ -1559,23 +1565,18 @@ fromentries@^1.2.0: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -1586,10 +1587,10 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= +get-func-name@^2.0.0, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== get-package-type@^0.1.0: version "0.1.0" @@ -1603,15 +1604,22 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@7.1.7: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -1621,14 +1629,14 @@ glob@7.1.7: path-is-absolute "^1.0.0" glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" @@ -1637,10 +1645,10 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.6.0, globals@^13.9.0: - version "13.11.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.11.0.tgz#40ef678da117fe7bd2e28f1fab24951bd0255be7" - integrity sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g== +globals@^13.19.0: + version "13.22.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.22.0.tgz#0c9fcb9c48a2494fbb5edbfee644285543eba9d8" + integrity sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw== dependencies: type-fest "^0.20.2" @@ -1656,20 +1664,20 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.4: - version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" - integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== +graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" @@ -1732,17 +1740,12 @@ iconv-lite@0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -1751,9 +1754,9 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: resolve-from "^4.0.0" import-local@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" - integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== dependencies: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" @@ -1761,7 +1764,7 @@ import-local@^3.0.2: imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== indent-string@^4.0.0: version "4.0.0" @@ -1771,7 +1774,7 @@ indent-string@^4.0.0: inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" @@ -1781,10 +1784,10 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -interpret@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" - integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== is-binary-path@~2.1.0: version "2.1.0" @@ -1793,36 +1796,24 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-core-module@^2.2.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.6.0.tgz#d7553b2526fe59b92ba3e40c8df757ec8a709e19" - integrity sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ== +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== dependencies: has "^1.0.3" is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-glob@^4.0.3: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -1834,6 +1825,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" @@ -1859,7 +1855,7 @@ is-stream@^2.0.0: is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== is-unicode-supported@^0.1.0: version "0.1.0" @@ -1874,12 +1870,12 @@ is-windows@^1.0.2: isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== isomorphic-fetch@^3.0.0: version "3.0.0" @@ -1889,10 +1885,10 @@ isomorphic-fetch@^3.0.0: node-fetch "^2.6.1" whatwg-fetch "^3.4.1" -istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.0-alpha.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.1.tgz#e8900b3ed6069759229cf30f7067388d148aeb5e" - integrity sha512-GvCYYTxaCPqwMjobtVcVKvSHtAGe48MNhGjpK8LtVF8K0ISX7hCKl85LgtuaSneWVyQmaGcW3iXVV3GaZSLpmQ== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== istanbul-lib-hook@^3.0.0: version "3.0.0" @@ -1912,48 +1908,56 @@ istanbul-lib-instrument@^4.0.0: semver "^6.3.0" istanbul-lib-processinfo@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz#e1426514662244b2f25df728e8fd1ba35fe53b9c" - integrity sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw== + version "2.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz#366d454cd0dcb7eb6e0e419378e60072c8626169" + integrity sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg== dependencies: archy "^1.0.0" - cross-spawn "^7.0.0" - istanbul-lib-coverage "^3.0.0-alpha.1" - make-dir "^3.0.0" + cross-spawn "^7.0.3" + istanbul-lib-coverage "^3.2.0" p-map "^3.0.0" rimraf "^3.0.0" - uuid "^3.3.3" + uuid "^8.3.2" istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== dependencies: istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" + make-dir "^4.0.0" supports-color "^7.1.0" istanbul-lib-source-maps@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" - integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== dependencies: debug "^4.1.1" istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" istanbul-reports@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" - integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== + version "3.1.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" + integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jest-worker@^27.0.6: - version "27.2.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.2.0.tgz#11eef39f1c88f41384ca235c2f48fe50bc229bc0" - integrity sha512-laB0ZVIBz+voh/QQy9dmUuuDsadixeerrKqyVpgPz+CCWiOYjOBabUXHIXZhsdvkWbLqSHbgkAHWl5cg24Q6RA== +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^28.0.2: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" + integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== dependencies: "@types/node" "*" merge-stream "^2.0.0" @@ -1964,7 +1968,7 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@4.1.0: +js-yaml@4.1.0, js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== @@ -1982,20 +1986,20 @@ js-yaml@^3.13.1: jsdom-global@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/jsdom-global/-/jsdom-global-3.0.2.tgz#6bd299c13b0c4626b2da2c0393cd4385d606acb9" - integrity sha1-a9KZwTsMRiay2iwDk81DhdYGrLk= + integrity sha512-t1KMcBkz/pT5JrvcJbpUR2u/w1kO9jXctaaGJ0vZDzwFnIvGWw9IDSRciT83kIs8Bnw4qpOl8bQK08V01YgMPg== -jsdom@^20.0.0: - version "20.0.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.0.tgz#882825ac9cc5e5bbee704ba16143e1fa78361ebf" - integrity sha512-x4a6CKCgx00uCmP+QakBDFXwjAJ69IkkIWHmtmjd3wvXPcdOS44hfX2vqkOQrVrq8l9DhNNADZRXaCEWvgXtVA== +jsdom@^20.0.3: + 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.7.1" - acorn-globals "^6.0.0" + 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.3.1" + decimal.js "^10.4.2" domexception "^4.0.0" escodegen "^2.0.0" form-data "^4.0.0" @@ -2003,18 +2007,17 @@ jsdom@^20.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.0" - parse5 "^7.0.0" + nwsapi "^2.2.2" + parse5 "^7.1.1" saxes "^6.0.0" symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^3.0.0" + 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.8.0" + ws "^8.11.0" xml-name-validator "^4.0.0" jsesc@^2.5.1: @@ -2022,10 +2025,15 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" @@ -2040,19 +2048,19 @@ json-schema-traverse@^1.0.0: json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" +json5@^2.2.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +keyv@^4.5.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.3.tgz#00873d2b046df737963157bd04f294ca818c9c25" + integrity sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug== + dependencies: + json-buffer "3.0.1" kind-of@^6.0.2: version "6.0.3" @@ -2067,18 +2075,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - loader-runner@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" - integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== locate-path@^5.0.0: version "5.0.0" @@ -2094,26 +2094,16 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - lodash.flattendeep@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" - integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= + integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ== lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= - log-symbols@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" @@ -2122,10 +2112,24 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -loglevel@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114" - integrity sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA== +loglevel@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" + integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== + +loupe@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" + integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== + dependencies: + get-func-name "^2.0.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" lru-cache@^6.0.0: version "6.0.0" @@ -2141,6 +2145,13 @@ make-dir@^3.0.0, make-dir@^3.0.2: dependencies: semver "^6.0.0" +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -2156,70 +2167,68 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromatch@^4.0.0, micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== +micromatch@^4.0.0, micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: - braces "^3.0.1" - picomatch "^2.2.3" + braces "^3.0.2" + picomatch "^2.3.1" -mime-db@1.49.0: - version "1.49.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" - integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== mime-types@^2.1.12, mime-types@^2.1.27: - version "2.1.32" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" - integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: - mime-db "1.49.0" + mime-db "1.52.0" -minimatch@3.0.4, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== dependencies: - brace-expansion "^1.1.7" + brace-expansion "^2.0.1" -minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -mocha@^9.1.1: - version "9.1.1" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.1.tgz#33df2eb9c6262434630510c5f4283b36efda9b61" - integrity sha512-0wE74YMgOkCgBUj8VyIDwmLUjTsS13WV1Pg7l0SHea2qzZzlq7MDnfbPsHKcELBRk3+izEVkRofjmClpycudCA== +mocha@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== dependencies: - "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" - chokidar "3.5.2" - debug "4.3.1" + chokidar "3.5.3" + debug "4.3.4" diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" - glob "7.1.7" - growl "1.10.5" + glob "7.2.0" he "1.2.0" js-yaml "4.1.0" log-symbols "4.1.0" - minimatch "3.0.4" + minimatch "5.0.1" ms "2.1.3" - nanoid "3.1.23" + nanoid "3.3.3" serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" - which "2.0.2" - wide-align "1.1.3" - workerpool "6.1.5" + workerpool "6.2.1" yargs "16.2.0" yargs-parser "20.2.4" yargs-unparser "2.0.0" @@ -2234,20 +2243,20 @@ ms@2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -nanocolors@^0.1.0, nanocolors@^0.1.5: - version "0.1.12" - resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.1.12.tgz#8577482c58cbd7b5bb1681db4cf48f11a87fd5f6" - integrity sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ== +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== -nanoid@3.1.23: - version "3.1.23" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" - integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== neo-async@^2.6.2: version "2.6.2" @@ -2255,9 +2264,9 @@ neo-async@^2.6.2: integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== node-fetch@^2.6.1: - version "2.6.5" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd" - integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ== + 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" @@ -2268,20 +2277,20 @@ node-preload@^0.2.1: dependencies: process-on-spawn "^1.0.0" -node-releases@^1.1.76: - version "1.1.76" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.76.tgz#df245b062b0cafbd5282ab6792f7dccc2d97f36e" - integrity sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA== +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -nwsapi@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" - integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== +nwsapi@^2.2.2: + version "2.2.7" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" + integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== nyc@^15.1.0: version "15.1.0" @@ -2319,33 +2328,21 @@ nyc@^15.1.0: once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" p-limit@^2.2.0: version "2.3.0" @@ -2354,7 +2351,7 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2, p-limit@^3.1.0: +p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -2404,10 +2401,10 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse5@^7.0.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.1.tgz#4649f940ccfb95d8754f37f73078ea20afe0c746" - integrity sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg== +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" @@ -2419,14 +2416,14 @@ path-exists@^4.0.0: path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6: +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -2441,10 +2438,15 @@ pathval@^1.1.1: resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" @@ -2458,15 +2460,10 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -prettier@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" - integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== +prettier@^2.8.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== process-on-spawn@^1.0.0: version "1.0.0" @@ -2475,20 +2472,20 @@ process-on-spawn@^1.0.0: dependencies: fromentries "^1.2.0" -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - psl@^1.1.33: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +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: version "1.2.3" @@ -2509,29 +2506,24 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -rechoir@^0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" - integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== dependencies: - resolve "^1.9.0" - -regexpp@^3.1.0, regexpp@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + resolve "^1.20.0" release-zalgo@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" - integrity sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA= + integrity sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA== dependencies: es6-error "^4.0.1" require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-from-string@^2.0.2: version "2.0.2" @@ -2543,6 +2535,11 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -2560,13 +2557,14 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve@^1.9.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== +resolve@^1.20.0: + version "1.22.6" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362" + integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw== dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" reusify@^1.0.4: version "1.0.4" @@ -2592,11 +2590,6 @@ safe-buffer@^5.1.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -2609,45 +2602,55 @@ saxes@^6.0.0: 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" - integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== +schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== dependencies: "@types/json-schema" "^7.0.8" ajv "^6.12.5" ajv-keywords "^3.5.2" -semver@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.2.1, semver@^7.3.4: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== +schema-utils@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== dependencies: - lru-cache "^6.0.0" + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" -semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.4, semver@^7.3.7, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" -serialize-javascript@6.0.0, serialize-javascript@^6.0.0: +serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== dependencies: randombytes "^2.1.0" +serialize-javascript@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== shallow-clone@^3.0.0: version "3.0.1" @@ -2669,47 +2672,28 @@ shebang-regex@^3.0.0: integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== signal-exit@^3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.4.tgz#366a4684d175b9cab2081e3681fda3747b6c51d7" - integrity sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q== + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - source-map-support@~0.5.20: - version "0.5.20" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" - integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.5.0: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - 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== -source-map@~0.7.2: - version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== - spawn-wrap@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-2.0.0.tgz#103685b8b8f9b79771318827aa78650a610d457e" @@ -2725,50 +2709,35 @@ spawn-wrap@^2.0.0: sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== string-width@^4.1.0, string-width@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" - integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" + strip-ansi "^6.0.1" -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: - ansi-regex "^5.0.0" + ansi-regex "^5.0.1" strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== strip-bom@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== -strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@3.1.1, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -2794,47 +2763,40 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "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== -table@^6.0.9: - version "6.7.1" - resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" - integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== - dependencies: - ajv "^8.0.1" - lodash.clonedeep "^4.5.0" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.0" - strip-ansi "^6.0.0" - tapable@^2.1.1, tapable@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -terser-webpack-plugin@^5.1.3: - version "5.2.4" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz#ad1be7639b1cbe3ea49fab995cbe7224b31747a1" - integrity sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA== +terser-webpack-plugin@^5.3.7: + version "5.3.9" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" + integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== dependencies: - jest-worker "^27.0.6" - p-limit "^3.1.0" + "@jridgewell/trace-mapping" "^0.3.17" + jest-worker "^27.4.5" schema-utils "^3.1.1" - serialize-javascript "^6.0.0" - source-map "^0.6.1" - terser "^5.7.2" + serialize-javascript "^6.0.1" + terser "^5.16.8" -terser@^5.7.2: - version "5.9.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.9.0.tgz#47d6e629a522963240f2b55fcaa3c99083d2c351" - integrity sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ== +terser@^5.16.8: + version "5.20.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.20.0.tgz#ea42aea62578703e33def47d5c5b93c49772423e" + integrity sha512-e56ETryaQDyebBwJIWYB2TT6f2EZ0fL0sW/JRXNMN26zZdKi2u/E/5my5lG6jNxym6qsrVXfFRmOdV42zlAgLQ== dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" commander "^2.20.0" - source-map "~0.7.2" source-map-support "~0.5.20" test-exclude@^6.0.0: @@ -2849,12 +2811,12 @@ test-exclude@^6.0.0: text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== to-regex-range@^5.0.1: version "5.0.1" @@ -2863,14 +2825,15 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== +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.1.2" + universalify "^0.2.0" + url-parse "^1.5.3" tr46@^3.0.0: version "3.0.0" @@ -2882,24 +2845,24 @@ tr46@^3.0.0: tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -ts-loader@^9.1.2: - version "9.2.6" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.6.tgz#9937c4dd0a1e3dbbb5e433f8102a6601c6615d74" - integrity sha512-QMTC4UFzHmu9wU2VHZEmWWE9cUajjfcdcws+Gh7FhiO+Dy0RnR1bNz0YCHqhI0yRowCE9arVnNxYHqELOy9Hjw== +ts-loader@^9.4.2: + version "9.4.4" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.4.4.tgz#6ceaf4d58dcc6979f84125335904920884b7cee4" + integrity sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w== dependencies: chalk "^4.1.0" enhanced-resolve "^5.0.0" micromatch "^4.0.0" semver "^7.3.4" -ts-node@^10.2.1: - version "10.2.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.2.1.tgz#4cc93bea0a7aba2179497e65bb08ddfc198b3ab5" - integrity sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw== +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== dependencies: - "@cspotcode/source-map-support" "0.6.1" + "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" "@tsconfig/node12" "^1.0.7" "@tsconfig/node14" "^1.0.0" @@ -2910,14 +2873,15 @@ ts-node@^10.2.1: create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tsconfig-paths@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.1.0.tgz#f8ef7d467f08ae3a695335bf1ece088c5538d2c1" - integrity sha512-AHx4Euop/dXFC+Vx589alFba8QItjF+8hf8LtmuiCwHyI4rHXQtOOENaM8kvYf5fR0dRChy3wzWIZ9WbB7FWow== +tsconfig-paths@^4.1.2: + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== dependencies: - json5 "^2.2.1" + json5 "^2.2.2" minimist "^1.2.6" strip-bom "^3.0.0" @@ -2927,9 +2891,9 @@ tslib@^1.8.1: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== tsutils@^3.21.0: version "3.21.0" @@ -2945,14 +2909,7 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - -type-detect@^4.0.0, type-detect@^4.0.5: +type-detect@^4.0.0, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== @@ -2974,15 +2931,23 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.4.3: - version "4.4.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.3.tgz#bdc5407caa2b109efd4f82fe130656f977a29324" - integrity sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA== +typescript@^4.9.4: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== -universalify@^0.1.2: - version "0.1.2" - 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== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" uri-js@^4.2.2: version "4.4.1" @@ -2991,108 +2956,115 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -uuid@^3.3.3: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +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" -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -w3c-xmlserializer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923" - integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg== +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" -watchpack@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce" - integrity sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA== +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" +webextension-polyfill@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz#ccb28101c910ba8cf955f7e6a263e662d744dbb8" + integrity sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + 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-cli@^4.10.0: - version "4.10.0" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" - integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== +webpack-cli@^5.0.1: + version "5.1.4" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== dependencies: "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^1.2.0" - "@webpack-cli/info" "^1.5.0" - "@webpack-cli/serve" "^1.7.0" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" colorette "^2.0.14" - commander "^7.0.0" + commander "^10.0.1" cross-spawn "^7.0.3" + envinfo "^7.7.3" fastest-levenshtein "^1.0.12" import-local "^3.0.2" - interpret "^2.2.0" - rechoir "^0.7.0" + interpret "^3.1.1" + rechoir "^0.8.0" webpack-merge "^5.7.3" webpack-merge@^5.7.3, webpack-merge@^5.8.0: - version "5.8.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" - integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + version "5.9.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.9.0.tgz#dc160a1c4cf512ceca515cc231669e9ddb133826" + integrity sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg== dependencies: clone-deep "^4.0.1" wildcard "^2.0.0" -webpack-sources@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.1.tgz#251a7d9720d75ada1469ca07dbb62f3641a05b6d" - integrity sha512-t6BMVLQ0AkjBOoRTZgqrWm7xbXMBzD+XDq2EZ96+vMfn3qKgsvdXZhbPZ4ElUOpdv4u+iiGe+w3+J75iy/bYGA== - -webpack@^5.53.0: - version "5.53.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.53.0.tgz#f463cd9c6fc1356ae4b9b7ac911fd1f5b2df86af" - integrity sha512-RZ1Z3z3ni44snoWjfWeHFyzvd9HMVYDYC5VXmlYUT6NWgEOWdCNpad5Fve2CzzHoRED7WtsKe+FCyP5Vk4pWiQ== - dependencies: - "@types/eslint-scope" "^3.7.0" - "@types/estree" "^0.0.50" - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/wasm-edit" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - acorn "^8.4.1" - acorn-import-assertions "^1.7.6" +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.75.0: + version "5.88.2" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.2.tgz#f62b4b842f1c6ff580f3fcb2ed4f0b579f4c210e" + integrity sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" + acorn "^8.7.1" + acorn-import-assertions "^1.9.0" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.8.0" - es-module-lexer "^0.7.1" + enhanced-resolve "^5.15.0" + es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.4" - json-parse-better-errors "^1.0.2" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" - schema-utils "^3.1.0" + schema-utils "^3.2.0" tapable "^2.1.1" - terser-webpack-plugin "^5.1.3" - watchpack "^2.2.0" - webpack-sources "^3.2.0" + terser-webpack-plugin "^5.3.7" + watchpack "^2.4.0" + webpack-sources "^3.2.3" whatwg-encoding@^2.0.0: version "2.0.0" @@ -3102,9 +3074,9 @@ whatwg-encoding@^2.0.0: iconv-lite "0.6.3" whatwg-fetch@^3.4.1: - version "3.6.2" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" - integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== + version "3.6.19" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.19.tgz#caefd92ae630b91c07345537e67f8354db470973" + integrity sha512-d67JP4dHSbm2TrpFj8AbO8DnL1JXL5J9u0Kq2xW6d0TFDbCA3Muhdt8orXC22utleTVj7Prqt82baN6RBvnEgw== whatwg-mimetype@^3.0.0: version "3.0.0" @@ -3122,44 +3094,32 @@ whatwg-url@^11.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" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + version "2.0.1" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== -which@2.0.2, which@^2.0.1: +which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" -wide-align@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - wildcard@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" - integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== - -word-wrap@^1.2.3, word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== -workerpool@6.1.5: - version "6.1.5" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581" - integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw== +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== wrap-ansi@^6.2.0: version "6.2.0" @@ -3182,7 +3142,7 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^3.0.0: version "3.0.3" @@ -3194,10 +3154,10 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -ws@^8.8.0: - version "8.8.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" - integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== +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== xml-name-validator@^4.0.0: version "4.0.0" @@ -3219,6 +3179,11 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" diff --git a/packages/ui/.eslintrc b/packages/ui/.eslintrc deleted file mode 100644 index 47b660285..000000000 --- a/packages/ui/.eslintrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "plugins": ["react", "react-hooks"], - "extends": ["prettier"] -} \ No newline at end of file diff --git a/packages/ui/.eslintrc.json b/packages/ui/.eslintrc.json new file mode 100644 index 000000000..50656b9d7 --- /dev/null +++ b/packages/ui/.eslintrc.json @@ -0,0 +1,26 @@ +{ + "plugins": [ + "react", + "react-hooks" + ], + "extends": [ + "react-app", + "react-app/jest", + "prettier" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "module" + }, + "overrides": [ + { + "files": [ + "**/*.stories.*" + ], + "rules": { + "import/no-anonymous-default-export": "off" + } + } + ] +} diff --git a/packages/ui/.nvmrc b/packages/ui/.nvmrc index 9f4189900..b714151ef 100644 --- a/packages/ui/.nvmrc +++ b/packages/ui/.nvmrc @@ -1 +1 @@ -v14.17.6 \ No newline at end of file +v18.18.0 \ No newline at end of file diff --git a/packages/ui/LICENSE b/packages/ui/LICENSE index ef487ce39..0d52353d8 100644 --- a/packages/ui/LICENSE +++ b/packages/ui/LICENSE @@ -1,4 +1,4 @@ -All copyrights (including licenses) are owned by Virtual Privacy AG (“BlockWallet”). +All copyrights (including licenses) are owned by Business Variety Limited (3189974). The BlockWallet owns all legal rights, title, and interest in and to the work, software, application, source code, documentation, and any other documents in this repository (collectively, the “Software”), including any intellectual property rights which subsist in the Software (whether those rights happen to be registered or not, and wherever in the world those rights may exist), whether in source code or any other form. diff --git a/packages/ui/Makefile b/packages/ui/Makefile index 3513b7c0b..498632135 100644 --- a/packages/ui/Makefile +++ b/packages/ui/Makefile @@ -1,27 +1,46 @@ INLINE_RUNTIME_CHUNK := false +BROWSER ?= chrome test/ui: yarn test --testTimeout 10000 build/ui: - @echo 'Building app...' + @echo 'Building BlockWallet UI' @rm -rf dist @yarn build:tailwind @node webpack/scripts/build.js +ifeq ($(BROWSER), firefox) + $(MAKE) copy/firefox --no-print-directory +else + $(MAKE) copy/chrome --no-print-directory +endif + @rm -rf build + + +copy/firefox: + @mkdir -p ../../dist-firefox + @cp -r build/* ../../dist-firefox + @cp ../../dist-firefox/index.html ../../dist-firefox/tab.html + @mv ../../dist-firefox/index.html ../../dist-firefox/popup.html + +copy/chrome: @mkdir -p ../../dist @cp -r build/* ../../dist - @rm -rf build @cp ../../dist/index.html ../../dist/tab.html @mv ../../dist/index.html ../../dist/popup.html +build/manifest: + @echo 'Generating manifest.json for $(BROWSER)...' + @node webpack/scripts/manifest.js $(BROWSER) + depcheck: @npx depcheck -version/patch: +version/patch: @yarn version --patch -version/minor: +version/minor: @yarn version --minor version/major: diff --git a/packages/ui/package.json b/packages/ui/package.json index 48dd9ee5a..7045bebef 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,63 +1,70 @@ { "name": "@block-wallet/ui", - "version": "0.6.0", + "version": "0.8.0", "private": true, "dependencies": { "@block-wallet/explorer-link": "https://github.com/Block-Wallet/explorer-link", "@block-wallet/phishing-prevention": "git+https://github.com/block-wallet/phishing-prevention#4708461673825816104b8a6cf5260ecb29b75c61", "@hookform/resolvers": "^2.9.3", + "@keystonehq/animated-qr": "^0.8.6", "@tailwindcss/forms": "^0.5.2", + "@types/react-virtualized": "^9.21.29", "bfj": "^7.0.2", "classnames": "^2.2.6", "color-hash": "^2.0.1", + "crypto-browserify": "^3.12.0", "ethereumjs-util": "^7.1.5", "ethers": "^5.6.9", - "loglevel": "^1.7.1", - "lottie-web": "^5.9.6", + "https-browserify": "^1.0.0", + "loglevel": "^1.8.1", + "lottie-web": "^5.12.2", "qrcode.react": "^3.1.0", "react": "^18.2.0", - "react-app-polyfill": "^2.0.0", + "react-app-polyfill": "^3.0.0", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.2.0", "react-dom-confetti": "^0.2.0", "react-error-boundary": "^3.1.3", "react-hook-form": "^7.33.1", - "react-icons": "^4.4.0", + "react-hotkeys-hook": "^4.3.8", + "react-icons": "^4.12.0", "react-idle-timer": "^5.4.1", - "react-json-view": "^1.21.3", - "react-modal": "^3.12.1", + "react-json-view-lite": "^0.9.5", + "react-modal": "^3.16.1", "react-password-strength-bar": "^0.4.1", "react-refresh": "^0.13.0", "react-router-dom": "^5.2.0", "react-router-last-location": "^2.0.1", "react-transition-group": "^4.4.5", - "react-virtualized-auto-sizer": "^1.0.6", + "react-virtualized": "^9.22.5", + "react-virtualized-auto-sizer": "^1.0.20", "react-window": "^1.8.6", - "styled-components": "^5.3.3", + "stream-http": "^3.2.0", + "styled-components": "^6.0.8", "use-custom-compare": "^1.2.0", "uuid": "^8.3.2", + "webextension-polyfill": "^0.10.0", "yup": "^0.32.11" }, "devDependencies": { - "@babel/core": "7.17.12", - "@babel/plugin-syntax-flow": "^7.16.7", - "@babel/plugin-transform-react-jsx": "^7.18.10", - "@pmmmwh/react-refresh-webpack-plugin": "0.4.2", + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", "@svgr/webpack": "5.5.0", "@testing-library/dom": "^8.16.0", - "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^13.3.0", - "@testing-library/user-event": "^14.2.5", + "@testing-library/jest-dom": "^5.17.0", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", "@types/chrome": "^0.0.193", "@types/classnames": "^2.2.11", "@types/color-hash": "^1.0.0", - "@types/jest": "^26.0.15", + "@types/jest": "^27.5.2", "@types/qrcode.react": "^1.0.1", - "@types/react": "^18.0.15", - "@types/react-dom": "^18.0.6", + "@types/react": "^18.2.22", + "@types/react-dom": "^18.2.7", "@types/react-lottie": "^1.2.5", - "@types/react-modal": "^3.10.6", + "@types/react-modal": "^3.16.0", "@types/react-router-dom": "^5.3.0", "@types/react-transition-group": "^4.4.5", "@types/react-virtualized-auto-sizer": "^1.0.1", @@ -65,23 +72,26 @@ "@types/styled-components": "^5.1.9", "@types/uuid": "^8.3.4", "@types/w3c-web-hid": "^1.0.3", - "@typescript-eslint/eslint-plugin": "^5.30.6", - "@typescript-eslint/parser": "^5.36.2", + "@types/webextension-polyfill": "^0.10.1", + "@typescript-eslint/eslint-plugin": "^6.7.2", + "@typescript-eslint/parser": "^6.7.2", + "assert": "^2.1.0", "autoprefixer": "^10.4.2", "babel-eslint": "^10.1.0", - "babel-jest": "^26.6.0", - "babel-loader": "8.2.5", - "babel-plugin-named-asset-import": "^0.3.7", - "babel-preset-react-app": "^10.0.0", - "camelcase": "^6.1.0", - "case-sensitive-paths-webpack-plugin": "2.4.0", - "chokidar-cli": "^2.1.0", - "cra-build-watch": "^3.4.0", - "css-loader": "4.3.0", - "dotenv-expand": "5.1.0", + "babel-jest": "^27.4.2", + "babel-loader": "^8.2.3", + "babel-plugin-named-asset-import": "^0.3.8", + "babel-preset-react-app": "^10.0.1", + "buffer": "^6.0.3", + "camelcase": "^6.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "css-loader": "^6.5.1", + "css-minimizer-webpack-plugin": "^3.2.0", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", "eslint": "^8.9.0", "eslint-config-prettier": "^8.5.0", - "eslint-config-react-app": "^7.0.0", + "eslint-config-react-app": "^7.0.1", "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-import": "^2.25.4", "eslint-plugin-jest": "^24.4.2", @@ -91,45 +101,47 @@ "eslint-plugin-testing-library": "^5.0.5", "eslint-webpack-plugin": "^3.1.1", "file-loader": "6.2.0", - "fs-extra": "^9.0.1", - "html-webpack-plugin": "4.5.0", - "jest": "26.6.0", + "fs-extra": "^10.0.0", + "html-webpack-plugin": "^5.5.0", + "jest": "^27.4.3", "jest-canvas-mock": "^2.3.1", "jest-chrome": "^0.7.1", "jest-circus": "26.6.0", - "jest-resolve": "26.6.0", - "jest-watch-typeahead": "0.6.4", - "mini-css-extract-plugin": "0.11.3", + "jest-resolve": "^27.4.2", + "jest-watch-typeahead": "^1.0.0", "npm-run-all": "^4.1.5", "optimize-css-assets-webpack-plugin": "5.0.4", "pnp-webpack-plugin": "1.7.0", - "postcss": "^8.4.5", - "postcss-flexbugs-fixes": "4.2.1", - "postcss-loader": "3.0.0", - "postcss-normalize": "8.0.1", - "postcss-preset-env": "6.7.0", + "postcss": "^8.4.4", + "postcss-flexbugs-fixes": "^5.0.2", + "postcss-loader": "^6.2.1", + "postcss-normalize": "^10.0.1", + "postcss-preset-env": "^7.0.1", "postcss-safe-parser": "5.0.2", "prettier": "^2.7.1", + "process": "^0.11.10", + "react-dev-utils": "^12.0.1", "react-dnd-cjs": "^9.5.1", "react-dnd-html5-backend-cjs": "^9.5.1", - "react-is": "^17.0.2", - "react-scripts": "^4.0.3", + "react-is": "^18.2.0", + "react-scripts": "^5.0.1", "sass-loader": "8.0.2", + "stream-browserify": "^3.0.0", "style-loader": "1.3.0", "tailwindcss": "^3.1.6", - "terser-webpack-plugin": "4.2.3", + "terser-webpack-plugin": "^5.2.5", "ts-loader": "^8.0.14", "ts-node": "^9.1.1", "ts-pnp": "1.2.0", "typescript": "^4.7.4", "url-loader": "4.1.1", "web-vitals": "^0.2.4", - "webpack": "4.44.2", - "webpack-cli": "^4.3.1", - "webpack-dev-server": "^4.7.4", - "webpack-manifest-plugin": "2.2.0", + "webpack": "^5.64.4", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^4.6.0", + "webpack-manifest-plugin": "^4.0.2", "webpack-merge": "^5.7.3", - "workbox-webpack-plugin": "5.1.4" + "workbox-webpack-plugin": "^6.4.1" }, "scripts": { "build:tailwind": "NODE_ENV=production tailwindcss build -i src/styles/tailwind.css -o src/styles/tailwind.generated.css --minify", @@ -141,23 +153,7 @@ "test:watch": "jest --watch", "lint": "prettier --check src/ '!**/*.{js,json,ts}'", "lint:fix": "prettier --write src/ '!**/*.{js,json,ts}'", - "eslint-report": "eslint src --ext .tsx,.ts -o eslint-report.html -f html" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ], - "overrides": [ - { - "files": [ - "**/*.stories.*" - ], - "rules": { - "import/no-anonymous-default-export": "off" - } - } - ] + "eslint": "eslint src --ext .tsx,.ts" }, "browserslist": { "production": [ diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx index 733c3efec..e0f279ee9 100644 --- a/packages/ui/src/App.tsx +++ b/packages/ui/src/App.tsx @@ -10,7 +10,7 @@ import { WindowIdProvider } from "./context/hooks/useWindowId" const AppLoading = () => { return ( -
+
) diff --git a/packages/ui/src/assets/fonts/Inter-Medium.ttf b/packages/ui/src/assets/fonts/Inter-Medium.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b53fb1c4acbe100c7a91f07564b7f1fa2d5bab12 GIT binary patch literal 314712 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 literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/fonts/Inter-SemiBold.ttf b/packages/ui/src/assets/fonts/Inter-SemiBold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c6aeeb16a6d7f754258bc7f3f2101d287976e6e6 GIT binary patch literal 315756 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
)a`>0Aw?n3u3y&rr!#XRzZ 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 - ) -} - -export default ActivityAssetsView diff --git a/packages/ui/src/components/ActivityList.tsx b/packages/ui/src/components/ActivityList.tsx index 13299b7cd..c1fdff092 100644 --- a/packages/ui/src/components/ActivityList.tsx +++ b/packages/ui/src/components/ActivityList.tsx @@ -7,8 +7,10 @@ import { useSelectedAccount } from "../context/hooks/useSelectedAccount" // Utils import useTransactions from "../util/hooks/useTransactions" +import { useBlankState } from "../context/background/backgroundHooks" const ActivityList = () => { + const { isNetworkChanging } = useBlankState()! const { chainId } = useSelectedNetwork() const { address } = useSelectedAccount() @@ -16,11 +18,12 @@ const ActivityList = () => { return (
diff --git a/packages/ui/src/components/AnimatedIcon.tsx b/packages/ui/src/components/AnimatedIcon.tsx index f899e5154..1e5b79398 100644 --- a/packages/ui/src/components/AnimatedIcon.tsx +++ b/packages/ui/src/components/AnimatedIcon.tsx @@ -5,6 +5,7 @@ import blueLineLoadingSkeleton from "../assets/images/icons/blueline_skeleton.js import greyLineLoadingSkeleton from "../assets/images/icons/greyline_skeleton.json" import greyCircleLoadingSkeleton from "../assets/images/icons/greycircle_skeleton.json" import blueCircleLoadingSkeleton from "../assets/images/icons/bluecircle_skeleton.json" +import wallet from "../assets/images/icons/wallet.json" import lottie, { AnimationItem } from "lottie-web" import successAnim from "../assets/images/icons/checkmark_notes.json" import { useEffect, useRef, FunctionComponent } from "react" @@ -19,6 +20,7 @@ export enum AnimatedIconName { GreyLineLoadingSkeleton, GreyCircleLoadingSkeleton, BlueCircleLoadingSkeleton, + Wallet, } type AnimationType = { @@ -60,6 +62,11 @@ const Animations: { [anim: number]: AnimationType } = { data: blueCircleLoadingSkeleton, loop: true, }, + [AnimatedIconName.Wallet]: { + autoplay: false, + data: wallet, + hover: true, + }, } interface AnimationProps { @@ -101,11 +108,11 @@ const AnimatedIcon: FunctionComponent = ({ } return () => lottieInstance.current && lottieInstance.current.destroy() - }, [animationData]) + }, [animationData, svgClassName]) return (
{ if (animationData.hover) { lottieInstance.current?.play() diff --git a/packages/ui/src/components/AppVersion.tsx b/packages/ui/src/components/AppVersion.tsx index 163acd602..2f28f218d 100644 --- a/packages/ui/src/components/AppVersion.tsx +++ b/packages/ui/src/components/AppVersion.tsx @@ -1,12 +1,10 @@ -const AppVersion = () => { - const { VERSION, VERSION_NAME } = process.env - - return VERSION ? ( - - Version: v{[VERSION, VERSION_NAME].filter(Boolean).join(" - ")} +const AppVersion = () => + process.env.VERSION ? ( + + Version: v{process.env.VERSION} ) : ( - DEVELOPMENT + DEVELOPMENT ) -} + export default AppVersion diff --git a/packages/ui/src/components/AssetsList.tsx b/packages/ui/src/components/AssetsList.tsx deleted file mode 100644 index c011910cd..000000000 --- a/packages/ui/src/components/AssetsList.tsx +++ /dev/null @@ -1,187 +0,0 @@ -import { BigNumber } from "@ethersproject/bignumber" -import { Fragment, FunctionComponent, useState } from "react" -import { useOnMountHistory } from "../context/hooks/useOnMount" -import { Token } from "@block-wallet/background/controllers/erc-20/Token" -import { TokenList, useTokensList } from "../context/hooks/useTokensList" -import { formatUnits } from "@ethersproject/units" - -import { Classes } from "../styles/classes" -import plus from "../assets/images/icons/plus.svg" -import unknownTokenIcon from "../assets/images/unknown_token.svg" -import ChevronRightIcon from "./icons/ChevronRightIcon" -import { formatRounded } from "../util/formatRounded" -import { ActionButton } from "./button/ActionButton" -import AssetsLoadingSkeleton from "./skeleton/AssetsLoadingSkeleton" -import useCurrencyFromatter from "../util/hooks/useCurrencyFormatter" -import { isNativeTokenAddress } from "../util/tokenUtils" -import { useBlankState } from "../context/background/backgroundHooks" -export type AssetItem = { - token: Token - balance: BigNumber -} - -export const AssetIcon: FunctionComponent<{ - asset: Partial - filled?: boolean -}> = ({ asset, filled }) => ( -
- { - { - ;(e.target as any).onerror = null - ;(e.target as any).src = unknownTokenIcon - }} - alt={asset.symbol || ""} - className="rounded-full" - /> - } -
-) - -const Asset: FunctionComponent<{ - asset: AssetItem - pushDeleteTokens: Function -}> = ({ asset }) => { - const history: any = useOnMountHistory() - const formatter = useCurrencyFromatter() - return ( -
- history.push({ - pathname: `/asset/details`, - state: { - address: asset.token.address, - transitionDirection: "left", - }, - }) - } - className="flex flex-row items-center justify-between px-6 py-5 -ml-6 transition duration-300 hover:bg-primary-100 hover:bg-opacity-50 active:bg-primary-200 active:bg-opacity-50 cursor-pointer" - style={{ width: "calc(100% + 2 * 1.5rem)" }} - role="listitem" - aria-label={asset.token.symbol} - > -
- -
- - {` - ${formatRounded( - formatUnits( - asset.balance || "0", - asset.token.decimals - ), - 4 - )} - ${asset.token.symbol} - `} - - - {formatter.format( - asset.balance || BigNumber.from(0), - asset.token.symbol, - asset.token.decimals, - isNativeTokenAddress(asset.token.address) - )} - -
-
-
- -
-
- ) -} - -const SubAssetList: FunctionComponent<{ assets: TokenList }> = ({ assets }) => { - const state = useBlankState()! - - const isLoading = - state.isNetworkChanging || state.isRatesChangingAfterNetworkChange - - const [deletedTokens, setDeletedTokens] = useState([] as string[]) - const pushDeleteTokens = (deleteToken: string) => { - setDeletedTokens([...deletedTokens, deleteToken]) - } - - // the action of delete a token is not sync, we include this blick of code for not showing deleted tokens while they are being deleted. - const newDeleted: string[] = [] - deletedTokens.forEach((t) => { - if (assets.map((a) => a.token.address).includes(t)) { - newDeleted.push(t) - } - }) - if (deletedTokens.length !== newDeleted.length) { - setDeletedTokens(newDeleted) - } - - return ( -
- {isLoading ? ( - - ) : ( - assets - .filter((t) => !deletedTokens.includes(t.token.address)) - .map((a, i) => ( - - {i > 0 ?
: null} - -
- )) - )} -
- ) -} - -const AssetsList = () => { - const { currentNetworkTokens, nativeToken } = useTokensList() - - const tokens = [nativeToken].concat(currentNetworkTokens) - - // Top spacing for network labels: "pt-6" - return ( -
- {tokens.length > 9 && ( -
- -
- )} -
- {/* Network label */} - {/* ETHEREUM */} - -
-
- -
-
- ) -} - -export default AssetsList diff --git a/packages/ui/src/components/CancelSpeedUpCommon.tsx b/packages/ui/src/components/CancelSpeedUpCommon.tsx index a50d167c3..232ddd07d 100644 --- a/packages/ui/src/components/CancelSpeedUpCommon.tsx +++ b/packages/ui/src/components/CancelSpeedUpCommon.tsx @@ -55,6 +55,7 @@ import { useSelectedAccount } from "../context/hooks/useSelectedAccount" import useCheckAccountDeviceLinked from "../util/hooks/useCheckAccountDeviceLinked" import HardwareDeviceNotLinkedDialog from "./dialog/HardwareDeviceNotLinkedDialog" import { getDeviceFromAccountType } from "../util/hardwareDevice" +import log from "loglevel" // Schema export const formSchema = yup.object({ @@ -241,6 +242,7 @@ const CancelAndSpeedUpComponent = ({ status: replacementTx.status, error: replacementTx.error as Error, epochTime: replacementTx?.approveTime, + qrParams: transaction?.qrParams, } : undefined, type === "cancel" @@ -316,7 +318,7 @@ const CancelAndSpeedUpComponent = ({ setIsLoading(false) }) .catch((e) => { - console.log(e) + log.error(e) history.push({ pathname: "/home", @@ -477,27 +479,22 @@ const CancelAndSpeedUpComponent = ({ } } - const notEnoughFunds = - type === "speed up" && - currentBalance - .sub(BigNumber.from(transaction.transactionParams.value ?? "0")) - .sub( - calcGasPrice(transactionType, { - gasLimit: oldGasLimit, - gasPrice: oldGasPrice, - maxFeePerGas: oldMaxFeePerGas, - }) - ) - .lt( - calcGasPrice(transactionType, { - gasLimit: parseUnits(newFees.gasLimit || "0", "wei"), - gasPrice: parseUnits(newFees.gasPrice || "0", "gwei"), - maxFeePerGas: parseUnits( - newFees.maxFeePerGas || "0", - "gwei" - ), - }) - ) + const notEnoughFunds = currentBalance + .sub(BigNumber.from(transaction.transactionParams.value ?? "0")) + .sub( + calcGasPrice(transactionType, { + gasLimit: oldGasLimit, + gasPrice: oldGasPrice, + maxFeePerGas: oldMaxFeePerGas, + }) + ) + .lt( + calcGasPrice(transactionType, { + gasLimit: parseUnits(newFees.gasLimit || "0", "wei"), + gasPrice: parseUnits(newFees.gasPrice || "0", "gwei"), + maxFeePerGas: parseUnits(newFees.maxFeePerGas || "0", "gwei"), + }) + ) useEffect(() => { if (!notEnoughFunds && error?.type !== "notEnoughFunds") return @@ -605,7 +602,7 @@ const CancelAndSpeedUpComponent = ({ error: texts?.error ?? "", }} gifs={gifs} - timeout={2900} + timeout={1500} clickOutsideToClose={false} txHash={replacementTx?.transactionParams.hash} onDone={() => { @@ -624,6 +621,7 @@ const CancelAndSpeedUpComponent = ({ }) } }} + showCloseButton />

{baseFeePerGas && ( - + Last base fee:{" "} {formatUnits(baseFeePerGas, "gwei")} GWEI diff --git a/packages/ui/src/components/CollapsableMessage.tsx b/packages/ui/src/components/CollapsableMessage.tsx new file mode 100644 index 000000000..aea4442e9 --- /dev/null +++ b/packages/ui/src/components/CollapsableMessage.tsx @@ -0,0 +1,86 @@ +import { useState } from "react" +import ErrorDialog from "./dialog/ErrorDialog" +import HotkeysDialog from "./dialog/HotkeysDialog" +import MessageDialog from "./dialog/MessageDialog" +import WarningDialog from "./dialog/WarningDialog" + +interface CollapsableMessageProps { + isCollapsedByDefault: boolean + type?: "warn" | "error" | "info" | "hotkeys" + onDismiss?: () => void + dialog: { + title: string + message: JSX.Element | string + } + collapsedMessage: JSX.Element | string + showCollapsedMessage?: boolean + onConfirm?: () => void + showSubHeader?: boolean +} +const CollapsableMessage = ({ + isCollapsedByDefault, + onDismiss, + dialog, + type = "warn", + collapsedMessage, + showCollapsedMessage = false, + onConfirm, + showSubHeader, +}: CollapsableMessageProps) => { + const [isCollapsed, setIsCollapsed] = useState(isCollapsedByDefault) + + const onClose = () => { + setIsCollapsed(true) + if (onDismiss) { + onDismiss() + } + } + + const hotkeysOnDone = () => { + setIsCollapsed(true) + if (onConfirm) { + onConfirm() + } + } + + let Dialog + switch (type) { + case "warn": + Dialog = WarningDialog + break + case "error": + Dialog = ErrorDialog + break + case "info": + Dialog = MessageDialog + break + case "hotkeys": + Dialog = HotkeysDialog + break + } + + return ( + <> +
setIsCollapsed(false)} + className="cursor-pointer" + > + {collapsedMessage} +
+ {(!isCollapsed || showCollapsedMessage) && ( + + )} + + ) +} + +export default CollapsableMessage diff --git a/packages/ui/src/components/CollapsableWarning.tsx b/packages/ui/src/components/CollapsableWarning.tsx deleted file mode 100644 index 9d9591235..000000000 --- a/packages/ui/src/components/CollapsableWarning.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { useState } from "react" -import ErrorDialog from "./dialog/ErrorDialog" -import WarningDialog from "./dialog/WarningDialog" - -interface CollapsableWarningProps { - isCollapsedByDefault: boolean - type?: "warn" | "error" - onDismiss?: () => void - dialog: { - title: string - message: JSX.Element | string - } - collapsedMessage: JSX.Element | string -} -const CollapsableWarning = ({ - isCollapsedByDefault, - onDismiss, - dialog, - type = "warn", - collapsedMessage, -}: CollapsableWarningProps) => { - const [isCollapsed, setIsCollapsed] = useState(isCollapsedByDefault) - - const onClose = () => { - setIsCollapsed(true) - if (onDismiss) { - onDismiss() - } - } - - const Dialog = type === "warn" ? WarningDialog : ErrorDialog - - return ( - <> -
setIsCollapsed(false)} - className="cursor-pointer" - > - {collapsedMessage} -
- {!isCollapsed && ( - - )} - - ) -} - -export default CollapsableWarning diff --git a/packages/ui/src/components/FeeDetails.tsx b/packages/ui/src/components/FeeDetails.tsx index d0b02944e..88ba174ff 100644 --- a/packages/ui/src/components/FeeDetails.tsx +++ b/packages/ui/src/components/FeeDetails.tsx @@ -10,7 +10,9 @@ interface FeeDetailsProps { const FeeDetails: FC = ({ summary, details }) => { return (
-

{summary}

+

+ {summary} +

{details && ( = ({ summary, details }) => { > )} diff --git a/packages/ui/src/components/FullCenterContainer.tsx b/packages/ui/src/components/FullCenterContainer.tsx index 3ca4e4ecf..265096e44 100644 --- a/packages/ui/src/components/FullCenterContainer.tsx +++ b/packages/ui/src/components/FullCenterContainer.tsx @@ -1,5 +1,4 @@ import { FunctionComponent } from "react" - import classnames from "classnames" const FullCenterContainer: FunctionComponent<{ @@ -8,12 +7,12 @@ const FullCenterContainer: FunctionComponent<{ children: React.ReactNode }> = ({ children, centered = false, screen = false }) => (
= ({ children, className }) => (
diff --git a/packages/ui/src/components/LogoHeader.tsx b/packages/ui/src/components/LogoHeader.tsx index 7f4b6a512..58ac77a9c 100644 --- a/packages/ui/src/components/LogoHeader.tsx +++ b/packages/ui/src/components/LogoHeader.tsx @@ -1,9 +1,18 @@ +import { FunctionComponent } from "react" import logo from "../assets/images/logo.svg" +import { classnames } from "../styles" -const LogoHeader = () => ( -
+const LogoHeader: FunctionComponent<{ + className?: string +}> = ({ className }) => ( +
logo - BlockWallet + BlockWallet
) diff --git a/packages/ui/src/components/PageLayout.tsx b/packages/ui/src/components/PageLayout.tsx index 9643de474..9ef0d1b26 100644 --- a/packages/ui/src/components/PageLayout.tsx +++ b/packages/ui/src/components/PageLayout.tsx @@ -31,8 +31,7 @@ const PageLayout: FunctionComponent<{ ) : null}
void - title?: string - message?: string -} +import useNetWorthBalance from "../../context/hooks/useNetWorthBalance" interface AccountDisplayProps { account: AccountInfo selected?: boolean showSelectedCheckmark?: boolean showAddress?: boolean + truncateName?: boolean showConnected?: boolean copyAddressToClipboard?: boolean menu?: AccountDisplayMenuOption[] actionButtons?: JSX.Element[] onClickAccount?: (account: AccountInfo) => void + className?: string } const AccountDisplay: FunctionComponent = ({ @@ -54,17 +46,24 @@ const AccountDisplay: FunctionComponent = ({ selected, showSelectedCheckmark = true, showAddress = false, + truncateName = true, showConnected = false, copyAddressToClipboard = false, actionButtons, menu, onClickAccount, + className, }) => { const [confirmationDialog, setConfirmationDialog] = - useState({ isOpen: false }) + useState({ open: false }) const { isHovering: isHoveringMenu, getIsHoveringProps } = useIsHovering() - const { chainId, nativeCurrency } = useSelectedNetwork() const checksumAddress = useAddressWithChainIdChecksum(account?.address) + const { + displayNetWorth, + netWorth, + nativeTokenBalance, + nativeTokenBalanceRounded, + } = useNetWorthBalance(!showAddress ? account : undefined) const { copied, onCopy } = useCopyToClipboard(checksumAddress) @@ -74,28 +73,26 @@ const AccountDisplay: FunctionComponent = ({ } setConfirmationDialog({ onConfirm: () => optionMetadata.handler!(checksumAddress), - isOpen: true, + open: true, title: optionMetadata.confirmationTitle, message: optionMetadata.confirmationMessage, }) } - const nativeTokenBalance = - (account.balances && account.balances[chainId]?.nativeTokenBalance) ?? - "0" - const hoverStyle = onClickAccount && !selected && !actionButtons && !isHoveringMenu - const accountName = formatName(account.name, showAddress ? 25 : 18) + const accountName = formatName(account.name, showAddress ? 25 : 25) return ( <>
onClickAccount && onClickAccount(account)} role="button" @@ -121,7 +118,9 @@ const AccountDisplay: FunctionComponent = ({
{!showAddress && ( {formatHashLastChars(checksumAddress)} @@ -140,19 +139,19 @@ const AccountDisplay: FunctionComponent = ({
{!showAddress ? ( - {formatNumberLength( - formatUnits(nativeTokenBalance), - 10 - )}{" "} - {nativeCurrency.symbol} + {displayNetWorth + ? netWorth + : nativeTokenBalanceRounded} ) : ( - + {formatHash(checksumAddress)} )} @@ -165,7 +164,7 @@ const AccountDisplay: FunctionComponent = ({
{account.accountType && ( - + {account.accountType.toString()} @@ -178,7 +177,7 @@ const AccountDisplay: FunctionComponent = ({ )}
-
+
{selected && showSelectedCheckmark ? ( = ({ ) : null} {actionButtons} - {menu && (
@@ -225,8 +223,8 @@ const AccountDisplay: FunctionComponent = ({ setConfirmationDialog({ isOpen: false })} + open={confirmationDialog.open} + onClose={() => setConfirmationDialog({ open: false })} onConfirm={confirmationDialog.onConfirm!} /> diff --git a/packages/ui/src/components/account/AccountDisplayMenu.tsx b/packages/ui/src/components/account/AccountDisplayMenu.tsx index 7083efc75..fb5d50d20 100644 --- a/packages/ui/src/components/account/AccountDisplayMenu.tsx +++ b/packages/ui/src/components/account/AccountDisplayMenu.tsx @@ -2,8 +2,10 @@ import classnames from "classnames" import { AccountInfo } from "@block-wallet/background/controllers/AccountTrackerController" import { isHardwareWallet } from "../../util/account" import Icon, { IconName } from "../ui/Icon" +import GearIcon from "../icons/GearIcon" export enum AccountMenuOptionType { + SETTINGS = "SETTINGS", REMOVE_ACCOUNT = "REMOVE_ACCOUNT", REMOVE_CONTACT = "REMOVE_CONTACT", EDIT = "EDIT", @@ -19,6 +21,20 @@ export interface AccountDisplayMenuOption { disabled?: boolean } +const SettingsOption: React.FC = ({ onClick }) => { + return ( +
+ + Settings +
+ ) +} + const RemoveOption: React.FC = ({ onClick }) => { return (
= ({ onClick }) => { const EditOption: React.FC = ({ onClick }) => { return (
@@ -49,7 +65,7 @@ const HideAccountOption: React.FC = ({ onClick, disabled }) => { return (
= ({ onClick, disabled }) => { return (
= ({ onClick, disabled }) => { const MENU_ITEM_BY_OPTION = { [AccountMenuOptionType.CUSTOM]: null, + [AccountMenuOptionType.SETTINGS]: SettingsOption, [AccountMenuOptionType.REMOVE_CONTACT]: RemoveOption, [AccountMenuOptionType.REMOVE_ACCOUNT]: RemoveOption, [AccountMenuOptionType.EDIT]: EditOption, diff --git a/packages/ui/src/components/account/AccountFilters.tsx b/packages/ui/src/components/account/AccountFilters.tsx index f4b8e7d0e..9cb5346e4 100644 --- a/packages/ui/src/components/account/AccountFilters.tsx +++ b/packages/ui/src/components/account/AccountFilters.tsx @@ -27,6 +27,7 @@ interface AccountFilterProps { filters: string[] customFilters?: AccountFilter[] onChangeFilters: (newFilters: string[]) => void + searchButtonClassName?: string } const AccountFilters: React.FC = ({ @@ -42,7 +43,7 @@ const AccountFilters: React.FC = ({ ) ) return ( -
+
{ return onChangeFilters( @@ -51,7 +52,10 @@ const AccountFilters: React.FC = ({ }} > - + {getFilterOptions(customFilters).map( @@ -64,7 +68,7 @@ const AccountFilters: React.FC = ({ selected={selectedFilters.includes( value )} - className="p-2 font-normal text-black" + className="p-2 font-normal text-primary-black-default" > {label} diff --git a/packages/ui/src/components/account/AccountMenu.tsx b/packages/ui/src/components/account/AccountMenu.tsx index be67e95d1..21f3aa9e1 100644 --- a/packages/ui/src/components/account/AccountMenu.tsx +++ b/packages/ui/src/components/account/AccountMenu.tsx @@ -13,16 +13,22 @@ import resetIcon from "../../assets/images/icons/reset.svg" import allowancesIcon from "../../assets/images/icons/allowances.svg" import qrIcon from "../../assets/images/icons/qr_icon.svg" import sites from "../../assets/images/icons/connected_sites.svg" -import editIcon from "../../assets/images/icons/pencil.svg" +import EditIcon from "../icons/EditIcon" import { generateExplorerLink, getExplorerTitle } from "../../util/getExplorer" import { useBlankState } from "../../context/background/backgroundHooks" import classnames from "classnames" import { useOnMountHistory } from "../../context/hooks/useOnMount" import { HARDWARE_TYPES } from "../../util/account" import { openHardwareRemove } from "../../context/commActions" +import browser from "webextension-polyfill" +import { useHotkeys } from "react-hotkeys-hook" +import { componentsHotkeys } from "../../util/hotkeys" +import accounts_order from "../../assets/images/icons/accounts_order.svg" +import assets_order from "../../assets/images/icons/assets_order.svg" const AccountMenu = () => { - const { availableNetworks, selectedNetwork } = useBlankState()! + const { availableNetworks, selectedNetwork, hotkeysEnabled } = + useBlankState()! const account = useSelectedAccount() const checksumAddress = useSelectedAddressWithChainIdChecksum() const history = useOnMountHistory() @@ -63,6 +69,19 @@ const AccountMenu = () => { }, ] + const accountMenuHotkeys = componentsHotkeys.AccountMenu + useHotkeys(accountMenuHotkeys, () => { + if (!hotkeysEnabled) return + browser.tabs.create({ + url: generateExplorerLink( + availableNetworks, + selectedNetwork, + account.address, + "address" + ), + }) + }) + const disabledOptions: { [k: string]: boolean } = {} const tooltipOptions: { [k: string]: string } = {} @@ -92,6 +111,18 @@ const AccountMenu = () => { to: "/accounts", }) + options.push({ + icon: assets_order, + label: "Assets order", + to: "/accounts/menu/tokensOrder", + }) + + options.push({ + icon: accounts_order, + label: "Acounts Order", + to: "/accounts/menu/order", + }) + options.push({ icon: resetIcon, label: "Reset Account", @@ -110,10 +141,11 @@ const AccountMenu = () => { : "/settings", }) }} + networkIndicator /> } > -
+
{ }, }) }} - className="cursor-pointer p-2 transition duration-300 rounded-full hover:bg-primary-100 hover:text-primary-300" + className="cursor-pointer p-2 transition duration-300 rounded-full text-primary-black-default hover:bg-primary-grey-default hover:text-primary-blue-default" > - Edit +
, ]} /> @@ -148,7 +180,9 @@ const AccountMenu = () => { onChange={(option) => option.to ? option.to.includes("https://") - ? chrome.tabs.create({ url: option.to }) + ? browser.tabs.create({ + url: option.to, + }) : history.push({ pathname: option.to, state: { @@ -177,7 +211,7 @@ const AccountMenu = () => { } />
- + {option.label} diff --git a/packages/ui/src/components/account/AccountMultipleSelect.tsx b/packages/ui/src/components/account/AccountMultipleSelect.tsx index e961045a0..b9ec5c5be 100644 --- a/packages/ui/src/components/account/AccountMultipleSelect.tsx +++ b/packages/ui/src/components/account/AccountMultipleSelect.tsx @@ -17,10 +17,10 @@ const AccountMultipleSelect: FunctionComponent<{ onChange(newValue) } return ( -
+
{accounts.map((account, i) => (
toggleAccount(account)} > diff --git a/packages/ui/src/components/account/AccountSearchBar.tsx b/packages/ui/src/components/account/AccountSearchBar.tsx index d7a1a12ba..6c2a4f7eb 100644 --- a/packages/ui/src/components/account/AccountSearchBar.tsx +++ b/packages/ui/src/components/account/AccountSearchBar.tsx @@ -47,9 +47,10 @@ const AccountSearchBar: FunctionComponent<{ {!searchBarVisible && ( )}
@@ -57,8 +58,8 @@ const AccountSearchBar: FunctionComponent<{ className={classnames( "transition-width", searchBarVisible - ? "w-full delay-150 duration-500" - : "w-auto duration-100 cursor-pointer" + ? "!w-full delay-150 duration-500" + : "duration-100 cursor-pointer" )} onClick={() => { if (!searchBarVisible) { @@ -66,12 +67,13 @@ const AccountSearchBar: FunctionComponent<{ setIsSearching(true) } }} + title="Search account" > - +
void + showSearchSkeleton: boolean + setShowSearchSkeleton: React.Dispatch> } export type AccountResult = { @@ -38,6 +43,8 @@ const AccountSearchResults = ({ filter, onSelect, resultsToDisplay = { wallet: true, addressBook: true, ens: true, ud: true }, + showSearchSkeleton, + setShowSearchSkeleton, }: AccountSearchResultsProps) => { // Hooks const { ens } = useSelectedNetwork() @@ -78,6 +85,17 @@ const AccountSearchResults = ({ ) } + const displaySearchMessage = (): boolean => { + return ( + noWalletResults && + noAddressBookResults && + noEnsResults && + noUDResults && + !isValidAddress(filter) && + filter === "" + ) + } + useEffect(() => { const search = async () => { // Filter Wallet Accounts @@ -99,23 +117,28 @@ const AccountSearchResults = ({ } // If Ens enabled, search for it - if (ensEnabled && ensEnabled.current) { + if (ensEnabled && ensEnabled.current) newResults.ens = filter ? await searchEns(filter) : undefined - } // Unstoppable Domains newResults.ud = filter ? await searchUD(filter) : undefined setResults(newResults) + disableSkeleton() } search() // eslint-disable-next-line react-hooks/exhaustive-deps }, [filter]) + const disableSkeleton = async () => { + // await new Promise((resolve) => setTimeout(resolve, 1000)) + setShowSearchSkeleton(false) + } + return ( <> - {!noWalletResults && ( + {!noWalletResults && !showSearchSkeleton && (
{results.wallet.map((account) => ( @@ -131,7 +154,7 @@ const AccountSearchResults = ({
)} - {!noAddressBookResults && ( + {!noAddressBookResults && !showSearchSkeleton && (
{results.addressBook.map((account) => ( @@ -151,7 +174,7 @@ const AccountSearchResults = ({
)} - {!noEnsResults && results.ens && ( + {!noEnsResults && results.ens && !showSearchSkeleton && (
)} - {!noUDResults && results.ud && ( + {!noUDResults && results.ud && !showSearchSkeleton && (
)} - {displayEmptyResultsMessage() && ( -
- No results found. + {displayEmptyResultsMessage() && !showSearchSkeleton && ( +
+
+ search +
+
+ + No results found. + +
+ + We cannot find anything you are searching for. Try + to adjust your search. + +
)} + + {displaySearchMessage() && !showSearchSkeleton && ( +
+
+ search +
+
+
+ + Add recipient by searching public address, name, or + select contact + +
+
+ )} + + {showSearchSkeleton && } ) } diff --git a/packages/ui/src/components/account/AccountSelect.tsx b/packages/ui/src/components/account/AccountSelect.tsx index 756436dae..539604225 100644 --- a/packages/ui/src/components/account/AccountSelect.tsx +++ b/packages/ui/src/components/account/AccountSelect.tsx @@ -11,15 +11,10 @@ import { } from "../../context/commActions" import { AccountType } from "../../context/commTypes" import { AccountMenuOptionType } from "./AccountDisplayMenu" -import GearIcon from "../icons/GearIcon" import { useHistory } from "react-router-dom" import AccountsList from "./AccountsList" import useAccountSearch from "../../util/hooks/account/useAccountSearch" -import { - isActiveAccount, - isHiddenAccount, - isInternalAccount, -} from "../../util/account" +import { isActiveAccount, isHiddenAccount } from "../../util/account" import type { LocationDescriptor } from "history" import useConnectedAccounts from "../../util/hooks/account/useConnectedAccounts" import { useBlankState } from "../../context/background/backgroundHooks" @@ -27,6 +22,7 @@ import useAccountsFilter from "../../util/hooks/account/useAccountsFilter" import AccountFilters from "./AccountFilters" import { AccountFilter } from "../../util/filterAccounts" import EmptyState from "../ui/EmptyState" +import OrderButton from "../button/OrderButton" interface AccountSelectProps { accounts: AccountInfo[] @@ -96,36 +92,51 @@ const AccountSelect: FunctionComponent = ({ const history = useHistory() - const internalAccountsNumber = accounts.filter( - (account) => - isInternalAccount(account.accountType) && isActiveAccount(account) + const accountsNumber = accounts.filter((account) => + isActiveAccount(account) ).length const getAccountOptions = (account: AccountInfo) => { if (!showMenu) return undefined + + const options = [] + + if (currentAccount?.address === account.address) { + options.push({ + optionType: AccountMenuOptionType.SETTINGS, + handler: () => { + history.push({ + pathname: "/accounts/menu", + state: { + fromAccountList: true, + }, + }) + }, + }) + } + if (account.accountType === AccountType.HD_ACCOUNT) { if (isHiddenAccount(account)) { - return [ - { - optionType: AccountMenuOptionType.UNHIDE_ACCOUNT, - handler: unhideAccount, - }, - ] - } - return [ - { + options.push({ + optionType: AccountMenuOptionType.UNHIDE_ACCOUNT, + handler: unhideAccount, + }) + } else { + options.push({ optionType: AccountMenuOptionType.HIDE_ACCOUNT, handler: hideAccount, - disabled: internalAccountsNumber === 1, - }, - ] - } - return [ - { + disabled: accountsNumber === 1, + }) + } + } else { + options.push({ optionType: AccountMenuOptionType.REMOVE_ACCOUNT, handler: removeAccount, - }, - ] + disabled: accountsNumber === 1, + }) + } + + return options } let searchedActiveAccounts = otherAccounts @@ -145,7 +156,7 @@ const AccountSelect: FunctionComponent = ({ } return ( -
+
= ({ setFilterValue(prevValue) } }} + searchButtonClassName="!h-10 !w-3" + /> + { + history.push({ + pathname: "/accounts/menu/order", + }) + }} + title="Edit accounts order" />
{showEmptyState && ( @@ -205,28 +225,6 @@ const AccountSelect: FunctionComponent = ({ selectedAccount.address === currentAccount!.address } - actionButtons={ - showActionButtons - ? [ -
{ - history.push({ - pathname: - "/accounts/menu", - state: { - fromAccountList: - true, - }, - }) - }} - className="cursor-pointer p-2 transition duration-300 rounded-full hover:bg-primary-100 hover:text-primary-300" - > - -
, - ] - : undefined - } /> )} diff --git a/packages/ui/src/components/account/AccountsDisplayDragDrop.tsx b/packages/ui/src/components/account/AccountsDisplayDragDrop.tsx new file mode 100644 index 000000000..343086504 --- /dev/null +++ b/packages/ui/src/components/account/AccountsDisplayDragDrop.tsx @@ -0,0 +1,211 @@ +import classnames from "classnames" +import { useState, FunctionComponent, useRef, useEffect } from "react" +import { formatHashLastChars } from "../../util/formatAccount" +import { DragSourceMonitor, useDrag, useDrop } from "react-dnd" +import useIsHovering from "../../util/hooks/useIsHovering" +import { HiDotsVertical } from "react-icons/hi" +import { AccountInfo } from "@block-wallet/background/controllers/AccountTrackerController" +import AccountIcon from "../icons/AccountIcon" +import { getAccountColor } from "../../util/getAccountColor" +import { useAddressWithChainIdChecksum } from "../../util/hooks/useSelectedAddressWithChainIdChecksum" +import useNetWorthBalance from "../../context/hooks/useNetWorthBalance" +import { AccountStatus } from "../../context/commTypes" + +type AccountCardProps = { + accountInfo: AccountInfo + originalIndex: number +} + +type AccountDisplayType = { + account: AccountInfo + hoverable?: boolean | false + moveAccountCard: (draggedIndex: string, hoveredOnIndex: number) => void + findAccountCard: (address: string) => { + account: AccountInfo + index: number + } + onSuccessfulDrop: () => void + hiddenAccount: boolean +} + +/** + * AccountDisplayDragDrop: + * Creates a display element to show account information. + * It allows drag & drop. + * + * @param account - Object containing account to display's informations. + * @param hoverable - Determines if the element shows a hover style. + * @param moveTokenCard - Changes the account order to new position + * @param findTokenCard - Finds a account by address + * @param onSuccessfulDrop - If drop was successful we saved the new order in the state + * @param hiddenAccount - If true, will indicate when it is a hidden account + */ +const AccountDisplayDragDrop: FunctionComponent = ({ + account, + hoverable, + hiddenAccount, + moveAccountCard, + findAccountCard, + onSuccessfulDrop, +}) => { + const checksumAddress = useAddressWithChainIdChecksum(account?.address) + const { isHovering: isHoveringIcons } = useIsHovering() + const [dropAnimation, setDropAnimation] = useState(false) + const dropRef = useRef(null) + const dragRef = useRef(null) + const originalIndex = findAccountCard(account.address).index + const { + displayNetWorth, + netWorth, + nativeTokenBalance, + nativeTokenBalanceRounded, + } = useNetWorthBalance(account) + + const [{ isDragging }, drag, preview] = useDrag( + () => ({ + type: "account", + item: { accountInfo: account, originalIndex: originalIndex }, + // This is used to inject isDragging variable into the component + collect: (monitor: DragSourceMonitor) => ({ + isDragging: monitor.isDragging(), + }), + // triggered when the dragging of this component is stopped + end: (item: AccountCardProps, monitor: DragSourceMonitor) => { + const didDrop = monitor.didDrop() + // if the drop was not successful in a dropzone, we return the card to its original position + if (!didDrop) { + moveAccountCard( + item.accountInfo.address, + item.originalIndex + ) + } + }, + }), + [account, originalIndex, moveAccountCard] + ) + + const [, drop] = useDrop( + () => ({ + accept: "account", + // Called when a dragged item is hovered over this component. + hover(item: AccountCardProps) { + if (item.accountInfo.address !== account.address) { + const { index: overIndex } = findAccountCard( + account.address + ) + // move the dragged item to the hovered item's position + moveAccountCard(item.accountInfo.address, overIndex) + } + }, + collect(monitor) { + if (monitor.didDrop()) { + const dropResult = + monitor.getDropResult() as AccountCardProps + // If the drop was successful, we trigger the animation and update background networks state + if (dropResult.accountInfo.address === account.address) { + setDropAnimation(true) + onSuccessfulDrop() + } + } + }, + drop(item: AccountCardProps) { + return item + }, + }), + [findAccountCard, moveAccountCard] + ) + + preview(drop(dropRef)) + drag(dragRef) + + useEffect(() => { + if (dropAnimation) { + setTimeout(() => { + setDropAnimation(false) + }, 800) + } + }, [dropAnimation]) + + const opacity = isDragging ? 0 : 1 + + const cardHoverStyle = !dropAnimation && !isHoveringIcons && hoverable + + // Render + return ( +
+
+
+
+ + +
+
+
+ + + {formatHashLastChars(checksumAddress)} + + {hiddenAccount && + account.status === + AccountStatus.HIDDEN && ( + + - HIDDEN + + )} +
+ + {displayNetWorth + ? netWorth + : nativeTokenBalanceRounded} + +
+
+
+
+
+
+ ) +} + +export default AccountDisplayDragDrop diff --git a/packages/ui/src/components/account/AccountsList.tsx b/packages/ui/src/components/account/AccountsList.tsx index 931b8e901..7a055e8a1 100644 --- a/packages/ui/src/components/account/AccountsList.tsx +++ b/packages/ui/src/components/account/AccountsList.tsx @@ -9,7 +9,7 @@ const AccountsList: FC> = ({ }) => { return (
- {title} + {title} {children}
) diff --git a/packages/ui/src/components/addressBook/AddressDisplay.tsx b/packages/ui/src/components/addressBook/AddressDisplay.tsx index cca591714..ff340c056 100644 --- a/packages/ui/src/components/addressBook/AddressDisplay.tsx +++ b/packages/ui/src/components/addressBook/AddressDisplay.tsx @@ -1,19 +1,59 @@ -import { FunctionComponent, useState } from "react" +import { FunctionComponent, useEffect, useState } from "react" import { formatHash, formatName } from "../../util/formatAccount" -import { isNativeTokenAddress } from "../../util/tokenUtils" -import CollapsableWarning from "../CollapsableWarning" +import CollapsableMessage from "../CollapsableMessage" import CheckmarkCircle from "../icons/CheckmarkCircle" import ExclamationCircleIconFull from "../icons/ExclamationCircleIconFull" +import { getAddressType } from "../../context/commActions" + +export enum AddressType { + NORMAL = "NORMAL", + SMART_CONTRACT = "SMART_CONTRACT", + ERC20 = "ERC20", + NULL = "NULL", +} const NATIVE_ADDRESS_MESSAGE = "This address is not owned by any user, is often associated with token burn & mint/genesis events and used as a generic null address. Please, make sure the information is correct otherwise your assets will be permanently lost." +const CONTRACT_ADDRESS_MESSAGE = + "You are about to send to a smart contract address which could result in the loss of your funds." + +const ERC20_CONTRACT_ADDRESS_MESSAGE = + "You are about to send to an ERC20 smart contract address which could result in the loss of your funds." + +const getWarningTitle = (addressType: AddressType) => { + switch (addressType) { + case AddressType.NULL: + return "Null Address" + case AddressType.SMART_CONTRACT: + return "Smart Contract" + case AddressType.ERC20: + return "ERC20 Contract Address" + default: + return "" + } +} + +const getModalMessage = (addressType: AddressType) => { + switch (addressType) { + case AddressType.NULL: + return NATIVE_ADDRESS_MESSAGE + case AddressType.SMART_CONTRACT: + return CONTRACT_ADDRESS_MESSAGE + case AddressType.ERC20: + return ERC20_CONTRACT_ADDRESS_MESSAGE + default: + return "" + } +} + export const AddressDisplay: FunctionComponent<{ receivingAddress: string selectedAccountName: string | undefined }> = ({ receivingAddress, selectedAccountName }) => { const [showingTheWholeAddress, setShowingTheWholeAddress] = useState(false) - const isNativeToken = isNativeTokenAddress(receivingAddress) + const [addressType, setAddressType] = useState() + const addressToDisplay = formatHash(receivingAddress) const fullAddressToDisplay = formatHash( receivingAddress, @@ -23,12 +63,17 @@ export const AddressDisplay: FunctionComponent<{ ? formatName(selectedAccountName, 20) : addressToDisplay - const displayAddressSpan = - accountNameToDisplay !== addressToDisplay || isNativeToken + const displayAddressSpan = accountNameToDisplay !== addressToDisplay + + useEffect(() => { + getAddressType(receivingAddress).then((type) => { + setAddressType(type) + }) + }, [receivingAddress]) return ( <> - {!isNativeToken ? ( + {!addressType || addressType === AddressType.NORMAL ? (
@@ -62,10 +107,10 @@ export const AddressDisplay: FunctionComponent<{ )}
) : ( - {NATIVE_ADDRESS_MESSAGE}, + message: {getModalMessage(addressType)}, }} isCollapsedByDefault collapsedMessage={ @@ -75,10 +120,10 @@ export const AddressDisplay: FunctionComponent<{ size="16" /> - Null Address + {getWarningTitle(addressType)} {addressToDisplay} diff --git a/packages/ui/src/components/allowances/AllowanceItem.tsx b/packages/ui/src/components/allowances/AllowanceItem.tsx index 6899416e2..20f62ba37 100644 --- a/packages/ui/src/components/allowances/AllowanceItem.tsx +++ b/packages/ui/src/components/allowances/AllowanceItem.tsx @@ -1,5 +1,5 @@ import { BigNumber } from "@ethersproject/bignumber" -import { useMemo, useState } from "react" +import { useState } from "react" import { formatUnits } from "@ethersproject/units" import { TokenAllowance } from "@block-wallet/background/controllers/AccountTrackerController" import { Classes, classnames } from "../../styles" @@ -12,7 +12,6 @@ import useIsHovering from "../../util/hooks/useIsHovering" import { generateExplorerLink } from "../../util/getExplorer" import { formatRounded } from "../../util/formatRounded" -import { AssetIcon } from "../AssetsList" import { AllowancesFilters } from "./AllowancesFilterButton" import { TabLabels } from "../assets/ActivityAllowancesView" import DetailsDialog from "../dialog/DetailsDialog" @@ -21,6 +20,7 @@ import ChevronRightIcon from "../icons/ChevronRightIcon" import revokeIcon from "../../assets/images/icons/revoke.svg" import { ButtonWithLoading } from "../button/ButtonWithLoading" import { TokenAllowanceStatus } from "../../context/commTypes" +import TokenLogo from "../token/TokenLogo" const AllowanceItem = ({ allowance, @@ -38,14 +38,15 @@ const AllowanceItem = ({ const history = useOnMountHistory() const { selectedNetwork, availableNetworks } = useBlankState()! - const isPendingUpdate = - allowance.status === TokenAllowanceStatus.AWAITING_TRANSACTION_RESULT - const [open, setOpen] = useState(false) const [isRevokeDisabled, setIsRevokeDisabled] = useState(false) const { isHovering: isHoveringButton, getIsHoveringProps } = useIsHovering() + const isPendingUpdate = + allowance.status === TokenAllowanceStatus.AWAITING_TRANSACTION_RESULT || + isRevokeDisabled + const revoke = async () => { if (!isRevokeDisabled) { setIsRevokeDisabled(true) @@ -165,7 +166,7 @@ const AllowanceItem = ({ "flex flex-row items-center justify-between py-4 mr-1 transition duration-300 -ml-6 px-6 w-[calc(100%+3rem)]", !isHoveringButton && !open && - "hover:cursor-pointer hover:bg-primary-100 hover:bg-opacity-50 active:bg-primary-200 active:bg-opacity-50" + "hover:cursor-pointer hover:bg-primary-grey-default hover:bg-opacity-50 active:bg-primary-grey-hover active:bg-opacity-50" )} >
- +
{name} {allowanceValue} @@ -224,8 +230,8 @@ const AllowanceItem = ({ disabled={isPendingUpdate} > Revoke { allowances.map((accountAllowance, accountAllowanceIndex) => (
{groupedByToken ? `${accountAllowance.groupBy.symbol} - ${accountAllowance.groupBy.name}` : accountAllowance.groupBy.name} -
+
{accountAllowance.allowances.map( (allowance, allowanceIndex) => { const [token, spender, showToken] = diff --git a/packages/ui/src/components/allowances/AllowancesFilterButton.tsx b/packages/ui/src/components/allowances/AllowancesFilterButton.tsx index 9e7bdf7c8..3e28884b7 100644 --- a/packages/ui/src/components/allowances/AllowancesFilterButton.tsx +++ b/packages/ui/src/components/allowances/AllowancesFilterButton.tsx @@ -21,7 +21,7 @@ const AllowancesFilterButton = ({ onChangeFilter: (newFilter: AllowancesFilters) => void }) => { return ( -
+
{ onChangeFilter(selected) @@ -31,7 +31,7 @@ const AllowancesFilterButton = ({ -
+
GROUP BY
{filterOptions.map(({ value, label }) => { @@ -40,7 +40,7 @@ const AllowancesFilterButton = ({ {label} diff --git a/packages/ui/src/components/allowances/AllowancesRefetchButton.tsx b/packages/ui/src/components/allowances/AllowancesRefetchButton.tsx index 1b61440f6..971e798dc 100644 --- a/packages/ui/src/components/allowances/AllowancesRefetchButton.tsx +++ b/packages/ui/src/components/allowances/AllowancesRefetchButton.tsx @@ -5,7 +5,7 @@ import OutlinedButton from "../ui/OutlinedButton" const AllowancesFilterButton = ({ onClick }: { onClick: () => void }) => { return (
diff --git a/packages/ui/src/components/assets/ActivityAllowancesView.tsx b/packages/ui/src/components/assets/ActivityAllowancesView.tsx index a499b7d9a..d2d1aceda 100644 --- a/packages/ui/src/components/assets/ActivityAllowancesView.tsx +++ b/packages/ui/src/components/assets/ActivityAllowancesView.tsx @@ -57,10 +57,10 @@ const ActivityAllowancesView = () => { disableStyles optionClassName={(value) => classnames( - "flex-1 flex flex-row items-center justify-center p-3 text-sm hover:text-primary-300", + "flex-1 flex flex-row items-center justify-center p-3 text-sm hover:text-primary-primary-blue-default", tab === value - ? "border-primary-300 border-b-2 text-primary-300 font-bold" - : "border-gray-200 text-gray-500 border-b hover:font-medium" + ? "border-primary-blue-default border-b-2 text-primary-blue-default font-semibold" + : "border-primary-grey-hover text-primary-grey-dark border-b hover:text-primary-blue-default font-medium" ) } containerClassName="flex flex-row -ml-6 !mt-0 w-[calc(100%+3rem)]" diff --git a/packages/ui/src/components/assets/AssetActivity.tsx b/packages/ui/src/components/assets/AssetActivity.tsx index ae629e951..cf657bfb8 100644 --- a/packages/ui/src/components/assets/AssetActivity.tsx +++ b/packages/ui/src/components/assets/AssetActivity.tsx @@ -2,23 +2,31 @@ import useTokenTransactions from "../../util/hooks/useTokenTransactions" import TransactionsList from "../transactions/TransactionsList" import { useOnMountHistory } from "../../context/hooks/useOnMount" import useGetAssetByTokenAddress from "../../util/hooks/useGetAssetByTokenAddress" +import { useBlankState } from "../../context/background/backgroundHooks" const AssetActivity = () => { + const { isNetworkChanging } = useBlankState()! const history: any = useOnMountHistory() const tokenAddress: string = history.location.state.address const token = useGetAssetByTokenAddress(tokenAddress)?.token const tokenTransactions = useTokenTransactions(token) return ( - <> +
{tokenTransactions.length > 0 ? ( - + ) : ( - + You have no transactions. )} - +
) } diff --git a/packages/ui/src/components/assets/AssetAllowances.tsx b/packages/ui/src/components/assets/AssetAllowances.tsx index 4ba70904c..941273505 100644 --- a/packages/ui/src/components/assets/AssetAllowances.tsx +++ b/packages/ui/src/components/assets/AssetAllowances.tsx @@ -40,7 +40,7 @@ const AssetAllowances = () => {
)) ) : ( - + {emptyMessage} )} diff --git a/packages/ui/src/components/assets/AssetAmountDisplay.tsx b/packages/ui/src/components/assets/AssetAmountDisplay.tsx index a469f857a..1d9d2384f 100644 --- a/packages/ui/src/components/assets/AssetAmountDisplay.tsx +++ b/packages/ui/src/components/assets/AssetAmountDisplay.tsx @@ -21,7 +21,7 @@ const AssetAmountDisplay: FC = ({ ) return ( -
+
{ const state = useBlankState()! @@ -89,6 +89,7 @@ const AssetDetailsPage = () => { )}`} close={false} disabled={isRemoving} + networkIndicator actions={ !isNative ? [ @@ -143,6 +144,7 @@ const AssetDetailsPage = () => { } /> } + showProviderStatus > { />
- - - + + + {token.name} @@ -192,7 +199,7 @@ const AssetDetailsPage = () => { )} - + { "w-8 h-8 overflow-hidden transition duration-300 rounded-full group-hover:opacity-75", disabledActions ? "bg-gray-300" - : "bg-primary-300" + : "bg-primary-blue-default" )} style={{ transform: "scaleY(-1)" }} >
- + Swap @@ -265,7 +272,7 @@ const AssetDetailsPage = () => { "w-8 h-8 overflow-hidden transition duration-300 rounded-full group-hover:opacity-75", disabledActions ? "bg-gray-300" - : "bg-primary-300" + : "bg-primary-blue-default" )} style={{ transform: "scaleY(-1)" }} > @@ -281,7 +288,7 @@ const AssetDetailsPage = () => { /> )}
- + Bridge diff --git a/packages/ui/src/components/assets/AssetDropdownDisplay.tsx b/packages/ui/src/components/assets/AssetDropdownDisplay.tsx index 303f7e584..d5da4c0d2 100644 --- a/packages/ui/src/components/assets/AssetDropdownDisplay.tsx +++ b/packages/ui/src/components/assets/AssetDropdownDisplay.tsx @@ -29,6 +29,8 @@ const AssetDropdownDisplay: FC = ({ logo={selectedAsset.token.logo} name={selectedAsset.token.name} className="mr-2" + filled={false} + logoSize="small" /> )}
@@ -40,7 +42,7 @@ const AssetDropdownDisplay: FC = ({ @@ -70,7 +72,7 @@ const AssetDropdownDisplay: FC = ({
) : (
-
Select...
+
Select token
) } diff --git a/packages/ui/src/components/assets/AssetList.tsx b/packages/ui/src/components/assets/AssetList.tsx index 32b5318fd..787de4746 100644 --- a/packages/ui/src/components/assets/AssetList.tsx +++ b/packages/ui/src/components/assets/AssetList.tsx @@ -51,7 +51,7 @@ const AssetList: FC<{ })} {searchValue && assets.length === 0 && (
-

+

The asset couldn’t be found, try adding it manually.

diff --git a/packages/ui/src/components/assets/AssetSelection.tsx b/packages/ui/src/components/assets/AssetSelection.tsx index e102191e6..c61b0e439 100644 --- a/packages/ui/src/components/assets/AssetSelection.tsx +++ b/packages/ui/src/components/assets/AssetSelection.tsx @@ -10,7 +10,7 @@ import { } from "react" import { TokenWithBalance, - useTokensList, + useTokenListWithNativeToken, } from "../../context/hooks/useTokensList" import { BigNumber } from "@ethersproject/bignumber" import { formatRounded } from "../../util/formatRounded" @@ -21,6 +21,8 @@ import { useSwappedTokenList } from "../../context/hooks/useSwappedTokenList" import AssetDropdownDisplay from "./AssetDropdownDisplay" import AssetList from "./AssetList" import { Token } from "@block-wallet/background/controllers/erc-20/Token" +import { useBlankState } from "../../context/background/backgroundHooks" +import { AssetsSortOptions } from "../../util/tokenUtils" export enum AssetListType { ALL = "ALL", @@ -77,15 +79,17 @@ export const AssetSelection: FC = ({ addTokenState, register, }) => { + const { tokensSortValue, hideSmallBalances } = useBlankState()! const [searchResult, setSearchResult] = useState([]) const [search, setSearch] = useState(null) const [assetList, setAssetList] = useState([]) + const defaultAssetList = useTokenListWithNativeToken( + tokensSortValue as AssetsSortOptions, + hideSmallBalances + ) - const { currentNetworkTokens, nativeToken } = useTokensList() const swappedAssetList = useSwappedTokenList() - const defaultAssetList = [nativeToken].concat(currentNetworkTokens) - useEffect(() => { // Only set asset list if this keeps being empty due to hooks init if (!assetList.length) { diff --git a/packages/ui/src/components/assets/AssetsButton.tsx b/packages/ui/src/components/assets/AssetsButton.tsx new file mode 100644 index 000000000..6443642b6 --- /dev/null +++ b/packages/ui/src/components/assets/AssetsButton.tsx @@ -0,0 +1,41 @@ +import classnames from "classnames" +import OutlinedButton from "../ui/OutlinedButton" +import { FC } from "react" + +const AssetsButton: FC<{ + onClick: () => void + imgClassName?: string + title?: string + icon?: string + disabled?: boolean +}> = ({ + onClick, + title = "", + imgClassName = "", + icon = "", + disabled = false, +}) => { + return ( +
+ + icon + +
+ ) +} + +export default AssetsButton diff --git a/packages/ui/src/components/assets/AssetsSort.tsx b/packages/ui/src/components/assets/AssetsSort.tsx new file mode 100644 index 000000000..4c0cbec15 --- /dev/null +++ b/packages/ui/src/components/assets/AssetsSort.tsx @@ -0,0 +1,87 @@ +import { FC, Fragment, useEffect, useState } from "react" +import Dropdown from "../ui/Dropdown/Dropdown" +import { DropdownOutlinedIconButton } from "../ui/Dropdown/DropdownButton" +import { IconName } from "../ui/Icon" +import { AssetsSortOptions } from "../../util/tokenUtils" +import { useBlankState } from "../../context/background/backgroundHooks" +import { setHideSmallBalances } from "../../context/commActions" +interface AssetsSortProps { + selectedValue: string + onClick: (selectedValue: AssetsSortOptions) => void +} + +const AssetsSort: FC = ({ selectedValue, onClick }) => { + const { nativeCurrency, hideSmallBalances } = useBlankState()! + const [hideSmallBalancesChk, setHideSmallBalancesChk] = + useState(hideSmallBalances) + + useEffect(() => { + setHideSmallBalances(hideSmallBalancesChk) + }, [hideSmallBalancesChk]) + + const sortOptions = [ + { label: "Name", value: AssetsSortOptions.NAME }, + { label: "Balance", value: AssetsSortOptions.BALANCE }, + { + label: nativeCurrency.toUpperCase() + " Value", + value: AssetsSortOptions.USD_VALUE, + }, + { label: "Stablecoins", value: AssetsSortOptions.STABLECOINS }, + { label: "Custom Order", value: AssetsSortOptions.CUSTOM }, + ] + + return ( +
+ { + onClick(selected) + }} + > + + + + +
+ SORT BY +
+ + {sortOptions.map(({ value, label }) => { + return ( + + + {label} + + + ) + })} + +
+ + + setHideSmallBalancesChk(!hideSmallBalancesChk) + } + /> +
+
+
+ ) +} + +export default AssetsSort diff --git a/packages/ui/src/components/bridge/BridgeDetails.tsx b/packages/ui/src/components/bridge/BridgeDetails.tsx index 4d922580c..4677ab865 100644 --- a/packages/ui/src/components/bridge/BridgeDetails.tsx +++ b/packages/ui/src/components/bridge/BridgeDetails.tsx @@ -61,6 +61,7 @@ const BridgeDetails: FC<{ if (tab) { setSelectedTab(tabs.find((t) => t.id === tab) || tabs[0]) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [tab]) if (!transaction) { @@ -72,13 +73,13 @@ const BridgeDetails: FC<{
-

+

Bridge details

} > - + {getChainName(Number(chainId))} @@ -93,7 +93,7 @@ const BridgeDetilsFees: FC<{ ))}
) : ( - + There are no fees for this operation! )} diff --git a/packages/ui/src/components/bridge/BridgeDetailsStatus.tsx b/packages/ui/src/components/bridge/BridgeDetailsStatus.tsx index 9fc6e82c2..dca29c2b6 100644 --- a/packages/ui/src/components/bridge/BridgeDetailsStatus.tsx +++ b/packages/ui/src/components/bridge/BridgeDetailsStatus.tsx @@ -21,7 +21,7 @@ const BridgeCompletedStatus = () => { return ( - Completed + Completed ) } @@ -69,7 +69,7 @@ const NotInitializedBridgeStatus = () => { return ( - Not initialized + Not initialized ) } @@ -88,8 +88,8 @@ const PendingBridgeStatus: FC<{ transaction: Partial }> = ({ return ( - - + + {pendingMessage.label} diff --git a/packages/ui/src/components/bridge/BridgeDetailsSummary.tsx b/packages/ui/src/components/bridge/BridgeDetailsSummary.tsx index 4b1802a13..1749e73f7 100644 --- a/packages/ui/src/components/bridge/BridgeDetailsSummary.tsx +++ b/packages/ui/src/components/bridge/BridgeDetailsSummary.tsx @@ -39,7 +39,7 @@ const Explorer = ({ className="flex flex-row items-center space-x-1" title={explorerName} > - + {explorerName} Open icon @@ -69,6 +69,7 @@ const BridgeDetailsSummary: FC = ({ bridgeTransactionsData ) : undefined + // eslint-disable-next-line react-hooks/exhaustive-deps }, [transaction]) if (!details) { @@ -158,7 +159,7 @@ const BridgeDetailsSummary: FC = ({ {transaction.transactionCategory === TransactionCategories.INCOMING_BRIDGE_REFUND && ( <> - + This is a refund transaction of a failed bridge. diff --git a/packages/ui/src/components/bridge/BridgeNotFoundItem.tsx b/packages/ui/src/components/bridge/BridgeNotFoundItem.tsx index b8d212b44..8e336562f 100644 --- a/packages/ui/src/components/bridge/BridgeNotFoundItem.tsx +++ b/packages/ui/src/components/bridge/BridgeNotFoundItem.tsx @@ -23,7 +23,7 @@ const BridgeNotFoundItem = ({ >
- {tool} + {tool}
-
+
{message} {details.map((d, i) => (
  • - {d} + {d}
  • ))}
    diff --git a/packages/ui/src/components/bridge/BridgeQuoteNotFoundErrorDetails.tsx b/packages/ui/src/components/bridge/BridgeQuoteNotFoundErrorDetails.tsx index 06fbb74d8..d81235680 100644 --- a/packages/ui/src/components/bridge/BridgeQuoteNotFoundErrorDetails.tsx +++ b/packages/ui/src/components/bridge/BridgeQuoteNotFoundErrorDetails.tsx @@ -16,7 +16,7 @@ const BridgeErrorDisplay = ({ }) => { let errors = new Map() - bridgeError.map((value) => { + bridgeError.forEach((value) => { if (errors.has(value.code)) { errors .get(value.code) @@ -32,7 +32,9 @@ const BridgeErrorDisplay = ({ return (
    - {tool} + + {tool} + {Array.from(errors.keys()).map((code) => { const errorSummary = errors.get(code) return ( diff --git a/packages/ui/src/components/bridge/ExpandableItem.tsx b/packages/ui/src/components/bridge/ExpandableItem.tsx index 304ddd6c6..b6f3dd7ea 100644 --- a/packages/ui/src/components/bridge/ExpandableItem.tsx +++ b/packages/ui/src/components/bridge/ExpandableItem.tsx @@ -2,14 +2,14 @@ import { FC, ReactNode, useState } from "react" import classnames from "classnames" import { ArrowUpDown } from "../icons/ArrowUpDown" -interface ExpandableItem { +interface ExpandableItemProps { children: ReactNode expanded: ReactNode className?: string expandable: boolean defaultExpanded?: boolean } -const ExpandableItem: FC = ({ +const ExpandableItem: FC = ({ children, expanded, className, diff --git a/packages/ui/src/components/bridge/FeeItem.tsx b/packages/ui/src/components/bridge/FeeItem.tsx index 8f25339eb..5293766ba 100644 --- a/packages/ui/src/components/bridge/FeeItem.tsx +++ b/packages/ui/src/components/bridge/FeeItem.tsx @@ -1,9 +1,6 @@ import { IToken } from "@block-wallet/background/controllers/erc-20/Token" import { formatUnits } from "@ethersproject/units" -import classnames from "classnames" -import { useState } from "react" import { formatRounded } from "../../util/formatRounded" -import { ArrowUpDown } from "../icons/ArrowUpDown" import { IBridgeFeeCost } from "@block-wallet/background/utils/bridgeApi" import ExpandableItem from "./ExpandableItem" @@ -33,13 +30,13 @@ const FeeItem = ({ + {detail.description ?? detail.name} } >
    - {detail.name}: + {detail.name}: {amountStr}
    diff --git a/packages/ui/src/components/bridge/FeeTokenSummaryDisplay.tsx b/packages/ui/src/components/bridge/FeeTokenSummaryDisplay.tsx index 8b3cf2347..ccdce8ce1 100644 --- a/packages/ui/src/components/bridge/FeeTokenSummaryDisplay.tsx +++ b/packages/ui/src/components/bridge/FeeTokenSummaryDisplay.tsx @@ -13,7 +13,7 @@ const FeeTokenSummaryDisplay = ({ }) => { return (
    - + {formatRounded( formatUnits(feeDetail.total, feeDetail.token.decimals), 4 diff --git a/packages/ui/src/components/button/ActionButton.tsx b/packages/ui/src/components/button/ActionButton.tsx index 5d093a714..1d451eb2d 100644 --- a/packages/ui/src/components/button/ActionButton.tsx +++ b/packages/ui/src/components/button/ActionButton.tsx @@ -1,5 +1,5 @@ import classnames from "classnames" -import { FunctionComponent, ReactElement } from "react" +import { FunctionComponent } from "react" import { Link } from "react-router-dom" import { Classes } from "../../styles" @@ -8,10 +8,11 @@ export const ActionButton: FunctionComponent<{ label: string to: string state?: any -}> = ({ icon, label, to, state }) => { + className?: string +}> = ({ icon, label, to, state, className }) => { return ( diff --git a/packages/ui/src/components/button/ButtonWithIcon.tsx b/packages/ui/src/components/button/ButtonWithIcon.tsx index 410723c90..3a698d173 100644 --- a/packages/ui/src/components/button/ButtonWithIcon.tsx +++ b/packages/ui/src/components/button/ButtonWithIcon.tsx @@ -12,7 +12,7 @@ export const ButtonWithIcon: FunctionComponent<{ const children = ( <> {icon && icon} - {label} + {label} ) @@ -23,7 +23,7 @@ export const ButtonWithIcon: FunctionComponent<{ className={ !disableStyles ? `flex flex-row items-center justify-between p-4 rounded-md text-sm transform transition-all duration-300 active:scale-95 - disabled:pointer-events-none bg-primary-100 hover:bg-primary-200 + disabled:pointer-events-none bg-primary-grey-default hover:bg-primary-grey-hover ${disabled ? "pointer-events-none" : ""} ` : "" diff --git a/packages/ui/src/components/button/OrderButton.tsx b/packages/ui/src/components/button/OrderButton.tsx new file mode 100644 index 000000000..4034d2dca --- /dev/null +++ b/packages/ui/src/components/button/OrderButton.tsx @@ -0,0 +1,22 @@ +import OutlinedButton from "../ui/OutlinedButton" +import { FC } from "react" +import Icon, { IconName } from "../ui/Icon" + +const OrderButton: FC<{ + onClick: () => void + title: string + buttonClassName?: string +}> = ({ onClick, title, buttonClassName }) => { + return ( +
    + + + +
    + ) +} + +export default OrderButton diff --git a/packages/ui/src/components/button/RoundedIconButton.tsx b/packages/ui/src/components/button/RoundedIconButton.tsx index 31cc31c01..521bf2a10 100644 --- a/packages/ui/src/components/button/RoundedIconButton.tsx +++ b/packages/ui/src/components/button/RoundedIconButton.tsx @@ -17,13 +17,13 @@ const RoundedIconButton: FC = ({
    - {children} + {children} ) } diff --git a/packages/ui/src/components/button/ToggleButton.tsx b/packages/ui/src/components/button/ToggleButton.tsx index 373448cee..e02453a18 100644 --- a/packages/ui/src/components/button/ToggleButton.tsx +++ b/packages/ui/src/components/button/ToggleButton.tsx @@ -35,8 +35,8 @@ const ToggleButton: FunctionComponent<{ const backgroundStyle = disabled ? "bg-gray-200" : isChecked - ? "bg-primary-300" - : "bg-primary-200" + ? "bg-primary-blue-default" + : "bg-primary-grey-hover" const onClick = () => { if (!disabled && !readOnly) { @@ -51,7 +51,7 @@ const ToggleButton: FunctionComponent<{ !disabled && !readOnly && "cursor-pointer" )} > - {label &&
    {label}
    } + {label &&
    {label}
    }
    ) : ( = ({
    @@ -43,7 +43,7 @@ const ChainDisplay: FC = ({ className="flex justify-start items-center flex-row py-3" title={name} > -
    +
    { @@ -55,7 +55,7 @@ const ChainDisplay: FC = ({ />
    - + {name} diff --git a/packages/ui/src/components/chain/ChainFiltersButton.tsx b/packages/ui/src/components/chain/ChainFiltersButton.tsx index cd6af2d45..70b79df75 100644 --- a/packages/ui/src/components/chain/ChainFiltersButton.tsx +++ b/packages/ui/src/components/chain/ChainFiltersButton.tsx @@ -41,7 +41,7 @@ const ChainFiltersButton: React.FC = ({ onChangeFilters, }) => { return ( -
    +
    { return onChangeFilters(handleOnChange(selected, filters)) @@ -57,7 +57,7 @@ const ChainFiltersButton: React.FC = ({ {label} diff --git a/packages/ui/src/components/chain/NetworkDisplayBadge.tsx b/packages/ui/src/components/chain/NetworkDisplayBadge.tsx index 7cf31f88b..2b1064ea4 100644 --- a/packages/ui/src/components/chain/NetworkDisplayBadge.tsx +++ b/packages/ui/src/components/chain/NetworkDisplayBadge.tsx @@ -2,57 +2,62 @@ import { Network } from "@block-wallet/background/utils/constants/networks" import { classnames } from "../../styles" import { getNetworkColor } from "../../util/getNetworkColor" import GenericTooltip from "../label/GenericTooltip" +import { useState } from "react" const NetworkDisplayBadge = ({ network, className, - fill = false, - truncate = false, + showName = false, }: { network: Network - fill?: boolean - truncate?: boolean className?: string + showName?: boolean }) => { const networkDesc = network.desc const networkColor = getNetworkColor(network) + const [hasImageError, setHasImageError] = useState(false) - // We limit the string chars here regardless of the truncate flag - // and the truncate class to prevent issues with the animated bullet size - // and to have a limit on huge network descriptions. - const formattedNetworkName = truncate - ? networkDesc.length > 42 - ? `${networkDesc.slice(0, 42)}...` - : networkDesc - : networkDesc return (
    - - - {formattedNetworkName} - + {!hasImageError && + network.iconUrls && + network.iconUrls.length > 0 ? ( + network icon { + setHasImageError(true) + }} + /> + ) : ( + + )} + + {showName && ( + + {networkDesc} + + )} + {networkDesc}

    } />
    diff --git a/packages/ui/src/components/chain/ProviderStatus.tsx b/packages/ui/src/components/chain/ProviderStatus.tsx new file mode 100644 index 000000000..0a084d1d8 --- /dev/null +++ b/packages/ui/src/components/chain/ProviderStatus.tsx @@ -0,0 +1,244 @@ +import { useRef, useState } from "react" +import { CSSTransition } from "react-transition-group" +import { AiFillInfoCircle } from "react-icons/ai" + +import { useBlankState } from "../../context/background/backgroundHooks" +import { switchProvider } from "../../context/commActions" +import { useSelectedNetwork } from "../../context/hooks/useSelectedNetwork" +import { useOnMountHistory } from "../../context/hooks/useOnMount" + +import { Classes, classnames } from "../../styles" +import CloseIcon from "../icons/CloseIcon" +import switchIcon from "../../assets/images/icons/switch.svg" +import Dialog from "../dialog/Dialog" +import Divider from "../Divider" +import { ButtonWithLoading } from "../button/ButtonWithLoading" + +enum ProviderType { + DEFAULT = "DEFAULT", + BACKUP = "BACKUP", + CUSTOM = "CUSTOM", + CURRENT = "CURRENT", +} + +const ProviderStatus = ({ onHomepage }: { onHomepage?: boolean }) => { + const { isUserNetworkOnline, isNetworkChanging, providerStatus } = + useBlankState()! + + const nodeRef = useRef(null) + const [isLoading, setIsLoading] = useState(false) + + const { + isCurrentProviderOnline, + isBackupProviderOnline, + isDefaultProviderOnline, + isUsingBackupProvider, + } = providerStatus + + const network = useSelectedNetwork() + const { chainId, currentRpcUrl, defaultRpcUrl } = network + + const history = useOnMountHistory() + + const [open, setOpen] = useState(false) + + const isUsingDefaultRpc = currentRpcUrl === defaultRpcUrl + + const showDefaultProviderRestored = + isDefaultProviderOnline && + isUsingBackupProvider && + isCurrentProviderOnline + + const showSwitchToBackup = + !isCurrentProviderOnline && isUsingDefaultRpc && isBackupProviderOnline + + const showSwitchToDefault = + !isCurrentProviderOnline && + !isUsingDefaultRpc && + isDefaultProviderOnline + + const showProviderStatus = + !isNetworkChanging && + ((!isCurrentProviderOnline && isUserNetworkOnline) || + showDefaultProviderRestored || + isUsingBackupProvider) + + const handleSwitch = async () => { + setIsLoading(true) + + setTimeout(async () => { + if ( + showSwitchToBackup || + showSwitchToDefault || + showDefaultProviderRestored + ) { + const providerType = showSwitchToBackup + ? ProviderType.BACKUP + : ProviderType.DEFAULT + await switchProvider({ chainId, providerType }) + } else { + history.push({ + pathname: "/settings/networks/details", + state: { + network, + }, + }) + } + setIsLoading(false) + setOpen(false) + }, 1000) + } + + const bannerTitle = showSwitchToBackup + ? "Default provider down." + : showDefaultProviderRestored + ? "Default provider restored." + : isUsingBackupProvider && isCurrentProviderOnline + ? "Backup provider active." + : "Provider down." + + const bannerCta = showSwitchToBackup + ? "Switch to backup" + : showDefaultProviderRestored + ? "Switch now" + : showSwitchToDefault + ? "Switch now" + : "Read more" + + const modalTitle = showSwitchToBackup + ? "Default Provider is Down" + : showDefaultProviderRestored + ? "Default Provider is Restored" + : isUsingBackupProvider && isCurrentProviderOnline + ? "Backup Provider is Active" + : "Provider is Down" + + const modalText = showSwitchToBackup + ? "Your wallet assets are secure and unaffected by this issue. To access your balances and perform transactions, consider switching to a backup provider." + : showDefaultProviderRestored + ? "You previously switched to a backup provider. You can now switch back to the default provider." + : showSwitchToDefault + ? "Your wallet assets are secure and unaffected by this issue. To access your balances and perform transactions, consider switching to BlockWallet’s default provider." + : isUsingBackupProvider && isCurrentProviderOnline + ? "Your wallet assets remain safe and unaffected. When a backup provider is active, Privacy Proxies will not be used when interacting with the blockchain." + : "Your wallet assets are secure and unaffected by this issue. To access your balances and perform transactions, consider changing the network's RPC URL from network settings." + + const modalCallOut = showSwitchToBackup + ? "Privacy Proxies are not enabled on the backup provider, which will impact the privacy of your transactions." + : showSwitchToDefault + ? "Privacy Proxies will be enabled on the default provider, which help protect the privacy of your transactions." + : isUsingBackupProvider && isCurrentProviderOnline + ? "You will be notified on your home screen once your default provider and Privacy Proxies become available." + : "Privacy Proxies are not enabled on custom providers, which will impact the privacy of your transactions." + + const SwitchCTA = + showSwitchToBackup || showDefaultProviderRestored || showSwitchToDefault + + const showModalCTA = + !(isUsingBackupProvider && isCurrentProviderOnline) || + showDefaultProviderRestored + + return ( + <> + +
    +
    + + {bannerTitle} +
    + setOpen(true)} + > + {bannerCta} + +
    +
    + setOpen(false)}> + +
    setOpen(false)} + className="cursor-pointer p-2 ml-auto -mr-2 text-gray-900 transition duration-300 rounded-full hover:bg-primary-grey-default hover:text-primary-blue-default" + > + +
    +
    + +
    +

    + {modalTitle} +

    +
    +
    {modalText}
    +
    + + Attention! + {" "} + {modalCallOut} +
    +
    +
    + {showModalCTA && ( + <> +
    + +
    + {isLoading ? ( + + ) : ( + + )} + + )} +
    + + ) +} + +export default ProviderStatus diff --git a/packages/ui/src/components/chain/RPCValidationEndLabelInfo.tsx b/packages/ui/src/components/chain/RPCValidationEndLabelInfo.tsx index adb9e68cd..6f33f9508 100644 --- a/packages/ui/src/components/chain/RPCValidationEndLabelInfo.tsx +++ b/packages/ui/src/components/chain/RPCValidationEndLabelInfo.tsx @@ -77,7 +77,7 @@ const RPCValidationEndLabelInfo = ({ = ({ + data, + clickable, + active, + hoverable, +}) => { + const [selected, setSelected] = useState(active ? active : false) + + // Render + return ( +
    (clickable ? setSelected(!selected) : null)} + > +
    + {data.symbol ?? } +
    + + {formatName(data.name, 22)} + +

    + {data.code.toUpperCase()} +

    + checkmark +
    + ) +} + +export default CurrencyDisplay diff --git a/packages/ui/src/components/currency/CurrencyDropdownDisplay.tsx b/packages/ui/src/components/currency/CurrencyDropdownDisplay.tsx new file mode 100644 index 000000000..385616e84 --- /dev/null +++ b/packages/ui/src/components/currency/CurrencyDropdownDisplay.tsx @@ -0,0 +1,51 @@ +import { FC } from "react" +import { Currency } from "@block-wallet/background/utils/currency" +import Spinner from "../spinner/Spinner" +import { Classes, classnames } from "../../styles" + +interface CurrencyDropdownDisplayProps { + selectedCurrency?: Currency + isLoading?: boolean + loadingText?: string +} + +const CurrencyDropdownDisplay: FC = ({ + selectedCurrency, + loadingText = "", + isLoading, +}) => { + if (isLoading) { + return ( +
    + + {loadingText} +
    + ) + } + + return selectedCurrency ? ( +
    + {selectedCurrency.symbol && ( +
    + {selectedCurrency.symbol} +
    + )} + {selectedCurrency.code.toUpperCase()} +
    + ) : ( +
    +
    Select currency
    +
    + ) +} + +export default CurrencyDropdownDisplay diff --git a/packages/ui/src/components/currency/CurrencyList.tsx b/packages/ui/src/components/currency/CurrencyList.tsx new file mode 100644 index 000000000..f8dbd672f --- /dev/null +++ b/packages/ui/src/components/currency/CurrencyList.tsx @@ -0,0 +1,56 @@ +import { FC } from "react" +import { Currency } from "@block-wallet/background/utils/currency" +import CurrencyDisplay from "./CurrencyDisplay" + +const CurrencyList: FC<{ + setActive?: () => void + onCurrencyClick: (currency: Currency, setActive?: () => void) => void + selectedCurrency?: string + currencies: Currency[] + searchValue: string | null + register: any +}> = ({ + onCurrencyClick, + setActive, + selectedCurrency, + currencies, + searchValue, + register, +}) => { + return ( +
    + + {currencies.map((currency) => { + return ( +
    onCurrencyClick(currency, setActive)} + > + +
    + ) + })} + {searchValue && currencies.length === 0 && ( +
    +

    + The currency couldn’t be found. +

    +
    + )} +
    + ) +} + +export default CurrencyList diff --git a/packages/ui/src/components/currency/CurrencySelection.tsx b/packages/ui/src/components/currency/CurrencySelection.tsx new file mode 100644 index 000000000..436da8b34 --- /dev/null +++ b/packages/ui/src/components/currency/CurrencySelection.tsx @@ -0,0 +1,134 @@ +import DropDownSelector from "../input/DropDownSelector" +import SearchInput from "../input/SearchInput" +import { + ChangeEvent, + Dispatch, + FC, + SetStateAction, + useEffect, + useState, +} from "react" +import { Currency } from "@block-wallet/background/utils/currency" +import CurrencyList from "./CurrencyList" +import CurrencyDropdownDisplay from "./CurrencyDropdownDisplay" + +interface CurrencySelectionProps { + defaultCurrencyList: Currency[] + onCurrencyChange: Dispatch> + selectedCurrency?: Currency + error?: string + register?: any + topMargin?: number + bottomMargin?: number + popupMargin?: number + dropdownWidth?: string + dropdownHeight?: string + popUpOpenLeft?: boolean + loadingText?: string +} + +export const CurrencySelection: FC = ({ + defaultCurrencyList, + onCurrencyChange, + selectedCurrency, + error, + topMargin, + bottomMargin, + popupMargin, + dropdownWidth, + dropdownHeight, + register, + popUpOpenLeft, + loadingText, +}) => { + const [searchResult, setSearchResult] = useState([]) + const [search, setSearch] = useState(null) + const [currencyList, setCurrencyList] = useState([]) + + useEffect(() => { + if (!currencyList.length) { + setCurrencyList(defaultCurrencyList) + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [defaultCurrencyList]) + + useEffect(() => { + const result = currencyList.filter(({ name, code }: Currency) => { + if (!search) { + return true + } + + const nameToSearch = name?.toUpperCase() + const codeToSearch = code.toUpperCase() + const uppercasedSearch = search.toUpperCase() + + return ( + nameToSearch?.includes(uppercasedSearch) || + codeToSearch.includes(uppercasedSearch) + ) + }) + + setSearchResult(result) + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [search, currencyList]) + + const onSearchInputChange = (event: ChangeEvent) => { + let value: string | null = event.target.value + + value = value.replace(/\W/g, "") + + if (!value) { + value = null + } + + setSearch(value) + } + + const onCurrencyClick = async ( + currency: Currency, + setActive?: Dispatch> + ) => { + onCurrencyChange(currency) + setActive && setActive(false) + } + + return ( + + } + error={error} + topMargin={topMargin || 0} + bottomMargin={bottomMargin || 0} + popupMargin={popupMargin || 16} + customWidth={dropdownWidth} + customHeight={dropdownHeight} + popUpOpenLeft={popUpOpenLeft} + disabled={!currencyList || currencyList.length === 0} + > +
    + +
    + +
    + ) +} diff --git a/packages/ui/src/components/dApp/DAppOrigin.tsx b/packages/ui/src/components/dApp/DAppOrigin.tsx index 48b4a8a6d..4e82523db 100644 --- a/packages/ui/src/components/dApp/DAppOrigin.tsx +++ b/packages/ui/src/components/dApp/DAppOrigin.tsx @@ -7,15 +7,22 @@ export const DAppOrigin = ({ iconURL: string | null name: string }) => { - return ( + return name.length < 40 ? (
    -
    +
    {iconURL && icon}
    -
    +
    {formatUrl(name)}
    + ) : ( +
    + {formatUrl(name)} +
    ) } diff --git a/packages/ui/src/components/dApp/DAppPopupHeader.tsx b/packages/ui/src/components/dApp/DAppPopupHeader.tsx index efee08c5c..86edbc693 100644 --- a/packages/ui/src/components/dApp/DAppPopupHeader.tsx +++ b/packages/ui/src/components/dApp/DAppPopupHeader.tsx @@ -34,7 +34,7 @@ const DAppPopupHeader: React.FC = ({
    = ({
    )} {showNetworkIndicator && ( - + )} ) diff --git a/packages/ui/src/components/dialog/CheckboxDialog.tsx b/packages/ui/src/components/dialog/CheckboxDialog.tsx index b4f4ad7cd..9ec14cabe 100644 --- a/packages/ui/src/components/dialog/CheckboxDialog.tsx +++ b/packages/ui/src/components/dialog/CheckboxDialog.tsx @@ -7,13 +7,14 @@ import Dialog from "./Dialog" const CheckBoxDialog: FunctionComponent<{ title: string - message: string + message: React.ReactElement | string open: boolean showCheckbox?: boolean checkboxText?: string closeText?: string confirmText?: string showCloseButton?: boolean + showXButton?: boolean onClose: () => void onCancel?: () => void onConfirm: (option?: boolean) => void @@ -25,6 +26,7 @@ const CheckBoxDialog: FunctionComponent<{ checkboxText = "", closeText = "Cancel", showCloseButton = true, + showXButton = true, confirmText = "Confirm", onClose, onCancel, @@ -37,7 +39,7 @@ const CheckBoxDialog: FunctionComponent<{ return (
    -

    {title}

    +

    {title}

    {message} @@ -58,16 +60,18 @@ const CheckBoxDialog: FunctionComponent<{
    )}
    - -
    onClose()} - className=" cursor-pointer p-2 ml-auto -mr-2 text-gray-900 transition duration-300 rounded-full hover:bg-primary-100 hover:text-primary-300" - > - -
    -
    + {showXButton && ( + +
    onClose()} + className=" cursor-pointer p-2 ml-auto -mr-2 text-gray-900 transition duration-300 rounded-full hover:bg-primary-grey-default hover:text-primary-blue-default" + > + +
    +
    + )}
    -
    +
    {showCloseButton && (
    diff --git a/packages/ui/src/components/dialog/DetailsDialog.tsx b/packages/ui/src/components/dialog/DetailsDialog.tsx index cb4354cb3..203a4a2d7 100644 --- a/packages/ui/src/components/dialog/DetailsDialog.tsx +++ b/packages/ui/src/components/dialog/DetailsDialog.tsx @@ -59,6 +59,7 @@ const DetailsDialog: FunctionComponent = ({ if (options.length === previousLengthRef.current) return setExpends(new Array(options.length).fill(expandedByDefault)) + // eslint-disable-next-line react-hooks/exhaustive-deps }, [options]) return ( @@ -72,7 +73,7 @@ const DetailsDialog: FunctionComponent = ({

    {title} @@ -91,13 +92,13 @@ const DetailsDialog: FunctionComponent = ({
    onClose()} - className=" cursor-pointer p-2 ml-auto -mr-2 text-gray-900 transition duration-300 rounded-full hover:bg-primary-100 hover:text-primary-300" + className=" cursor-pointer p-2 ml-auto -mr-2 text-gray-900 transition duration-300 rounded-full hover:bg-primary-grey-default hover:text-primary-blue-default" >
    {!fixedTitle && ( -

    +

    {title}

    )} @@ -122,7 +123,7 @@ const DetailsDialog: FunctionComponent = ({

    {option.title} diff --git a/packages/ui/src/components/dialog/ErrorDialog.tsx b/packages/ui/src/components/dialog/ErrorDialog.tsx index f347d70ad..f0fdc8563 100644 --- a/packages/ui/src/components/dialog/ErrorDialog.tsx +++ b/packages/ui/src/components/dialog/ErrorDialog.tsx @@ -6,11 +6,13 @@ import MessageDialog, { messageDialogProps } from "./MessageDialog" import Divider from "../Divider" import { Classes } from "../../styles" +import CloseIcon from "../icons/CloseIcon" type ErrorDialogProps = messageDialogProps & { onDone: React.MouseEventHandler | (() => void) timeout?: number // If setted, it will trigger onClickButton() after timeout value hideButton?: boolean + showCloseButton?: boolean } const ErrorDialog: FunctionComponent = ({ @@ -21,6 +23,7 @@ const ErrorDialog: FunctionComponent = ({ onClickOutside, onDone, timeout, + showCloseButton = false, }) => { useEffect(() => { if (timeout && open) { @@ -36,7 +39,22 @@ const ErrorDialog: FunctionComponent = ({ open={open} onClickOutside={onClickOutside} header={ - + <> + {showCloseButton && ( +
    + +
    + )} + + } footer={ !timeout && diff --git a/packages/ui/src/components/dialog/HardwareDeviceNotLinkedDialog.tsx b/packages/ui/src/components/dialog/HardwareDeviceNotLinkedDialog.tsx index 2b82c03b7..d3092edee 100644 --- a/packages/ui/src/components/dialog/HardwareDeviceNotLinkedDialog.tsx +++ b/packages/ui/src/components/dialog/HardwareDeviceNotLinkedDialog.tsx @@ -78,10 +78,10 @@ const HardwareDeviceNotLinkedDialog: React.FC<{
    diff --git a/packages/ui/src/components/dialog/HotkeysDialog.tsx b/packages/ui/src/components/dialog/HotkeysDialog.tsx new file mode 100644 index 000000000..165a951ac --- /dev/null +++ b/packages/ui/src/components/dialog/HotkeysDialog.tsx @@ -0,0 +1,85 @@ +import { FunctionComponent } from "react" +import { Classes, classnames } from "../../styles" +import { ButtonWithLoading } from "../button/ButtonWithLoading" +import Divider from "../Divider" +import CloseIcon from "../icons/CloseIcon" +import Dialog from "./Dialog" + +export type messageDialogProps = { + message: React.ReactElement | string + open: boolean + onClickOutside?: () => void + onDone?: () => void + onCancel?: () => void + showSubHeader?: boolean +} + +const HotkeysDialog: FunctionComponent = ({ + message, + open, + onClickOutside, + onDone, + onCancel, + showSubHeader, +}) => { + return ( + <> + + <> + +
    + +
    +
    +
    +
    +

    + Keyboard Shortcuts +

    +
    +
    + {showSubHeader && ( +

    + Shortcuts for current screen. +

    + )} +
    +
    + {message} +
    +
    +
    + +
    + + +
    +
    + +
    + + ) +} + +export default HotkeysDialog diff --git a/packages/ui/src/components/dialog/IsLockedDialog.tsx b/packages/ui/src/components/dialog/IsLockedDialog.tsx index 3925282ee..22d504d04 100644 --- a/packages/ui/src/components/dialog/IsLockedDialog.tsx +++ b/packages/ui/src/components/dialog/IsLockedDialog.tsx @@ -8,15 +8,16 @@ const IsLockedDialog = () => { return (
    -
    +
    {" "} - Warning: Locked Wallet + Wallet Locked
    - BlockWallet is locked. Please login again in the extension - to continue. + BlockWallet is locked.
    + Please open the BlockWallet extension and log in with your + password to continue.
    diff --git a/packages/ui/src/components/dialog/MessageDialog.tsx b/packages/ui/src/components/dialog/MessageDialog.tsx index 9e191c4a3..d5d5f5f1b 100644 --- a/packages/ui/src/components/dialog/MessageDialog.tsx +++ b/packages/ui/src/components/dialog/MessageDialog.tsx @@ -1,5 +1,5 @@ import { FunctionComponent } from "react" -import { classnames } from "../../styles" +import { Classes, classnames } from "../../styles" import Dialog from "./Dialog" import FullScreenDialog from "./FullScreenDialog" @@ -12,6 +12,8 @@ export type messageDialogProps = { onClickOutside?: () => void fullScreen?: boolean wideMargins?: boolean + onDone?: () => void + buttonLabel?: string } const MessageDialog: FunctionComponent = ({ @@ -23,6 +25,8 @@ const MessageDialog: FunctionComponent = ({ onClickOutside, wideMargins = true, fullScreen = false, + onDone, + buttonLabel, }) => { const DialogComponent = fullScreen ? FullScreenDialog : Dialog return ( @@ -33,17 +37,33 @@ const MessageDialog: FunctionComponent = ({ > <> {header} -

    {title}

    +

    + {title} +

    - + {message}
    + {buttonLabel && ( +
    + +
    + )} {footer} diff --git a/packages/ui/src/components/dialog/ProviderDownDialog.tsx b/packages/ui/src/components/dialog/ProviderDownDialog.tsx deleted file mode 100644 index b8ade153e..000000000 --- a/packages/ui/src/components/dialog/ProviderDownDialog.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { CgDanger } from "react-icons/cg" -import { useLocation } from "react-router-dom" -import { useBlankState } from "../../context/background/backgroundHooks" -import { useOnMountHistory } from "../../context/hooks/useOnMount" -import { Classes, classnames } from "../../styles" -import NetworkSelect from "../input/NetworkSelect" -import MessageDialog from "./MessageDialog" - -// Allowed paths to prevent the dialog from showing up -const allowedPaths = [ - "/chain/switch", - "/settings/networks", - "/settings/networks/details", -] - -const ProviderDownDialog = () => { - const { isProviderNetworkOnline, isUserNetworkOnline } = useBlankState()! - const location = useLocation() - const history = useOnMountHistory() - - const showDialog = - !isProviderNetworkOnline && - isUserNetworkOnline && - !allowedPaths.includes(location.pathname) - - return ( - <> - - - - - } - title="Network Provider Down" - message={ -
    -

    - Please check your internet connection and restart - the wallet using the button below. -

    -

    - If the problem persists, try{" "} - - history.push({ - pathname: "/settings/networks", - state: { - isFromHomePage: true, - }, - }) - } - > - changing the network's RPC URL - - . -

    -
    - } - open={showDialog} - footer={ - - } - /> - - ) -} - -export default ProviderDownDialog diff --git a/packages/ui/src/components/dialog/SuccessDialog.tsx b/packages/ui/src/components/dialog/SuccessDialog.tsx index 5708c1030..03683a130 100644 --- a/packages/ui/src/components/dialog/SuccessDialog.tsx +++ b/packages/ui/src/components/dialog/SuccessDialog.tsx @@ -7,6 +7,7 @@ import arrow from "../../assets/images/icons/arrow_right.svg" import classnames from "classnames" import { Classes } from "../../styles" import MessageDialog, { messageDialogProps } from "./MessageDialog" +import CloseIcon from "../icons/CloseIcon" const SuccessDialog: FunctionComponent< messageDialogProps & { @@ -17,6 +18,7 @@ const SuccessDialog: FunctionComponent< timeout?: number // If setted, it will trigger onDone() after timeout value txHash?: string // If valid hash, shows explorer link i.e. View on Etherscan onDone: () => void + showCloseButton?: boolean } > = ({ open, @@ -27,6 +29,7 @@ const SuccessDialog: FunctionComponent< hideButton, onDone, onClickOutside, + showCloseButton = false, }) => { const { selectedNetwork, availableNetworks } = useBlankState()! @@ -46,12 +49,27 @@ const SuccessDialog: FunctionComponent< open={open} onClickOutside={onClickOutside || onDone} header={ -
    - -
    + <> + {showCloseButton && ( +
    + +
    + )} +
    + +
    + } footer={ <> @@ -66,7 +84,7 @@ const SuccessDialog: FunctionComponent< )} target="_blank" rel="noopener noreferrer" - className="flex flex-row items-center space-x-2 text-sm font-bold text-primary-300" + className="flex flex-row items-center space-x-2 text-sm font-semibold text-primary-blue-default" > View on {explorerName} = ({ if (status === "success") { onSuccess() } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [status, history])} /> ) diff --git a/packages/ui/src/components/dialog/WaitingDialog.tsx b/packages/ui/src/components/dialog/WaitingDialog.tsx index 6f165b99c..a4dce7e9b 100644 --- a/packages/ui/src/components/dialog/WaitingDialog.tsx +++ b/packages/ui/src/components/dialog/WaitingDialog.tsx @@ -23,6 +23,7 @@ export type loadingDialogProps = { txHash?: string // Success only: If valid hash, shows explorer link i.e. View on Etherscan hideButton?: boolean clickOutsideToClose?: boolean + showCloseButton?: boolean } type state = { @@ -147,6 +148,7 @@ const WaitingDialog = ({ onDone, hideButton, clickOutsideToClose, + showCloseButton = false, }: loadingDialogProps) => { if (!open) return <> @@ -173,6 +175,7 @@ const WaitingDialog = ({ clickOutsideToClose === false ? () => {} : onDone } hideButton={hideButton} + showCloseButton={showCloseButton} /> ) case "error": @@ -187,6 +190,7 @@ const WaitingDialog = ({ clickOutsideToClose === false ? () => {} : onDone } hideButton={hideButton} + showCloseButton={showCloseButton} /> ) case "idle": diff --git a/packages/ui/src/components/error/ErrorFallbackPage.tsx b/packages/ui/src/components/error/ErrorFallbackPage.tsx index 38aa33729..217c61d47 100644 --- a/packages/ui/src/components/error/ErrorFallbackPage.tsx +++ b/packages/ui/src/components/error/ErrorFallbackPage.tsx @@ -92,7 +92,7 @@ const ErrorFallbackPage: FunctionComponent<{ onConfirm={handleReset} />
    -
    + -
    + -
    +
    Download state logs for support diff --git a/packages/ui/src/components/gas/GasPricesInfo.tsx b/packages/ui/src/components/gas/GasPricesInfo.tsx index 89562284b..b5f6b8f34 100644 --- a/packages/ui/src/components/gas/GasPricesInfo.tsx +++ b/packages/ui/src/components/gas/GasPricesInfo.tsx @@ -25,6 +25,8 @@ import { SEND_GAS_COST } from "../../util/constants" import car from "../../assets/images/icons/car.svg" import scooter from "../../assets/images/icons/scooter.svg" import plane from "../../assets/images/icons/plane.svg" +import { useHotkeys } from "react-hotkeys-hook" +import { componentsHotkeys } from "../../util/hotkeys" export type DisplayGasPricesData = { baseFee?: string @@ -87,7 +89,7 @@ const GasDataInfo: FC<{ label: string; value: string }> = ({ return (
  • {label}: - {value} + {value}
  • ) } @@ -119,7 +121,7 @@ const GasPricesInfo: FC = () => { localeInfo, networkNativeCurrency, isNetworkChanging, - isRatesChangingAfterNetworkChange, + hotkeysEnabled, } = useBlankState()! const { @@ -139,10 +141,26 @@ const GasPricesInfo: FC = () => { GAS_LIMITS[calculateGasCost] ) - const isLoading = - isNetworkChanging || - isRatesChangingAfterNetworkChange || - !displayGasPrices + const isLoading = isNetworkChanging || !displayGasPrices + + const gasPricesInfoHotkeys = componentsHotkeys.GasPricesInfo + useHotkeys(gasPricesInfoHotkeys, (e) => { + if (!hotkeysEnabled) return + + const keyPressed = e.code + .replace(/key/i, "") + .replace(/digit/i, "") + .replace(/numpad/i, "") + .toLowerCase() + + if (showGasLevels) { + if (e.altKey && keyPressed === "g") { + setActive(!active) + } else if (keyPressed === "enter") { + setActive(false) + } + } + }) return ( <> @@ -150,7 +168,7 @@ const GasPricesInfo: FC = () => {
    { @@ -164,7 +182,7 @@ const GasPricesInfo: FC = () => { svgClassName="rounded-md" /> ) : ( - + {displayGasPrices.average.totalGwei} )} @@ -177,14 +195,14 @@ const GasPricesInfo: FC = () => {
    setActive(false)} - className=" cursor-pointer p-2 ml-auto -mr-2 text-gray-900 transition duration-300 rounded-full hover:bg-primary-100 hover:text-primary-300" + className=" cursor-pointer p-2 ml-auto -mr-2 text-gray-900 transition duration-300 rounded-full hover:bg-primary-grey-default hover:text-primary-blue-default" >
    -

    +

    Gas Prices

    @@ -195,7 +213,7 @@ const GasPricesInfo: FC = () => { > { ] return (
    { }{" "} GWEI - + ~ {gasPriceToNativeCurrency( gasPriceData.totalTransactionCost, diff --git a/packages/ui/src/components/hardwareWallet/AdvancedSettings.tsx b/packages/ui/src/components/hardwareWallet/AdvancedSettings.tsx new file mode 100644 index 000000000..5f38109bd --- /dev/null +++ b/packages/ui/src/components/hardwareWallet/AdvancedSettings.tsx @@ -0,0 +1,138 @@ +import classnames from "classnames" +import { useState, useEffect, useRef } from "react" +import { AiFillInfoCircle } from "react-icons/ai" +import { Devices, HDPaths } from "../../context/commTypes" +import { Classes } from "../../styles" +import { LINKS } from "../../util/constants" +import { useOnClickOutside } from "../../util/useOnClickOutside" +import Divider from "../Divider" +import FullScreenDialog from "../dialog/FullScreenDialog" +import CloseIcon from "../icons/CloseIcon" +import Select from "../input/Select" +import Tooltip from "../label/Tooltip" +import Icon, { IconName } from "../ui/Icon" + +export const AccountsPageAdvancedSettings = ({ + currentHDPath, + vendor, + disabled, + setHDPath, +}: { + currentHDPath: string + vendor: Devices + disabled?: boolean + setHDPath: (hdPath: string) => void +}) => { + const [openModal, setOpenModal] = useState(false) + const hdPaths = HDPaths[vendor] + const [selectedHDPath, setSelectedHDPath] = useState(currentHDPath) + + useEffect(() => { + setSelectedHDPath(currentHDPath) + }, [currentHDPath]) + + const ref = useRef(null) + useOnClickOutside(ref, () => { + setOpenModal(false) + }) + return ( + <> +
    !disabled && setOpenModal(true)} + className={classnames( + "w-full pl-2 pt-4 bg-white rounded-md cursor-pointer underline-offset-1 flex items-center justify-between", + disabled + ? "text-gray-200 !cursor-not-allowed" + : "hover:underline" + )} + > + + Advanced Settings + +
    + +
    +
    + + +
    +
    + + Advanced Settings + +
    + + + + +
    +
    +
    setOpenModal(false)} + className=" cursor-pointer p-2 text-gray-900 transition duration-300 rounded-full hover:bg-primary-grey-default hover:text-primary-blue-default" + > + +
    +
    + +
    + + If you don't see the accounts you're expecting, try + switching the HD path. + +
    + + +
    +
    + +
    +
    +
    + + +
    +
    +
    + + ) +} diff --git a/packages/ui/src/components/hardwareWallet/HardwareWalletAccount.tsx b/packages/ui/src/components/hardwareWallet/HardwareWalletAccount.tsx new file mode 100644 index 000000000..0c1a2a176 --- /dev/null +++ b/packages/ui/src/components/hardwareWallet/HardwareWalletAccount.tsx @@ -0,0 +1,130 @@ +import { DeviceAccountInfo } from "@block-wallet/background/controllers/AccountTrackerController" +import classnames from "classnames" +import { BigNumber } from "@ethersproject/bignumber" +import { formatUnits } from "ethers/lib/utils" +import { useState } from "react" +import { getAccountBalance } from "../../context/commActions" +import { useTokensList } from "../../context/hooks/useTokensList" +import { Classes } from "../../styles" +import { formatHash } from "../../util/formatAccount" +import { formatRounded } from "../../util/formatRounded" +import { getAccountColor } from "../../util/getAccountColor" +import { ViewOnExplorerButton } from "../button/ViewOnExplorerButtons" +import AccountIcon from "../icons/AccountIcon" +import EyeRevealIcon from "../icons/EyeRevealIcon" +import Spinner from "../spinner/Spinner" + +export const HardwareWalletAccount = ({ + account, + accountsBalances, + selected = false, + disabled = false, + onChange, + onBalanceFetched, +}: { + account: DeviceAccountInfo + accountsBalances: { [address in string]: BigNumber } + selected: boolean + disabled: boolean + onChange: () => void + onBalanceFetched: (address: string, balance: BigNumber) => void +}) => { + const { nativeToken } = useTokensList() + const [isLoading, setIsLoading] = useState(false) + const [balance, setBalance] = useState( + account.address in accountsBalances + ? formatRounded( + formatUnits( + accountsBalances[account.address] || "0", + nativeToken.token.decimals + ), + 5 + ) + ` ${nativeToken.token.symbol}` + : "*******" + ) + + const fetchBalance = async () => { + try { + setIsLoading(true) + const balanceFetched = await getAccountBalance(account.address) + onBalanceFetched(account.address, balanceFetched) + setBalance( + formatRounded( + formatUnits( + balanceFetched || "0", + nativeToken.token.decimals + ), + 5 + ) + ` ${nativeToken.token.symbol}` + ) + } catch (error) { + setBalance("") + } finally { + setIsLoading(false) + } + } + return ( + + ) +} diff --git a/packages/ui/src/components/home/ActivityAssetsView.tsx b/packages/ui/src/components/home/ActivityAssetsView.tsx new file mode 100644 index 000000000..059ce07d2 --- /dev/null +++ b/packages/ui/src/components/home/ActivityAssetsView.tsx @@ -0,0 +1,92 @@ +import { PopupTabs } from "@block-wallet/background/controllers/PreferencesController" +import { FunctionComponent, useEffect, useState } from "react" +import { useHotkeys } from "react-hotkeys-hook" +import { useBlankState } from "../../context/background/backgroundHooks" +import { updatePopupTab } from "../../context/commActions" +import { useOnMountHistory } from "../../context/hooks/useOnMount" +import ActivityList from "./../ActivityList" +import AssetsList from "./AssetsList" +import HorizontalSelect from "./../input/HorizontalSelect" +import { componentsHotkeys } from "../../util/hotkeys" + +const tabs = [ + { + label: "Activity", + component: ActivityList, + }, + { + label: "Assets", + component: AssetsList, + }, +] + +const ActivityAssetsView: FunctionComponent<{ initialTab: PopupTabs }> = ({ + initialTab, +}) => { + const history = useOnMountHistory() + const { popupTab, hotkeysEnabled } = useBlankState()! + const initialTabIndex = initialTab === "activity" ? 0 : 1 + const [tab, setTab] = useState(tabs[initialTabIndex]) + const [currentTabLabel, setCurrentTabLabel] = useState(tab.label) + const TabComponent = tab.component + + const onTabChange = (value: { + label: string + component: () => JSX.Element + }) => { + setTab(value) + updatePopupTab(value.label.toLowerCase() as PopupTabs) + } + + //UseHotkeys changes state.popupTab, we do it to change the tab in real time, otherwise it will change only next time we open the extension + useEffect(() => { + setCurrentTabLabel(popupTab) + if (popupTab !== tab.label.toLocaleLowerCase()) { + const newTab = tabs.find( + (l) => l.label.toLocaleLowerCase() === popupTab + ) + if (newTab) onTabChange(newTab) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [popupTab]) + + //Adding useHotkey to add new token, only on Assets View + const activityAssetsViewHotkeys = componentsHotkeys.ActivityAssetsView + useHotkeys(activityAssetsViewHotkeys, () => { + if (!hotkeysEnabled) return + if (currentTabLabel.toLocaleLowerCase() === "assets") { + history.push({ + pathname: "/settings/tokens/add", + state: { + from: history.location.pathname, + }, + }) + } + }) + + return ( +
    + t.label} + disableStyles + optionClassName={(value) => + `flex-1 flex flex-row items-center justify-center p-3 text-sm hover:text-primary-blue-default ${ + tab === value + ? "border-primary-blue-default border-b-2 text-primary-blue-default font-semibold" + : "border-primary-grey-hover text-primary-grey-dark border-b hover:text-primary-blue-default font-medium" + }` + } + containerClassName="flex flex-row -ml-6" + containerStyle={{ width: "calc(100% + 2 * 1.5rem)" }} + /> +
    + +
    +
    + ) +} + +export default ActivityAssetsView diff --git a/packages/ui/src/components/home/AssetsList.tsx b/packages/ui/src/components/home/AssetsList.tsx new file mode 100644 index 000000000..b12af7ab5 --- /dev/null +++ b/packages/ui/src/components/home/AssetsList.tsx @@ -0,0 +1,290 @@ +import { Fragment, FunctionComponent, useState, useEffect, useRef } from "react" +///Hooks +import { useOnMountHistory } from "../../context/hooks/useOnMount" +import { + TokenList, + useTokenListWithNativeToken, +} from "../../context/hooks/useTokensList" +import useCurrencyFromatter from "../../util/hooks/useCurrencyFormatter" +import useTokenSearch from "../../util/hooks/token/useTokenSearch" + +///Icons + Classes +import { Classes, classnames } from "../../styles/classes" +import plus from "../../assets/images/icons/plus.svg" +import unknownTokenIcon from "../../assets/images/unknown_token.svg" +import ChevronRightIcon from "../icons/ChevronRightIcon" +import order from "../../assets/images/icons/order.svg" + +///Background +import { Token } from "@block-wallet/background/controllers/erc-20/Token" + +///Utils +import { BigNumber } from "@ethersproject/bignumber" +import { formatUnits } from "@ethersproject/units" +import { formatRounded } from "../../util/formatRounded" +import { AssetsSortOptions, isNativeTokenAddress } from "../../util/tokenUtils" +import AssetsLoadingSkeleton from "./../skeleton/AssetsLoadingSkeleton" +import { useBlankState } from "../../context/background/backgroundHooks" +import SearchInput from "../input/SearchInput" +import AssetsButton from "../assets/AssetsButton" +import AssetsSort from "../assets/AssetsSort" +import { setTokensSortValue } from "../../context/commActions" +import AutoSizer from "react-virtualized/dist/commonjs/AutoSizer" +import List from "react-virtualized/dist/commonjs/List" +export type AssetItem = { + token: Token + balance: BigNumber +} + +export const AssetIcon: FunctionComponent<{ + asset: Partial + filled?: boolean +}> = ({ asset, filled }) => ( +
    + { + { + ;(e.target as any).onerror = null + ;(e.target as any).src = unknownTokenIcon + }} + alt={asset.symbol || ""} + className="rounded-full" + /> + } +
    +) + +const Asset: FunctionComponent<{ + asset: AssetItem + pushDeleteTokens: Function +}> = ({ asset }) => { + const history: any = useOnMountHistory() + const formatter = useCurrencyFromatter() + return ( +
    + history.push({ + pathname: `/asset/details`, + state: { + address: asset.token.address, + transitionDirection: "left", + }, + }) + } + className={classnames( + "flex flex-row items-center justify-between px-6 py-4 transition duration-300", + "hover:bg-primary-100 hover:bg-opacity-50 active:bg-primary-200 active:bg-opacity-50", + "cursor-pointer" + )} + style={{ width: "calc(88% + 3rem)" }} + role="listitem" + aria-label={asset.token.symbol} + > +
    + +
    + + {` + ${formatRounded( + formatUnits( + asset.balance || "0", + asset.token.decimals + ), + 4 + )} + ${asset.token.symbol} + `} + + + {formatter.format( + asset.balance || BigNumber.from(0), + asset.token.symbol, + asset.token.decimals, + isNativeTokenAddress(asset.token.address) + )} + +
    +
    +
    + +
    +
    + ) +} + +const SubAssetList: FunctionComponent<{ assets: TokenList }> = ({ assets }) => { + const state = useBlankState()! + + const isLoading = state.isNetworkChanging + + const [deletedTokens, setDeletedTokens] = useState([] as string[]) + const [currentAssets, setCurrentAssets] = useState(assets) + const pushDeleteTokens = (deleteToken: string) => { + setDeletedTokens([...deletedTokens, deleteToken]) + } + useEffect(() => { + setCurrentAssets( + assets.filter((t) => !deletedTokens.includes(t.token.address)) + ) + }, [assets, deletedTokens]) + // the action of delete a token is not sync, we include this blick of code for not showing deleted tokens while they are being deleted. + const newDeleted: string[] = [] + deletedTokens.forEach((t) => { + if (assets.map((a) => a.token.address).includes(t)) { + newDeleted.push(t) + } + }) + if (deletedTokens.length !== newDeleted.length) { + setDeletedTokens(newDeleted) + } + + const ref = useRef() + + useEffect(() => { + // react-virtualized does not recompute row height when the underlying transaction data changes. + // thats why we force a height recompution here and adjust tx height based on its state. + ref.current && ref.current.recomputeRowHeights(0) + }, [currentAssets]) + + return ( + <> + {isLoading ? ( +
    + +
    + ) : ( + + {({ height }) => ( + ( +
    + {index > 0 ? ( +
    +
    +
    + ) : null} + +
    + )} + >
    + )} +
    + )} + + ) +} + +const AssetsList = () => { + const { tokensSortValue, hideSmallBalances } = useBlankState()! + const history = useOnMountHistory() + const [sortValue, setSortValue] = useState( + tokensSortValue as AssetsSortOptions + ) + const currentNetworkTokens = useTokenListWithNativeToken( + sortValue, + hideSmallBalances + ) + const searchInputRef = useRef(null) + const { search, tokensResult, onChangeSearch } = + useTokenSearch(currentNetworkTokens) + + useEffect(() => { + const updateSortValue = async () => { + await setTokensSortValue(sortValue) + } + + if (sortValue !== tokensSortValue) { + updateSortValue() + } + }, [sortValue, tokensSortValue]) + + return ( + <> +
    +
    +
    + { + onChangeSearch(e.target.value) + }} + debounced + defaultValue={search} + ref={searchInputRef} + showClearIcon={true} + /> +
    + { + history.push({ + pathname: "/settings/tokens/add", + }) + }} + title="Add Token" + icon={plus} + /> + + { + history.push({ + pathname: "/accounts/menu/tokensOrder", + state: { isFromHomePage: true }, + }) + }} + title="Edit Assets Order" + icon={order} + disabled={sortValue !== AssetsSortOptions.CUSTOM} + /> +
    +
    +
    + +
    + + ) +} + +export default AssetsList diff --git a/packages/ui/src/components/hotkeys/DisplayHotkey.tsx b/packages/ui/src/components/hotkeys/DisplayHotkey.tsx new file mode 100644 index 000000000..de29dfa49 --- /dev/null +++ b/packages/ui/src/components/hotkeys/DisplayHotkey.tsx @@ -0,0 +1,164 @@ +import classnames from "classnames" +import React from "react" +import { FC } from "react" +import { getCurrentOS } from "../../context/util/platform" +import { useHotkeysByPath } from "../../util/hotkeys" +import Divider from "../Divider" + +interface DisplayHotkeyProps { + description: string + alt?: boolean + ctrl?: boolean + hotkey: string + className?: string + currentOS: "win" | "mac" | "UNIX" | "Linux" | undefined +} + +interface DisplayHotkeysByPathProp { + pathName: string + permissions?: { [action: string]: boolean } + includeDivider?: boolean +} + +export const DisplayHotkey: FC = ({ + description, + alt = false, + ctrl = false, + hotkey, + className = "", + currentOS, +}) => { + return ( +
    +
    {description}
    +
    + {ctrl && ( + <> +
    + Ctrl +
    +
    +
    + + )} + {alt && ( + <> +
    + {currentOS === "mac" ? "⌥" : "Alt"} +
    +
    +
    + + )} +
    + {hotkey} +
    +
    +
    + ) +} + +export const DisplayHotkeysByPath: FC = ({ + pathName, + permissions, + includeDivider, +}) => { + const hotkeys = useHotkeysByPath(pathName) + const currentOS = getCurrentOS() + + let hotkeysElement: JSX.Element[] + let showDivider = false + if (hotkeys !== "") { + hotkeysElement = hotkeys["ALT"].map((hotkey, index) => { + //If we have permissions array, check if that hotkey is there. If it is check if enabled, if it is not we allow it. + const enabledHotkey = permissions + ? permissions[ + pathName + "/alt/" + hotkey.hotkey.toLowerCase() + ] === undefined || + permissions[pathName + "/alt/" + hotkey.hotkey.toLowerCase()] + : true + + if (enabledHotkey) { + if (index > 0 && !showDivider) showDivider = true + return ( + + {includeDivider && showDivider && ( + + )} + + + ) + } else { + return + } + }) + + hotkeys["CTRLALT"].map((hotkey, index) => { + if (index > 0 && !showDivider) showDivider = true + return hotkeysElement.push( + + {includeDivider && showDivider && ( + + )} + + + ) + }) + + hotkeys["CTRL"].map((hotkey, index) => { + if (index > 0 && !showDivider) showDivider = true + return hotkeysElement.push( + + {includeDivider && showDivider && ( + + )} + + + ) + }) + + return <>{hotkeysElement} + } + + return <> +} + +const DisplayHotkeys = { DisplayHotkey, DisplayHotkeysByPath } + +export default DisplayHotkeys diff --git a/packages/ui/src/components/hotkeys/HotkeysCollapsedMessage.tsx b/packages/ui/src/components/hotkeys/HotkeysCollapsedMessage.tsx new file mode 100644 index 000000000..23196487c --- /dev/null +++ b/packages/ui/src/components/hotkeys/HotkeysCollapsedMessage.tsx @@ -0,0 +1,73 @@ +import { FC, useState } from "react" +import { useHotkeys } from "react-hotkeys-hook" +import { useBlankState } from "../../context/background/backgroundHooks" +import { useOnMountHistory } from "../../context/hooks/useOnMount" +import CollapsableMessage from "../CollapsableMessage" +import { DisplayHotkeysByPath } from "../hotkeys/DisplayHotkey" +import ConfirmDialog from "../dialog/ConfirmDialog" +import { componentsHotkeys } from "../../util/hotkeys" + +export const HotkeysCollapsedMessage: FC<{ + hotkeysPermissions?: { [action: string]: boolean } +}> = ({ hotkeysPermissions }) => { + const history = useOnMountHistory() + const currentLocation = history.location.pathname + const [isMessageVisible, setIsMessageVisible] = useState(false) + const { hotkeysEnabled } = useBlankState()! + + const hotkeysCollapsedMessageHotkeys = + componentsHotkeys.HotkeysCollapsedMessage + useHotkeys(hotkeysCollapsedMessageHotkeys, (e) => { + const keyPressed = e.code + .replace(/key/i, "") + .replace(/digit/i, "") + .replace(/numpad/i, "") + .toLowerCase() + if (e.altKey && keyPressed === "k") { + setIsMessageVisible(true) + } else { + setIsMessageVisible(false) + } + }) + + return hotkeysEnabled ? ( + + +
    + ), + }} + type="hotkeys" + isCollapsedByDefault + showCollapsedMessage={isMessageVisible} + collapsedMessage={""} + onDismiss={() => setIsMessageVisible(false)} + onConfirm={() => { + history.push("/settings/preferences/hotkeys") + }} + showSubHeader={hotkeysEnabled} + /> + ) : ( + setIsMessageVisible(false)} + onConfirm={() => { + history.push("/settings/preferences/hotkeys") + }} + confirmText="Settings" + /> + ) +} + +export default HotkeysCollapsedMessage diff --git a/packages/ui/src/components/icons/AppIcon.tsx b/packages/ui/src/components/icons/AppIcon.tsx index 73daae4ab..cf7df0ecc 100644 --- a/packages/ui/src/components/icons/AppIcon.tsx +++ b/packages/ui/src/components/icons/AppIcon.tsx @@ -14,24 +14,28 @@ const AppIcon = ({ iconSize, background = true, title, -}: AppIconProps) => ( -
    - {iconURL ? ( - icon - ) : null} -
    -) - +}: AppIconProps) => { + return ( +
    + {iconURL ? ( + icon + ) : null} +
    + ) +} export default AppIcon diff --git a/packages/ui/src/components/icons/ArrowUpDown.tsx b/packages/ui/src/components/icons/ArrowUpDown.tsx index 6a200c49c..d74515ade 100644 --- a/packages/ui/src/components/icons/ArrowUpDown.tsx +++ b/packages/ui/src/components/icons/ArrowUpDown.tsx @@ -8,7 +8,7 @@ export const ArrowUpDown: FunctionComponent<{ { className="border-2 border-black rounded-full flex items-center justify-center" style={{ width: circleSize, height: circleSize }} > - {text} + {text}
    ) } diff --git a/packages/ui/src/components/icons/EditIcon.tsx b/packages/ui/src/components/icons/EditIcon.tsx new file mode 100644 index 000000000..871d3f2ef --- /dev/null +++ b/packages/ui/src/components/icons/EditIcon.tsx @@ -0,0 +1,20 @@ +const EditIcon = () => ( + + + + +) + +export default EditIcon diff --git a/packages/ui/src/components/icons/EmptyDrawerIcon.tsx b/packages/ui/src/components/icons/EmptyDrawerIcon.tsx index e13b1c96a..7af5af895 100644 --- a/packages/ui/src/components/icons/EmptyDrawerIcon.tsx +++ b/packages/ui/src/components/icons/EmptyDrawerIcon.tsx @@ -8,21 +8,32 @@ const EmptyDrawer: React.FC<{ className: string }> = ({ className }) => { className={classnames("w-20 h-20", className)} xmlns="http://www.w3.org/2000/svg" > - - - - - - + + + + + + + + + + + + + ) } diff --git a/packages/ui/src/components/icons/ExclamationCircleIconFull.tsx b/packages/ui/src/components/icons/ExclamationCircleIconFull.tsx index de7076b76..9a3bf7363 100644 --- a/packages/ui/src/components/icons/ExclamationCircleIconFull.tsx +++ b/packages/ui/src/components/icons/ExclamationCircleIconFull.tsx @@ -21,8 +21,8 @@ const PROFILES: ProfileDef = { icon: "#FFBB54", }, info: { - background: "#49c5ff", - icon: "white", + background: " #08090A", + icon: "#EEF1EF", }, outlined: { background: "#FFBB54", diff --git a/packages/ui/src/components/icons/EyeCloseIcon.tsx b/packages/ui/src/components/icons/EyeCloseIcon.tsx new file mode 100644 index 000000000..1fd73b000 --- /dev/null +++ b/packages/ui/src/components/icons/EyeCloseIcon.tsx @@ -0,0 +1,26 @@ +import { FunctionComponent } from "react" +import classnames from "classnames" +const EyeCloseIcon: FunctionComponent<{ className?: string }> = ({ + className, +}) => { + return ( + + + + + ) +} + +export default EyeCloseIcon diff --git a/packages/ui/src/components/icons/EyeOpenIcon.tsx b/packages/ui/src/components/icons/EyeOpenIcon.tsx new file mode 100644 index 000000000..a50a6bd7b --- /dev/null +++ b/packages/ui/src/components/icons/EyeOpenIcon.tsx @@ -0,0 +1,22 @@ +import { FunctionComponent } from "react" +import classnames from "classnames" +const EyeOpenIcon: FunctionComponent<{ className?: string }> = ({ + className, +}) => { + return ( + + + + ) +} + +export default EyeOpenIcon diff --git a/packages/ui/src/components/icons/FilterIcon.tsx b/packages/ui/src/components/icons/FilterIcon.tsx new file mode 100644 index 000000000..3b455be28 --- /dev/null +++ b/packages/ui/src/components/icons/FilterIcon.tsx @@ -0,0 +1,25 @@ +import classnames from "classnames" + +const FilterIcon: React.FC<{ className?: string }> = ( + { className } = { className: "w-8 h-8" } +) => { + return ( + + + + ) +} + +export default FilterIcon diff --git a/packages/ui/src/components/icons/ImportIcon.tsx b/packages/ui/src/components/icons/ImportIcon.tsx index c83d61fc6..582d5831a 100644 --- a/packages/ui/src/components/icons/ImportIcon.tsx +++ b/packages/ui/src/components/icons/ImportIcon.tsx @@ -9,24 +9,24 @@ const ImportIcon: React.FC<{ className?: string }> = ({ className }) => { xmlns="http://www.w3.org/2000/svg" > ) diff --git a/packages/ui/src/components/icons/OpenExplorerIcon.tsx b/packages/ui/src/components/icons/OpenExplorerIcon.tsx index 30f9be35e..9afc04819 100644 --- a/packages/ui/src/components/icons/OpenExplorerIcon.tsx +++ b/packages/ui/src/components/icons/OpenExplorerIcon.tsx @@ -1,10 +1,11 @@ -const OpenExplorerIcon = () => ( +const OpenExplorerIcon = (props: { className?: string }) => ( = ( + { className } = { className: "w-8 h-8" } +) => { + return ( + + + + + + + + ) +} + +export default OrderIcon diff --git a/packages/ui/src/components/icons/SearchIcon.tsx b/packages/ui/src/components/icons/SearchIcon.tsx index 8987bc43c..4f33e35a1 100644 --- a/packages/ui/src/components/icons/SearchIcon.tsx +++ b/packages/ui/src/components/icons/SearchIcon.tsx @@ -1,15 +1,20 @@ -import classnames from "classnames" - const SearchIcon: React.FC<{ className: string }> = ({ className }) => { return ( - + ) } diff --git a/packages/ui/src/components/icons/SwitchIcon.tsx b/packages/ui/src/components/icons/SwitchIcon.tsx index 308929f50..a9e070630 100644 --- a/packages/ui/src/components/icons/SwitchIcon.tsx +++ b/packages/ui/src/components/icons/SwitchIcon.tsx @@ -10,15 +10,15 @@ const SwitchIcon: React.FC<{ className?: string }> = ( className={className} xmlns="http://www.w3.org/2000/svg" > - + diff --git a/packages/ui/src/components/icons/ThreeDotsIcon.tsx b/packages/ui/src/components/icons/ThreeDotsIcon.tsx index 83c7b88c1..f28a71b10 100644 --- a/packages/ui/src/components/icons/ThreeDotsIcon.tsx +++ b/packages/ui/src/components/icons/ThreeDotsIcon.tsx @@ -2,15 +2,36 @@ import classnames from "classnames" const ThreeDotsIcon: React.FC = ({ className }) => { return ( - - - + + + ) } diff --git a/packages/ui/src/components/icons/TrashBinIcon.tsx b/packages/ui/src/components/icons/TrashBinIcon.tsx index 3973b1651..d085d98ff 100644 --- a/packages/ui/src/components/icons/TrashBinIcon.tsx +++ b/packages/ui/src/components/icons/TrashBinIcon.tsx @@ -10,9 +10,12 @@ const TrashBinIcon: FunctionComponent<{ className?: string }> = ({ className={classnames("fill-black", className)} xmlns="http://www.w3.org/2000/svg" > - - - + + + ) } diff --git a/packages/ui/src/components/icons/UsbIcon.tsx b/packages/ui/src/components/icons/UsbIcon.tsx index aa6440b98..c0c200e48 100644 --- a/packages/ui/src/components/icons/UsbIcon.tsx +++ b/packages/ui/src/components/icons/UsbIcon.tsx @@ -1,51 +1,50 @@ -import classnames from "classnames" - -const UsbIcon: React.FC<{ className?: string }> = ({ className }) => { +const UsbIcon = () => { return ( ) diff --git a/packages/ui/src/components/icons/WalletIcon.tsx b/packages/ui/src/components/icons/WalletIcon.tsx index 4c9396bb4..e5ed5520d 100644 --- a/packages/ui/src/components/icons/WalletIcon.tsx +++ b/packages/ui/src/components/icons/WalletIcon.tsx @@ -8,41 +8,38 @@ const WalletIcon: React.FC<{ className?: string }> = ({ className }) => { fill="none" xmlns="http://www.w3.org/2000/svg" > - - - - - + + + + + + + - - - - - ) } diff --git a/packages/ui/src/components/info/Info.tsx b/packages/ui/src/components/info/Info.tsx index 5fd8eff17..e90302441 100644 --- a/packages/ui/src/components/info/Info.tsx +++ b/packages/ui/src/components/info/Info.tsx @@ -25,9 +25,7 @@ const Info: FC<{ children: React.ReactNode }> & InfoComponents = ({ const Title = ({ children }: { children: React.ReactNode }) => { return ( - - {children} - + {children} ) } diff --git a/packages/ui/src/components/info/ReleaseNotesInfo.tsx b/packages/ui/src/components/info/ReleaseNotesInfo.tsx index 82ae1e937..d7c269459 100644 --- a/packages/ui/src/components/info/ReleaseNotesInfo.tsx +++ b/packages/ui/src/components/info/ReleaseNotesInfo.tsx @@ -54,7 +54,7 @@ const ReleaseNotesInfo: FC = ({ ) return (
    - + {title} @@ -76,7 +76,7 @@ const ReleaseNotesInfo: FC = ({ href={LINKS.ARTICLES.CHANGELOG} target="_blank" rel="noreferrer" - className="text-primary-300 hover:underline" + className="text-primary-blue-default hover:underline" > Find full release notes here. diff --git a/packages/ui/src/components/info/WelcomeInfo.tsx b/packages/ui/src/components/info/WelcomeInfo.tsx index ba070fac2..1368eded1 100644 --- a/packages/ui/src/components/info/WelcomeInfo.tsx +++ b/packages/ui/src/components/info/WelcomeInfo.tsx @@ -7,7 +7,7 @@ import Info from "./Info" import { useBlankState } from "../../context/background/backgroundHooks" interface WelcomeInfoProps { - onDismiss: () => void + onDismiss: () => Promise } const WelcomeInfo: FC = ({ onDismiss }) => { const { settings } = useBlankState()! @@ -22,6 +22,7 @@ const WelcomeInfo: FC = ({ onDismiss }) => { /> } + submitOnEnter={{ onSubmit: onDismiss }} >
    @@ -55,7 +56,7 @@ const WelcomeInfo: FC = ({ onDismiss }) => { target="_blank" rel="noreferrer" href={LINKS.TELEGRAM} - className="text-decoration-line: underline; text-blue-600 hover:text-blue-800" + className="text-decoration-line: underline; text-primary-blue-default hover:text-primary-blue-hover" > Telegram group {" "} diff --git a/packages/ui/src/components/input/Checkbox.tsx b/packages/ui/src/components/input/Checkbox.tsx index a38cc53fb..3d23c0a32 100644 --- a/packages/ui/src/components/input/Checkbox.tsx +++ b/packages/ui/src/components/input/Checkbox.tsx @@ -34,7 +34,7 @@ const Checkbox = ({ type="checkbox" checked={checked} className={classnames( - " cursor-pointer w-4 h-4 border-1 border-gray-200 rounded-md focus:ring-0", + " cursor-pointer w-4 h-4 border-1 border-primary-grey-hover rounded-md focus:ring-0", className && className )} onChange={() => { diff --git a/packages/ui/src/components/input/DropDownSelector.tsx b/packages/ui/src/components/input/DropDownSelector.tsx index 39b9b8a09..2b48a7792 100644 --- a/packages/ui/src/components/input/DropDownSelector.tsx +++ b/packages/ui/src/components/input/DropDownSelector.tsx @@ -14,6 +14,8 @@ type DropDownSelectorProps = { disabled?: boolean customWidth?: string className?: string + popUpOpenLeft?: boolean + customHeight?: string } /** @@ -28,6 +30,8 @@ type DropDownSelectorProps = { * @param error - If needed, the error to display. * @param disabled - Shows popup on click or not. * @param customWidth - Custom dropdown width + * @param customHeight - Custom dropdown height + * @param popUpOpenLeft - If true, will open the popUp to left. */ const DropDownSelector: FC = ({ display, @@ -39,6 +43,8 @@ const DropDownSelector: FC = ({ disabled, children, customWidth, + customHeight, + popUpOpenLeft, }) => { // State const [active, setActive] = useState(false) @@ -98,11 +104,11 @@ const DropDownSelector: FC = ({ {/* Display */}
    = ({ alt="active-arrow" src={arrowDown} className={classnames( - "w-3 h-2 text-black", + "w-3 h-2 text-primary-black-default", active && "rotate-180" )} /> @@ -127,10 +133,11 @@ const DropDownSelector: FC = ({ {/* Popup */}
    ( -
    +
    {children}
    - {label} + {label}
    ) diff --git a/packages/ui/src/components/input/HorizontalSelect.tsx b/packages/ui/src/components/input/HorizontalSelect.tsx index 02e8237bb..f1dd044e9 100644 --- a/packages/ui/src/components/input/HorizontalSelect.tsx +++ b/packages/ui/src/components/input/HorizontalSelect.tsx @@ -31,8 +31,8 @@ const HorizontalSelect: FunctionComponent<{ !disableStyles ? `flex-1 flex flex-row items-center justify-center p-4 border-b-2 text-sm ${ option === value - ? "border-primary-300 text-primary-300 font-bold" - : "border-transparent text-gray-500 hover:text-primary-300 hover:font-medium" + ? "border-primary-blue-default text-primary-blue-default font-semibold" + : "border-transparent text-primary-grey-dark hover:text-primary-blue-default font-medium" }` : optionClassName ? optionClassName(option) diff --git a/packages/ui/src/components/input/NetworkSelect.tsx b/packages/ui/src/components/input/NetworkSelect.tsx index 32ba54be3..6ad089730 100644 --- a/packages/ui/src/components/input/NetworkSelect.tsx +++ b/packages/ui/src/components/input/NetworkSelect.tsx @@ -1,4 +1,4 @@ -import { FunctionComponent, useRef, useState } from "react" +import { FunctionComponent, useEffect, useMemo, useRef, useState } from "react" import { RiArrowDownSLine, RiArrowUpSLine } from "react-icons/ri" import { BsCheck } from "react-icons/bs" import { useHistory } from "react-router-dom" @@ -20,25 +20,39 @@ const NetworkOption: FunctionComponent<{ disabled?: boolean }> = ({ option, selectedNetwork, handleNetworkChange, disabled = false }) => { const networkColor = getNetworkColor(option) + const [hasImageError, setHasImageError] = useState(false) + return (
  • await handleNetworkChange(option.name)} > - + {!hasImageError && option.iconUrls && option.iconUrls.length > 0 ? ( + network icon { + setHasImageError(true) + }} + /> + ) : ( + + )} + {option.desc} @@ -54,6 +68,8 @@ const NetworkSelect: FunctionComponent<{ }> = ({ className, optionsContainerClassName }) => { const history = useHistory()! const [networkList, setNetworkList] = useState(false) + const [hasImageError, setHasImageError] = useState(false) + const { selectedNetwork, availableNetworks, @@ -64,16 +80,30 @@ const NetworkSelect: FunctionComponent<{ const ref = useRef(null) useOnClickOutside(ref, () => setNetworkList(false)) - const handleNetworkChange = (network: string) => { + const handleNetworkChange = async (network: string) => { setNetworkList(false) changeNetwork(network) } - const getNetworkData = (): { color: string; desc: string } => { - const network = availableNetworks[selectedNetwork.toUpperCase()] - const color = getNetworkColor(network) - return { color: color, desc: network.desc } - } + const networkData = availableNetworks[selectedNetwork.toUpperCase()] + + const networkColor = useMemo(() => { + return getNetworkColor(networkData) + }, [networkData]) + + // Check if the network icon is valid before render. if not show the colored circle + useEffect(() => { + if (networkData.iconUrls && networkData.iconUrls.length > 0) { + const image = new Image() + image.src = networkData.iconUrls[0] + image.onerror = () => { + setHasImageError(true) + } + image.onload = () => { + setHasImageError(false) + } + } + }, [networkData]) return (
    - + {!hasImageError && + networkData.iconUrls && + networkData.iconUrls.length > 0 ? ( + network icon + ) : ( + + )} - {getNetworkData().desc} + {networkData.desc} {networkList ? ( - + ) : ( - + )}
    +
    setShowPassword(false)} - /> + > + +
    {isCapsLock && ( ( {/* STRENGTH */} {strengthBar ? ( - { - if (setPasswordScore) { - setPasswordScore(s) - } - }} - /> +
  • }> + { + if (setPasswordScore) { + setPasswordScore(s) + } + }} + /> + ) : null} {/* ERROR */} diff --git a/packages/ui/src/components/input/SearchInput.tsx b/packages/ui/src/components/input/SearchInput.tsx index 3cbcd08f9..859b955c4 100644 --- a/packages/ui/src/components/input/SearchInput.tsx +++ b/packages/ui/src/components/input/SearchInput.tsx @@ -31,6 +31,8 @@ type SearchInputProps = { minSearchChar?: number defaultValue?: string inputClassName?: string + searchShowSkeleton?: React.Dispatch> + showClearIcon?: boolean } /** @@ -54,6 +56,7 @@ type SearchInputProps = { * @param debounceTime - Set the debouncing time. * @param minSearchChar - Set a minimum char before triggering `onChange`. Note that this has the priority over `onChange` and `debounce`. * @param defaultValue - In AssetSelection we already have a value which was previously looked for. So we paste it as default in case of "add new token" + * @param showClearIcon - It makes input as "search" intut instead of "text" input */ const SearchInput = forwardRef( ( @@ -75,6 +78,8 @@ const SearchInput = forwardRef( register, defaultValue, inputClassName, + searchShowSkeleton, + showClearIcon = false, }: SearchInputProps, ref ) => { @@ -108,7 +113,7 @@ const SearchInput = forwardRef(
    @@ -123,7 +128,7 @@ const SearchInput = forwardRef( ( )} className={classnames( Classes.inputBordered, - "w-full relative z-0 outline-none transition-all delay-100", + "w-full relative z-0 outline-none transition-all delay-100", isFocus ? "pl-2" : "pl-9", inputClassName, isValid @@ -154,11 +159,18 @@ const SearchInput = forwardRef( if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current) - timeoutIdRef.current = setTimeout( - () => onValueChanged(e), - debounceTime - ) + timeoutIdRef.current = setTimeout(() => { + onValueChanged(e) + + if ( + searchShowSkeleton && + e.target.value !== "" + ) + searchShowSkeleton(true) + }, debounceTime) } else { + if (searchShowSkeleton && e.target.value !== "") + searchShowSkeleton(true) onValueChanged(e) } }} @@ -166,7 +178,6 @@ const SearchInput = forwardRef( onFocus={() => setIsFocus(true)} defaultValue={defaultValue ?? ""} /> - & CompoundProps = ({ return null }, [currentValue, children]) return ( -
    +
    {label ? (
    -
    +
    {content}
    diff --git a/packages/ui/src/components/label/GenericTooltip.tsx b/packages/ui/src/components/label/GenericTooltip.tsx index fe23115cd..46791dc01 100644 --- a/packages/ui/src/components/label/GenericTooltip.tsx +++ b/packages/ui/src/components/label/GenericTooltip.tsx @@ -1,5 +1,5 @@ import classnames from "classnames" -import { FunctionComponent, useLayoutEffect, useRef, useState } from "react" +import { FunctionComponent, useLayoutEffect, useRef } from "react" /** * Generic tooltip component. @@ -65,10 +65,10 @@ const GenericTooltip: FunctionComponent<{ ref={ref} className={classnames( "absolute transform inline-block z-40", - "invisible opacity-0 group-hover:visible group-hover:opacity-95", - "pointer-events-none rounded-md bg-white shadow-md", + "invisible opacity-0 group-hover:visible group-hover:opacity-100", + "pointer-events-none rounded-md bg-primary-black-default text-white shadow-md", "transition-all duration-300 ease-out", - "p-1 text-xs", + "p-1 text-xs font-medium", top && `bottom-full mb-1.5`, right && "left-full mr-1.5", bottom && "top-full mt-1.5", diff --git a/packages/ui/src/components/label/InfoTip.tsx b/packages/ui/src/components/label/InfoTip.tsx index c6dc0cdeb..9ecdd55ce 100644 --- a/packages/ui/src/components/label/InfoTip.tsx +++ b/packages/ui/src/components/label/InfoTip.tsx @@ -4,11 +4,6 @@ import classnames from "classnames" import ExclamationCircleIconFull from "../icons/ExclamationCircleIconFull" import CloseIcon from "../icons/CloseIcon" -const infoBgStyle = { - backgroundColor: "#e2f6ff", - color: "#00529B", -} - /** * Info tip component. * @@ -31,17 +26,17 @@ const InfoTip: FunctionComponent<{

    @@ -52,7 +47,7 @@ const InfoTip: FunctionComponent<{

    setIsClosed(true)} > diff --git a/packages/ui/src/components/label/Tooltip.tsx b/packages/ui/src/components/label/Tooltip.tsx index 257790d0f..ccbf572f6 100644 --- a/packages/ui/src/components/label/Tooltip.tsx +++ b/packages/ui/src/components/label/Tooltip.tsx @@ -10,7 +10,7 @@ const Tooltip: FunctionComponent<{
    diff --git "a/packages/ui/src/components/label/\320\241opyToClipboardTooltip.tsx" "b/packages/ui/src/components/label/\320\241opyToClipboardTooltip.tsx" index 221b16ce7..080dcd523 100644 --- "a/packages/ui/src/components/label/\320\241opyToClipboardTooltip.tsx" +++ "b/packages/ui/src/components/label/\320\241opyToClipboardTooltip.tsx" @@ -10,13 +10,12 @@ const CopyTooltip: FunctionComponent<{ }> = ({ copied = false, text }) => (
    -
    {copied ? ( (
    ) diff --git a/packages/ui/src/components/network/DropdownNetworkDisplay.tsx b/packages/ui/src/components/network/DropdownNetworkDisplay.tsx index 24ff0aa33..ee38abe23 100644 --- a/packages/ui/src/components/network/DropdownNetworkDisplay.tsx +++ b/packages/ui/src/components/network/DropdownNetworkDisplay.tsx @@ -24,8 +24,8 @@ const DropdownNetworkDisplay: FunctionComponent<
    diff --git a/packages/ui/src/components/network/NetworkDisplay.tsx b/packages/ui/src/components/network/NetworkDisplay.tsx index 682a1d0a9..be1108149 100644 --- a/packages/ui/src/components/network/NetworkDisplay.tsx +++ b/packages/ui/src/components/network/NetworkDisplay.tsx @@ -19,9 +19,9 @@ const NetworkDisplay: FunctionComponent = ({ return (
    = ({ +const NetworkDropdownDisplay: FC = ({ isLoading, isEmpty, selectedNetwork, diff --git a/packages/ui/src/components/network/NetworkSelector.tsx b/packages/ui/src/components/network/NetworkSelector.tsx index 51b2b9ff4..89cc10ff1 100644 --- a/packages/ui/src/components/network/NetworkSelector.tsx +++ b/packages/ui/src/components/network/NetworkSelector.tsx @@ -122,7 +122,7 @@ export const NetworkSelector: FunctionComponent = ({
    {search && searchResult.length === 0 ? (
    -

    +

    No available networks match with the search.

    diff --git a/packages/ui/src/components/network/NetworkSelectorList.tsx b/packages/ui/src/components/network/NetworkSelectorList.tsx index 88ed4f8fe..95bc61867 100644 --- a/packages/ui/src/components/network/NetworkSelectorList.tsx +++ b/packages/ui/src/components/network/NetworkSelectorList.tsx @@ -20,7 +20,7 @@ const NetworkSelectorList: FC = ({ }) => { return ( <> - + {networks.length} available networks (null) @@ -144,10 +145,10 @@ const NetworkDisplay = ({ return (
    - + {!hasImageError && + networkInfo.iconUrls && + networkInfo.iconUrls.length > 0 ? ( + network icon { + setHasImageError(true) + }} + /> + ) : ( + + )}
    - + {networkInfo.desc}
    {isSelectedNetwork ? (
    ) : (
    setConfirmSwitchNetwork(true)} > @@ -237,7 +253,7 @@ const NetworkDisplay = ({ )}
    diff --git a/packages/ui/src/components/phishing/AntiPhishing.tsx b/packages/ui/src/components/phishing/AntiPhishing.tsx index 67e53b511..f66ffd208 100644 --- a/packages/ui/src/components/phishing/AntiPhishing.tsx +++ b/packages/ui/src/components/phishing/AntiPhishing.tsx @@ -8,8 +8,8 @@ const SIZES = { } const TRANSLATE = { - sm: "-translate-y-20 translate-x-1/4", - normal: "-translate-y-40 translate-x-3/4", + sm: "!-translate-y-20 !translate-x-1/4", + normal: "!-translate-y-40 !translate-x-3/4", } const AntiPhishing: FunctionComponent<{ @@ -27,11 +27,11 @@ const AntiPhishing: FunctionComponent<{ anti-phishing +
    Phishing Protection{" "}
    diff --git a/packages/ui/src/components/popup/PopupHeader.tsx b/packages/ui/src/components/popup/PopupHeader.tsx index e44856a0a..6d853aa6a 100644 --- a/packages/ui/src/components/popup/PopupHeader.tsx +++ b/packages/ui/src/components/popup/PopupHeader.tsx @@ -14,6 +14,7 @@ import CloseIcon from "../icons/CloseIcon" import ArrowIcon from "../icons/ArrowIcon" import Dropdown from "../ui/Dropdown/Dropdown" import { DropdownMenuItem } from "../ui/Dropdown/DropdownMenu" +import useHotKey, { UseHotKeyProps } from "../../util/hooks/useHotKey" import { AiFillInfoCircle } from "react-icons/ai" import Tooltip from "../label/Tooltip" @@ -32,6 +33,7 @@ export interface PopupHeaderProps { children?: React.ReactNode | undefined className?: string goBackState?: object + permissions?: { [action: string]: boolean } //used to validate hotkeys actions } const PopupHeader: FunctionComponent = ({ @@ -49,15 +51,55 @@ const PopupHeader: FunctionComponent = ({ actions, className, goBackState, + permissions, }) => { const history = useOnMountHistory() const lastLocation = useOnMountLastLocation() const network = useSelectedNetwork() - const [fromAction, setFromAction] = useState(false) - const [mounted, setMounted] = useState(false) + const onBackAction = (e: any) => { + if (onBack) return onBack(e) + + //means there is no stack at all and we don't were to go + //as the history hasn't been restored. + //Also, we don't have the lastLocation as the extension may have been closed + //and restored using the localStorage (useLocationRecovery). + //Therefore, we return to home. + if (history.length <= 1) { + return history.replace("/") + } + + if (keepState || goBackState) { + let newState = {} + if (keepState) { + newState = lastLocation?.state + ? (lastLocation?.state as any & { + keepState: true + }) + : {} + } + if (goBackState) { + newState = { + ...newState, + ...goBackState, + } + } + return history.replace({ + pathname: lastLocation?.pathname, + state: newState, + }) + } + fromAction ? history.go(-3) : history.goBack() + } + + const onCloseAction = (e: any) => { + if (onClose) return onClose(e as any) + + history.push(typeof close === "string" ? close : "/home") + } + useEffect(() => { setFromAction(history.location.state?.fromAction) setMounted(true) @@ -66,54 +108,27 @@ const PopupHeader: FunctionComponent = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []) + useHotKey({ + onClose: close && onCloseAction, + onBack: backButton && onBackAction, + permissions: permissions, + } as UseHotKeyProps) + return (
    {backButton && (
    )} - - {title} - + {title && ( + + {title} + + )} {tooltip && (
    )} -
    - {actions && ( - - - {actions.map((action, idx) => { - return ( - - {action} - - ) - })} - - - )} - {networkIndicator && ( - - )} - {close && ( - - )} -
    + {(networkIndicator || actions || close) && ( +
    + {networkIndicator && ( + + )} + {actions && ( + + + {actions.map((action, idx) => { + return ( + + {action} + + ) + })} + + + )} + {close && ( + + )} +
    + )} {children}
    ) diff --git a/packages/ui/src/components/popup/PopupLayout.tsx b/packages/ui/src/components/popup/PopupLayout.tsx index c0649eedc..75613bcb6 100644 --- a/packages/ui/src/components/popup/PopupLayout.tsx +++ b/packages/ui/src/components/popup/PopupLayout.tsx @@ -1,5 +1,4 @@ import { FunctionComponent, useLayoutEffect } from "react" - import { rejectUnconfirmedRequests } from "../../context/commActions" import useBeforeunload from "../../context/hooks/useBeforeUnload" import usePreventWindowResize from "../../context/hooks/usePreventWindowResize" @@ -7,19 +6,32 @@ import { isAutomaticClose } from "../../context/setup" import useSubmitOnEnter, { submitOnEnterProps, } from "../../util/hooks/useSubmitOnEnter" +import { useCheckLocationHotkeys } from "../../util/hotkeys" import PageLayout from "../PageLayout" +import ProviderStatus from "../chain/ProviderStatus" +import HotkeysCollapsedMessage from "../hotkeys/HotkeysCollapsedMessage" const PopupLayout: FunctionComponent<{ header?: React.ReactNode + showProviderStatus?: boolean footer?: React.ReactNode children: React.ReactNode | undefined submitOnEnter?: submitOnEnterProps -}> = ({ header, children, footer, submitOnEnter }) => { + hotkeysPermissions?: { [action: string]: boolean } +}> = ({ + header, + children, + footer, + submitOnEnter, + showProviderStatus, + hotkeysPermissions, +}) => { const { preventResize, cancelPreventResize } = usePreventWindowResize() + const fullHeader = ( <> {header} -
    +
    ) @@ -36,21 +48,39 @@ const PopupLayout: FunctionComponent<{ useSubmitOnEnter(submitOnEnter ?? {}) + const padding = { paddingTop: header ? "69px" : "0" } + + //Lets check if this currentLocation has hotkeys, in case we have something we show it in footer. + const hotkeyByPath = useCheckLocationHotkeys(hotkeysPermissions) return ( -
    +
    {fullHeader}
    -
    {fullHeader}
    -
    +
    + {showProviderStatus && } {children}
    {footer ? ( <> -
    +
    {footer} + {hotkeyByPath && ( + + )} - ) : null} + ) : ( + hotkeyByPath && ( + + ) + )} ) } diff --git a/packages/ui/src/components/qr/QRReader.tsx b/packages/ui/src/components/qr/QRReader.tsx new file mode 100644 index 000000000..8da839495 --- /dev/null +++ b/packages/ui/src/components/qr/QRReader.tsx @@ -0,0 +1,101 @@ +import { FC, useRef, useState } from "react" +import { requestMediaAccess } from "../../context/util/requestMediaAccess" +import classnames from "classnames" +import no_camera from "../../assets/images/icons/no_camera.svg" +import { AnimatedQRScanner, URType } from "@keystonehq/animated-qr" + +export interface URParameter { + type: string + cbor: string +} + +interface Props { + deviceNotReady?: boolean + onRead: (ur: URParameter) => Promise + options?: { + width?: number | string + height?: number | string + blur?: boolean + } + urTypes: URType[] +} + +const QrContainer: FC = ({ + deviceNotReady = false, + options, + onRead, + urTypes, +}) => { + const lastResult = useRef() + const done = useRef(false) + const [isCameraReady, setIsCameraReady] = useState(true) + + requestMediaAccess().then((result) => { + setIsCameraReady(result) + }) + + const handleResult = async (ur: URParameter) => { + const resultText = ur?.cbor + if (!ur || !resultText) return + if (done && done.current) return + + // This callback will keep existing even after + // this component is unmounted + // So ignore it (only in this reference) if result keeps repeating + if (lastResult.current === resultText) { + return + } + + lastResult.current = resultText + if (await onRead(ur)) { + done.current = true + } + } + const handleError = (error: string) => { + console.log("Error") + console.log(error) + } + + return ( + <> + {!deviceNotReady && isCameraReady ? ( + <> +
    + +
    + + ) : !isCameraReady && !deviceNotReady ? ( + <> +
    +
    +
    + icon +
    +
    + We can't find your camera, make sure it's + connected and installed properly. +
    +
    +
    + + ) : ( +
    + )} + + ) +} + +export default QrContainer diff --git a/packages/ui/src/components/qr/TransactionQR.tsx b/packages/ui/src/components/qr/TransactionQR.tsx new file mode 100644 index 000000000..1b7bc597f --- /dev/null +++ b/packages/ui/src/components/qr/TransactionQR.tsx @@ -0,0 +1,39 @@ +import { FC, useState } from "react" +import TransactionShowQR from "./TransactionShowQR" +import TransactionReadQR from "./TransactionReadQR" +import { QRTransactionParams } from "@block-wallet/background/controllers/transactions/utils/types" +import { URParameter } from "./QRReader" + +interface Props { + qrParams?: QRTransactionParams + onBack: () => void + onQRSignatureProvided: (ur: URParameter) => Promise +} + +const TransactionQR: FC = ({ + onBack, + qrParams, + onQRSignatureProvided, +}) => { + const [showQrDone, setShowQRDone] = useState(false) + + return !showQrDone ? ( + { + setShowQRDone(true) + }} + /> + ) : ( + { + setShowQRDone(false) + }} + onCancel={onBack} + onSuccess={onQRSignatureProvided} + /> + ) +} + +export default TransactionQR diff --git a/packages/ui/src/components/qr/TransactionReadQR.tsx b/packages/ui/src/components/qr/TransactionReadQR.tsx new file mode 100644 index 000000000..7e8520598 --- /dev/null +++ b/packages/ui/src/components/qr/TransactionReadQR.tsx @@ -0,0 +1,58 @@ +import { FC } from "react" +import QrContainer, { URParameter } from "./QRReader" +import Divider from "../Divider" +import { ButtonWithLoading } from "../button/ButtonWithLoading" +import { Classes } from "../../styles" +import { URType } from "@keystonehq/animated-qr" + +interface Props { + onBack: () => void + onCancel: () => void + onSuccess: (ur: URParameter) => Promise +} + +const SendSignReadQR: FC = ({ onBack, onCancel, onSuccess }) => { + const onQRRead = async (ur: URParameter): Promise => { + return onSuccess(ur) + } + + return ( +
    +
    +
    +
    + + Please scan your Keystone QR code. The blur effect + does not affect the scanning. + +
    + + +
    + + +
    +
    +
    +
    +
    +
    + ) +} + +export default SendSignReadQR diff --git a/packages/ui/src/components/qr/TransactionShowQR.tsx b/packages/ui/src/components/qr/TransactionShowQR.tsx new file mode 100644 index 000000000..68f0a59bb --- /dev/null +++ b/packages/ui/src/components/qr/TransactionShowQR.tsx @@ -0,0 +1,80 @@ +import { FC, useEffect, useState } from "react" +import { ButtonWithLoading } from "../button/ButtonWithLoading" +import { Classes } from "../../styles" +import QRCode from "qrcode.react" +import Divider from "../Divider" +import Spinner from "../spinner/Spinner" + +interface Props { + QRValues?: string[] + onBack: () => void + onSuccess: () => void +} + +const TransactionShowQR: FC = ({ onBack, onSuccess, QRValues }) => { + const [QRValue, setQRValue] = useState("") + const [index, setIndex] = useState(0) + + useEffect(() => { + const timeoutRef = setTimeout(() => { + if (QRValues && QRValues.length) { + if (index < QRValues.length) { + setQRValue(QRValues[index]) + } + + let newIndex = index + 1 + if (newIndex >= QRValues.length) { + newIndex = 0 + } + setIndex(newIndex) + } + }, 1200) + + return () => { + clearTimeout(timeoutRef) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [QRValue, QRValues]) + + return ( +
    +
    +
    +
    + + Please scan the QR code using your Keystone. + +
    + {QRValue ? ( + + ) : ( +
    + +
    + )} + +
    + + +
    +
    +
    +
    +
    +
    + ) +} + +export default TransactionShowQR diff --git a/packages/ui/src/components/setup/SeedImport.tsx b/packages/ui/src/components/setup/SeedImport.tsx index 8f06745b7..159ec5d0a 100644 --- a/packages/ui/src/components/setup/SeedImport.tsx +++ b/packages/ui/src/components/setup/SeedImport.tsx @@ -163,6 +163,7 @@ const SeedImport: FunctionComponent<{ } else { setIsImportDisabled(true) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [seedPhrase, passwordValues, seedPhraseError, formState.errors.password]) useEffect(() => { @@ -170,11 +171,12 @@ const SeedImport: FunctionComponent<{ if (passwordValues.password && passwordValues.passwordConfirmation) { trigger("passwordConfirmation") } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [passwordValues.password, trigger]) return (
    @@ -207,13 +209,13 @@ const SeedImport: FunctionComponent<{ />
    -
    +
    {Array.from({ length: numberOfWords }, (v, i) => { const wordnN = i + 1 return ( Terms of Use @@ -302,7 +304,7 @@ const SeedImport: FunctionComponent<{ type="submit" className={classnames( Classes.button, - "w-1/2 font-bold border-2 border-primary-300", + "w-1/2 font-semibold border-2 border-primary-blue-default", (isLoading || isImportDisabled) && "opacity-50 pointer-events-none" )} diff --git a/packages/ui/src/components/skeleton/AssetsLoadingSkeleton.tsx b/packages/ui/src/components/skeleton/AssetsLoadingSkeleton.tsx index 51b4bee7f..001be80a2 100644 --- a/packages/ui/src/components/skeleton/AssetsLoadingSkeleton.tsx +++ b/packages/ui/src/components/skeleton/AssetsLoadingSkeleton.tsx @@ -9,9 +9,9 @@ const AssetsLoadingSkeleton = () => {
    {index > 0 ?
    : null} -
    +
    -
    +
    { return ( -
    +
    { + return ( + <> + {[...Array(3)].map((x, index) => ( +
    + {index > 0 ?
    : null} +
    +
    +
    + +
    +
    + + +
    +
    +
    +
    + ))} + + ) +} + +export default SendPageLoadingSkeleton diff --git a/packages/ui/src/components/skeleton/TransactionsLoadingSkeleton.tsx b/packages/ui/src/components/skeleton/TransactionsLoadingSkeleton.tsx index 749288968..7db6049e8 100644 --- a/packages/ui/src/components/skeleton/TransactionsLoadingSkeleton.tsx +++ b/packages/ui/src/components/skeleton/TransactionsLoadingSkeleton.tsx @@ -9,7 +9,7 @@ const TransactionsLoadingSkeleton = () => { {index > 0 ?
    : null}
    -
    +
    = ({ size = "1rem", text = "", iconTextSeparation = "4px", color = "" }) => ( +}> = ({ size = "16px", text = "", iconTextSeparation = "4px", color = "" }) => (
    = ({ - size = "1rem", + size = "16px", text = "", iconTextSeparation = "4px", color = "text-primary-100", @@ -45,7 +45,7 @@ const Spinner: FunctionComponent<{ r="20" fill="none" strokeWidth="2" - className="animate-spinner-dash stroke-primary-300 linecap-round" + className="animate-spinner-dash stroke-primary-blue-default linecap-round" > {text} diff --git a/packages/ui/src/components/swaps/PriceImpactDialog.tsx b/packages/ui/src/components/swaps/PriceImpactDialog.tsx index a75155098..b53ed7936 100644 --- a/packages/ui/src/components/swaps/PriceImpactDialog.tsx +++ b/packages/ui/src/components/swaps/PriceImpactDialog.tsx @@ -1,10 +1,10 @@ -import { BasicToken } from "@block-wallet/background/utils/types/1inch" import { BigNumber } from "@ethersproject/bignumber" import { formatUnits } from "ethers/lib/utils" import { FC } from "react" import { formatRounded } from "../../util/formatRounded" import useCurrencyFromatter from "../../util/hooks/useCurrencyFormatter" import WarningDialog from "../dialog/WarningDialog" +import { BasicToken } from "@block-wallet/background/utils/swaps/1inch" interface HighPriceImpactDialogProps { onClose: () => void @@ -37,7 +37,9 @@ const HighPriceImpactExplained: FC< difference in the values you are about to swap.
    - You pay + + You pay +
    {`${formatToken(fromToken.token, fromToken.amount)} ~= ${format( @@ -47,7 +49,9 @@ const HighPriceImpactExplained: FC< )}`}
    - You get + + You get +
    {`${formatToken( toToken.token, diff --git a/packages/ui/src/components/swaps/RateUpdateDialog.tsx b/packages/ui/src/components/swaps/RateUpdateDialog.tsx index 060ea5f3c..d7f54e6ef 100644 --- a/packages/ui/src/components/swaps/RateUpdateDialog.tsx +++ b/packages/ui/src/components/swaps/RateUpdateDialog.tsx @@ -1,6 +1,6 @@ import Dialog from "../dialog/Dialog" import Divider from "../Divider" -import React, { FC, useEffect, useState } from "react" +import { FC, memo, useEffect, useRef, useState } from "react" import { Classes } from "../../styles" import { BigNumber } from "@ethersproject/bignumber" import { formatUnits } from "@ethersproject/units" @@ -23,7 +23,7 @@ const RateUpdateDialog: FC = ({ threshold, }) => { const history = useOnMountHistory() - const lastRateRef = React.useRef() + const lastRateRef = useRef() const [isOpen, setIsOpen] = useState(false) useEffect(() => { @@ -60,13 +60,13 @@ const RateUpdateDialog: FC = ({ return (
    -

    Rate has changed!

    +

    Rate has changed!

    Rate expired. Please acknowledge the receiving amount update.

    -

    OLD AMOUNT

    -

    +

    OLD AMOUNT

    +

    {`${formatNumberLength( formatUnits( lastRateRef.current || BigNumber.from(0), @@ -75,8 +75,10 @@ const RateUpdateDialog: FC = ({ 10 )} ${assetName}`}

    -

    NEW AMOUNT

    -

    +

    + NEW AMOUNT +

    +

    {`${formatNumberLength( formatUnits(rate || BigNumber.from(0), assetDecimals), 10 @@ -88,7 +90,7 @@ const RateUpdateDialog: FC = ({

    history.push("/home")} /> = ({ ) } -export default React.memo(RateUpdateDialog) +export default memo(RateUpdateDialog) diff --git a/packages/ui/src/components/swaps/RefreshLabel.tsx b/packages/ui/src/components/swaps/RefreshLabel.tsx index 94929e474..dd12e2d20 100644 --- a/packages/ui/src/components/swaps/RefreshLabel.tsx +++ b/packages/ui/src/components/swaps/RefreshLabel.tsx @@ -1,4 +1,3 @@ -import React from "react" import classnames from "classnames" import Icon, { IconName } from "../ui/Icon" @@ -16,7 +15,9 @@ const RefreshLabel: React.FC = ({ value, className }) => { )} > - Refreshes in {value} + + Refreshes in {value} +
    ) } diff --git a/packages/ui/src/components/token/AddTokenListView.tsx b/packages/ui/src/components/token/AddTokenListView.tsx index 5b9920dfd..dc4457360 100644 --- a/packages/ui/src/components/token/AddTokenListView.tsx +++ b/packages/ui/src/components/token/AddTokenListView.tsx @@ -10,6 +10,7 @@ import searchIcon from "../../assets/images/icons/search.svg" import { TokenResponse } from "../../routes/settings/AddTokensPage" import { useEffect, useState } from "react" import useSubmitOnEnter from "../../util/hooks/useSubmitOnEnter" +import { Size } from "react-virtualized-auto-sizer" export interface addTokenListView { results?: TokenResponse[] @@ -81,9 +82,9 @@ const AddTokenListView = ({ alt="search" className="w-7 h-7 absolute z-10" /> -
    +
    - + Add the tokens that you've acquired using BlockWallet.
    Enter an address for adding a custom token. @@ -97,7 +98,7 @@ const AddTokenListView = ({ >
    @@ -123,7 +124,7 @@ const AddTokenListView = ({ })}
    @@ -131,7 +132,7 @@ const AddTokenListView = ({
    {results.length < 1 && selected.length <= 0 ? ( -
    +
    No match
    ) : ( @@ -142,7 +143,7 @@ const AddTokenListView = ({ className="w-full" > - {({ width, height }) => ( + {({ width, height }: Size) => (
    -
    +
    Custom Token
    { diff --git a/packages/ui/src/components/token/TokenDisplay.tsx b/packages/ui/src/components/token/TokenDisplay.tsx index dc554f7e3..d5a2c8f94 100644 --- a/packages/ui/src/components/token/TokenDisplay.tsx +++ b/packages/ui/src/components/token/TokenDisplay.tsx @@ -48,19 +48,28 @@ const TokenDisplay: FunctionComponent = ({ className={classnames( "relative flex items-center p-3 my-0.5 rounded-md transition-all duration-300 active:scale-95", clickable && "cursor-pointer", - selected && "bg-primary-200", - hoverable && "hover:bg-primary-100" + selected && "bg-primary-grey-hover", + hoverable && "hover:bg-primary-grey-default" )} onClick={() => (clickable ? setSelected(!selected) : null)} > - +
    - + {formatName(data.name, 22)} {balance && ( {formatRounded(formatUnits(balance, data.decimals), 6)} @@ -68,7 +77,7 @@ const TokenDisplay: FunctionComponent = ({ )}

    - {data.symbol} + {data.symbol.toUpperCase()}

    void + findTokenCard: (address: string) => { + token: TokenWithBalance + index: number + } + onSuccessfulDrop: () => void +} + +/** + * TokenDisplay: + * Creates a display element to show token information. + * Can or cannot be clicked to show a selected style. + * Can show a selected style. + * + * @param data - Object containing token to display's informations. + * @param hoverable - Determines if the element shows a hover style. + * @param balance - Contains the asset balance in case it exists. e.g. if it is a New Asset there is no balance + * @param moveTokenCard - Changes the token order to new position + * @param findTokenCard - Finds a token by address + * @param onSuccessfulDrop - If drop was successful we saved the new order in the state + */ +const TokenDisplayDragDrop: FunctionComponent = ({ + data, + hoverable, + balance, + moveTokenCard, + findTokenCard, + onSuccessfulDrop, +}) => { + const { isHovering: isHoveringIcons } = useIsHovering() + const [dropAnimation, setDropAnimation] = useState(false) + const dropRef = useRef(null) + const dragRef = useRef(null) + const formatter = useCurrencyFromatter() + + const originalIndex = findTokenCard(data.address).index + + const [{ isDragging }, drag, preview] = useDrag( + () => ({ + type: "token", + item: { tokenInfo: data, originalIndex: originalIndex }, + // This is used to inject isDragging variable into the component + collect: (monitor: DragSourceMonitor) => ({ + isDragging: monitor.isDragging(), + }), + // triggered when the dragging of this component is stopped + end: (item: TokenCardProps, monitor: DragSourceMonitor) => { + const didDrop = monitor.didDrop() + // if the drop was not successful in a dropzone, we return the card to its original position + if (!didDrop) { + moveTokenCard(item.tokenInfo.address, item.originalIndex) + } + }, + }), + [data, originalIndex, moveTokenCard] + ) + + const [, drop] = useDrop( + () => ({ + accept: "token", + // Called when a dragged item is hovered over this component. + hover(item: TokenCardProps) { + if (item.tokenInfo.address !== data.address) { + const { index: overIndex } = findTokenCard(data.address) + // move the dragged item to the hovered item's position + moveTokenCard(item.tokenInfo.address, overIndex) + } + }, + collect(monitor) { + if (monitor.didDrop()) { + const dropResult = monitor.getDropResult() as TokenCardProps + // If the drop was successful, we trigger the animation and update background networks state + if (dropResult.tokenInfo.address === data.address) { + setDropAnimation(true) + onSuccessfulDrop() + } + } + }, + drop(item: TokenCardProps) { + return item + }, + }), + [findTokenCard, moveTokenCard] + ) + + preview(drop(dropRef)) + drag(dragRef) + + useEffect(() => { + if (dropAnimation) { + setTimeout(() => { + setDropAnimation(false) + }, 800) + } + }, [dropAnimation]) + + const opacity = isDragging ? 0 : 1 + + const cardHoverStyle = !dropAnimation && !isHoveringIcons && hoverable + + // Render + return ( +
    +
    +
    +
    + + +
    + + {`${formatRounded( + formatUnits(balance || "0", data.decimals), + 4 + )} + ${data.symbol}`} + + + {formatter.format( + balance || BigNumber.from(0), + data.symbol, + data.decimals ?? 18, + isNativeTokenAddress(data.address) + )} + +
    +
    +
    +
    +
    + ) +} + +export default TokenDisplayDragDrop diff --git a/packages/ui/src/components/token/TokenDropdownDisplay.tsx b/packages/ui/src/components/token/TokenDropdownDisplay.tsx new file mode 100644 index 000000000..ccdeb7db9 --- /dev/null +++ b/packages/ui/src/components/token/TokenDropdownDisplay.tsx @@ -0,0 +1,55 @@ +import { FC } from "react" +import TokenLogo from "../token/TokenLogo" +import { Token } from "@block-wallet/background/controllers/erc-20/Token" +import classnames from "classnames" +import Spinner from "../spinner/Spinner" + +interface TokenDropdownDisplayProps { + selectedToken?: Token + displayIcon?: boolean + isLoading?: boolean + loadingText?: string +} + +const TokenDropdownDisplay: FC = ({ + selectedToken, + displayIcon, + loadingText = "", + isLoading, +}) => { + if (isLoading) { + return ( +
    + + {loadingText} +
    + ) + } + + return selectedToken ? ( +
    + {displayIcon && ( + + )} + + {selectedToken.symbol} + +
    + ) : ( +
    +
    Select token
    +
    + ) +} + +export default TokenDropdownDisplay diff --git a/packages/ui/src/components/token/TokenList.tsx b/packages/ui/src/components/token/TokenList.tsx new file mode 100644 index 000000000..22b17a508 --- /dev/null +++ b/packages/ui/src/components/token/TokenList.tsx @@ -0,0 +1,56 @@ +import { FC } from "react" +import TokenDisplay from "../token/TokenDisplay" +import { Token } from "@block-wallet/background/controllers/erc-20/Token" + +const TokenList: FC<{ + setActive?: () => void + onTokenClick: (token: Token, setActive?: () => void) => void + selectedAddress?: string + tokens: Token[] + searchValue: string | null + register: any +}> = ({ + onTokenClick, + setActive, + selectedAddress, + tokens, + searchValue, + register, +}) => { + return ( +
    + + {tokens.map((token) => { + return ( +
    onTokenClick(token, setActive)} + > + +
    + ) + })} + {searchValue && tokens.length === 0 && ( +
    +

    + The token couldn’t be found. +

    +
    + )} +
    + ) +} + +export default TokenList diff --git a/packages/ui/src/components/token/TokenLogo.tsx b/packages/ui/src/components/token/TokenLogo.tsx index 262f9bc49..9a0f95a69 100644 --- a/packages/ui/src/components/token/TokenLogo.tsx +++ b/packages/ui/src/components/token/TokenLogo.tsx @@ -1,28 +1,50 @@ import unknownTokenIcon from "../../assets/images/unknown_token.svg" -import { FunctionComponent } from "react" -import { classnames } from "../../styles" +import { FunctionComponent, useEffect, useState } from "react" +import { Classes, classnames } from "../../styles" interface TokenLogoProps { name: string logo?: string - bigLogo?: boolean + logoSize?: "small" | "medium" | "big" className?: string + filled?: boolean } const TokenLogo: FunctionComponent = ({ logo = unknownTokenIcon, name, - bigLogo = false, + logoSize = "medium", className, + filled, }) => { + const [divClassName, setDivClassName] = useState(Classes.roundedIcon) + + useEffect(() => { + switch (logoSize) { + case "small": + setDivClassName( + filled + ? Classes.smallRoundedIcon + : Classes.smallRoundedFilledIcon + ) + break + case "medium": + setDivClassName( + filled + ? Classes.mediumRoundedIcon + : Classes.mediumRoundedFilledIcon + ) + break + case "big": + setDivClassName( + filled ? Classes.roundedIcon : Classes.roundedFilledIcon + ) + break + } + }, [logoSize, filled]) + return ( -
    +
    { - const { run } = useAsyncInvoke() const history = useOnMountHistory() const [message, setMessage] = useState("") const [selected, setSelected] = useState([]) // Handlers - const onSubmit = async () => { + const onSubmit = useCallback(() => { try { // Valid form data if (selected.length > 0) { @@ -48,11 +47,11 @@ const SearchedTokenView = ({ // Prevent manual form submission setMessage("Please select a token first.") } - } catch (event) { + } catch (error) { // Invalid form data - console.log(event) + log.error(error) } - } + }, [history, searchedValue, selected]) // Functions const addToken = (token: TokenResponse) => { @@ -86,15 +85,15 @@ const SearchedTokenView = ({ useEffect(() => { if (submitForm) { - run(onSubmit()) + onSubmit() } - }, [submitForm]) + }, [submitForm, onSubmit]) useEffect(() => { if (setSubmitEnabled) { setSubmitEnabled(selected.length > 0) } - }, [selected]) + }, [selected, setSubmitEnabled]) return (
    @@ -115,9 +114,9 @@ const SearchedTokenView = ({ alt="search" className="w-7 h-7 absolute z-10" /> -
    +
    - + Add the tokens that you've acquired using BlockWallet.
    Enter an address for adding a custom token. @@ -126,7 +125,7 @@ const SearchedTokenView = ({ ) : (
    @@ -152,7 +151,7 @@ const SearchedTokenView = ({ })}
    @@ -160,7 +159,7 @@ const SearchedTokenView = ({
    {results.length < 1 && selected.length <= 0 ? ( -
    +
    No match
    ) : ( @@ -171,7 +170,7 @@ const SearchedTokenView = ({ className="w-full" > - {({ width, height }) => ( + {({ width, height }: Size) => ( void + selectedToken?: Token + displayIcon?: boolean + error?: string + register?: any + topMargin?: number + bottomMargin?: number + popupMargin?: number + dropdownWidth?: string + dropdownHeight?: string + popUpOpenLeft?: boolean + loadingText?: string +} +const SEARCH_LIMIT = 20 + +export const TokenSelection: FC = ({ + defaultTokenList, + onTokenChange, + selectedToken, + displayIcon = false, + error, + topMargin, + bottomMargin, + popupMargin, + dropdownWidth, + dropdownHeight, + register, + popUpOpenLeft, + loadingText, +}) => { + const [searchResult, setSearchResult] = useState([]) + const [search, setSearch] = useState(null) + const [tokenList, setTokenList] = useState([]) + + useEffect(() => { + if (!tokenList.length) { + setTokenList(defaultTokenList) + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [defaultTokenList]) + + const searchTokenInList = (searchedValue: string) => { + return tokenList.filter((token) => { + return ( + token.name.toLowerCase().includes(searchedValue) || + token.symbol.toLowerCase().includes(searchedValue) || + token.address?.toLowerCase() === searchedValue + ) + }) + } + + useEffect(() => { + const searchAll = () => { + if (!search) { + setSearchResult(tokenList) + return + } + + const input = search.toLowerCase() + let searchRes = searchTokenInList(input) + + searchRes = searchRes.filter((t) => !!t.symbol) + + // Order search + let searchResult: Token[] = [] + + // Limit the results + const searchLength = + searchRes.length > SEARCH_LIMIT + ? SEARCH_LIMIT + : searchRes.length + + for (let index = 0; index < searchLength; index++) { + searchResult.push(searchRes[index] as Token) + continue + } + + setSearchResult(searchResult) + } + searchAll() + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [search, tokenList]) + + const onSearchInputChange = (event: ChangeEvent) => { + let value: string | null = event.target.value + + value = value.replace(/\W/g, "") + + if (!value) { + value = null + } + + setSearch(value) + } + + const onTokenClick = async ( + token: Token, + setActive?: Dispatch> + ) => { + onTokenChange(token) + setActive && setActive(false) + } + + return ( + + } + error={error} + topMargin={topMargin || 0} + bottomMargin={bottomMargin || 0} + popupMargin={popupMargin || 16} + customWidth={dropdownWidth} + popUpOpenLeft={popUpOpenLeft} + disabled={!tokenList || tokenList.length === 0} + customHeight={dropdownHeight} + > +
    + +
    + +
    + ) +} diff --git a/packages/ui/src/components/token/TokenSummary.tsx b/packages/ui/src/components/token/TokenSummary.tsx index b3c3f18a2..23ad6f497 100644 --- a/packages/ui/src/components/token/TokenSummary.tsx +++ b/packages/ui/src/components/token/TokenSummary.tsx @@ -10,7 +10,11 @@ interface TokenSummaryMembers { children: React.ReactNode className?: string }> - ExchangeRateBalance: FC<{ title?: string; children: React.ReactNode }> + ExchangeRateBalance: FC<{ + title?: string + className?: string + children: React.ReactNode + }> TokenName: FC<{ title?: string; children: React.ReactNode }> Actions: FC<{ children: React.ReactNode; className?: string | undefined }> } @@ -24,10 +28,9 @@ const TokenSummary: FC<{ return (
    {children}
    @@ -43,8 +46,7 @@ const Balances = ({ }) => { const state = useBlankState()! - const isLoading = - state.isNetworkChanging || state.isRatesChangingAfterNetworkChange + const isLoading = state.isNetworkChanging return ( <> @@ -70,7 +72,7 @@ const TokenBalance: FC<{ }> = ({ children, title, className }) => { return ( {children} @@ -80,10 +82,14 @@ const TokenBalance: FC<{ const ExchangeRateBalance: FC<{ title?: string + className?: string children: React.ReactNode -}> = ({ children, title }) => { +}> = ({ children, title, className }) => { return ( - + {children} ) @@ -94,7 +100,7 @@ const TokenName: FC<{ children: React.ReactNode }> = ({ children, title }) => { return ( - + {children} ) diff --git a/packages/ui/src/components/transactions/AdvancedSettings.tsx b/packages/ui/src/components/transactions/AdvancedSettings.tsx index 6a212d0b0..d9257887e 100644 --- a/packages/ui/src/components/transactions/AdvancedSettings.tsx +++ b/packages/ui/src/components/transactions/AdvancedSettings.tsx @@ -349,15 +349,15 @@ export const AdvancedSettings: FunctionComponent = ({ {buttonDisplay ? ( setIsOpen(true)} - className="w-full py-3" + className="!w-full py-3 h-12 space-x-2 p-4" > - {label} + {label} ) : (
    setIsOpen(true)} > {label} @@ -371,7 +371,7 @@ export const AdvancedSettings: FunctionComponent = ({ onClick={() => { setIsOpen(false) }} - className="cursor-pointer p-2 ml-auto -mr-2 text-gray-900 transition duration-300 rounded-full hover:bg-primary-100 hover:text-primary-300" + className="cursor-pointer p-2 ml-auto -mr-2 text-gray-900 transition duration-300 rounded-full hover:bg-primary-grey-default hover:text-primary-blue-default" >
    @@ -380,11 +380,11 @@ export const AdvancedSettings: FunctionComponent = ({ className="flex flex-col w-full px-3" ref={clickOutsideRef} > -

    {label}

    +

    {label}

    {display.slippage && ( -
    -

    +

    +

    Slippage percentage (%)

    = ({ onSlippageChange(e) }} className={classnames( - "w-full", + "w-full mt-2", Classes.inputBordered, errors.slippage ? "border-red-400 focus:border-red-400" @@ -416,7 +416,7 @@ export const AdvancedSettings: FunctionComponent = ({ {display.nonce && (
    -

    +

    Custom Nonce

    = ({ onNonceChange(e) }} className={classnames( - "w-full", + "w-full mt-2", Classes.inputBordered, errors.nonce && "border-red-400 focus:border-red-400" @@ -452,7 +452,7 @@ export const AdvancedSettings: FunctionComponent = ({
    = ({

    resetSettings()} - className="text-xs text-blue-500 hover:text-blue-800 cursor-pointer w-min" + className="text-xs text-primary-blue-default hover:text-primary-blue-hover cursor-pointer w-min" > Reset

    diff --git a/packages/ui/src/components/transactions/AllowanceInput.tsx b/packages/ui/src/components/transactions/AllowanceInput.tsx index b02b84995..143adde7c 100644 --- a/packages/ui/src/components/transactions/AllowanceInput.tsx +++ b/packages/ui/src/components/transactions/AllowanceInput.tsx @@ -10,6 +10,7 @@ import { Classes } from "../../styles" import ErrorMessage from "../error/ErrorMessage" import Dialog from "../dialog/Dialog" import CloseIcon from "../icons/CloseIcon" +import { formatRoundedUp } from "../../util/formatRounded" const UNLIMITED_ALLOWANCE = MaxUint256 @@ -139,7 +140,7 @@ const AllowanceInput = ({ currentAllowance?: BigNumber }) => { const [allowanceAmount, setAllowanceAmount] = useState( - formatUnits(defaultValue, tokenDecimals) + formatRoundedUp(formatUnits(defaultValue, tokenDecimals)) ) const [error, setError] = useState("") @@ -230,9 +231,11 @@ const AllowanceInput = ({ setUsingRevoke(isRevoke) setUsingUnlimited(isUnlimited) - setAllowanceAmount(formatUnits(defaultValue, tokenDecimals)) + setAllowanceAmount( + formatRoundedUp(formatUnits(defaultValue, tokenDecimals)) + ) // Needed in dependency array to update the input value when the defaultValue changes if there is multiple allowance approvals in the queue - }, [defaultValue]) + }, [defaultValue, tokenDecimals]) return (
    @@ -240,13 +243,13 @@ const AllowanceInput = ({
    setShowOptionsInfo(true)} />
    @@ -254,9 +257,9 @@ const AllowanceInput = ({
    @@ -286,7 +289,7 @@ const AllowanceInput = ({ ref={inputRef} className={classnames( Classes.blueSectionInput, - "mb-0 text-sm", + "mb-0 text-sm w-[130px]", usingUnlimited && "hidden" )} placeholder={`0 ${tokenName}`} @@ -341,8 +344,8 @@ const AllowanceInput = ({ className={classnames( "w-16 text-center float-right rounded-md cursor-pointer border p-1", usingRevoke - ? "bg-primary-300 border-primary-300 text-white hover:bg-blue-600 hover:border-blue-600" - : "bg-blue-200 border-blue-200 hover:bg-blue-300 hover:border-blue-300" + ? "bg-gray-500 border-gray-500 text-white hover:bg-gray-400 hover:border-gray-400" + : "bg-gray-300 border-gray-300 hover:bg-gray-400 hover:border-gray-400" )} title={`Revoke value`} onClick={() => { @@ -358,8 +361,8 @@ const AllowanceInput = ({ className={classnames( "w-16 text-center float-right rounded-md cursor-pointer border p-1", usingUnlimited - ? "bg-primary-300 border-primary-300 text-white hover:bg-blue-600 hover:border-blue-600" - : "bg-blue-200 border-blue-200 hover:bg-blue-300 hover:border-blue-300" + ? "bg-gray-500 border-gray-500 text-white hover:bg-gray-400 hover:border-gray-400" + : "bg-gray-300 border-gray-300 hover:bg-gray-400 hover:border-gray-400" )} title={`Unlimited value`} onClick={() => { @@ -391,18 +394,18 @@ const AllowanceInput = ({
    setShowOptionsInfo(false)} - className=" cursor-pointer p-2 ml-auto -mr-2 text-gray-900 transition duration-300 rounded-full hover:bg-primary-100 hover:text-primary-300" + className=" cursor-pointer p-2 ml-auto -mr-2 text-gray-900 transition duration-300 rounded-full hover:bg-primary-grey-default hover:text-primary-blue-default" >
    -

    +

    Allowance Options

    -
    +

    Allowances let DApps automate transactions for you. These are your options. @@ -412,10 +415,10 @@ const AllowanceInput = ({ className="flex flex-col space-y-1" key={index} > -

    +

    {item.title}

    -

    +

    {item.description}

    diff --git a/packages/ui/src/components/transactions/BridgeNotFoundQuoteDetails.tsx b/packages/ui/src/components/transactions/BridgeNotFoundQuoteDetails.tsx index c16af307c..77edfbb45 100644 --- a/packages/ui/src/components/transactions/BridgeNotFoundQuoteDetails.tsx +++ b/packages/ui/src/components/transactions/BridgeNotFoundQuoteDetails.tsx @@ -1,4 +1,4 @@ -import { FunctionComponent, useState } from "react" +import { FunctionComponent } from "react" import Dialog from "../dialog/Dialog" import CloseIcon from "../icons/CloseIcon" import Divider from "../Divider" @@ -28,33 +28,37 @@ export const BridgeNotFoundQuoteDetails: FunctionComponent< > = ({ open, onClose, details }) => { let errorsByTool = new Map() - details.errors.map((error) => { - const err = { - code: error.code, - type: error.tool, - message: error.message, - fromToken: error.action.fromToken.symbol, - toToken: error.action.toToken.symbol, - } - if (errorsByTool.has(error.tool)) { - errorsByTool.get(error.tool)?.push(err) - } else { - errorsByTool.set(error.tool, [err]) - } + details.errors.failed.forEach((error) => { + Object.values(error.subpaths).forEach((subpaths) => { + subpaths.forEach((subpath) => { + const err = { + code: subpath.code, + type: subpath.tool, + message: subpath.message, + fromToken: subpath.action.fromToken.symbol, + toToken: subpath.action.toToken.symbol, + } + if (errorsByTool.has(subpath.tool)) { + errorsByTool.get(subpath.tool)?.push(err) + } else { + errorsByTool.set(subpath.tool, [err]) + } + }) + }) }) return (
    -

    +

    Unable to generate a quote

    @@ -63,7 +67,7 @@ export const BridgeNotFoundQuoteDetails: FunctionComponent< style={{ width: "calc(100% + 1.5rem)" }} >
    - Summary + Summary
    {details.message}
    {errorsByTool && errorsByTool.size > 0 && ( @@ -73,7 +77,7 @@ export const BridgeNotFoundQuoteDetails: FunctionComponent<
    - + Errors by tool
    @@ -111,7 +115,7 @@ export const BridgeNotFoundQuoteDetails: FunctionComponent< href={LINKS.ARTICLES.BRIDGES} target="_blank" rel="noopener noreferrer" - className="flex flex-row items-center space-x-2 text-xs font-bold text-primary-300" + className="flex flex-row items-center space-x-2 text-xs font-semibold text-primary-blue-default" > Read about bridges Download report { className={classnames( "text-base font-semibold cursor-pointer capitalize", selectedOption.label === option.label && - "text-primary-300" + "text-primary-blue-default" )} > {option.label} @@ -122,7 +125,7 @@ const GasSelectorBasic = (props: GasComponentProps) => { "text-xs", selectedOption.label === option.label && - "text-primary-300" + "text-primary-blue-default" )} > {option.totalNativeCurrencyCostRange} @@ -133,7 +136,7 @@ const GasSelectorBasic = (props: GasComponentProps) => { "text-sm", selectedOption.label === option.label - ? "text-primary-300" + ? "text-primary-blue-default" : "hidden" )} /> @@ -142,10 +145,10 @@ const GasSelectorBasic = (props: GasComponentProps) => {
    {option.totalETHCostRange} @@ -343,7 +346,7 @@ const GasSelectorAdvanced = (props: GasComponentProps) => {
    -
    -
    -
    - + Last base fee: {formatUnits(baseFeePerGas!, "gwei")} GWEI
    -
    +
    -

    +

    Details are not available

    @@ -141,7 +141,7 @@ export const TransactionDetails: FunctionComponent<
                             
    -                            
    +                            
                                      (
                                     
                                         
    - {`\u00A0\u00A0\u00A0\u00A0${arg.type}`} + {`\u00A0\u00A0\u00A0\u00A0${arg.type}`} {arg.name ? ` ${arg.name}` : ""} {i !== signature.args.length - 1 ? "," : ""}
    diff --git a/packages/ui/src/components/transactions/TransactionDetailsBasic.tsx b/packages/ui/src/components/transactions/TransactionDetailsBasic.tsx index fc78f0aa1..c5a630d01 100644 --- a/packages/ui/src/components/transactions/TransactionDetailsBasic.tsx +++ b/packages/ui/src/components/transactions/TransactionDetailsBasic.tsx @@ -10,7 +10,7 @@ import arrowRight from "../../assets/images/icons/arrow_right_black.svg" import CopyTooltip from "../label/СopyToClipboardTooltip" import { formatHash, - formatHashLastChars, + formatHashFirstLastChars, formatName, } from "../../util/formatAccount" import { useSortedAccounts } from "../../context/hooks/useSortedAccounts" @@ -111,7 +111,7 @@ export const TransactionDetailsBasic: FunctionComponent< label: "Spender", value: spenderData ? formatName(spenderData.name, 18) - : `Spender ${formatHashLastChars(spenderAddress)}`, + : `Spender ${formatHashFirstLastChars(spenderAddress)}`, link: generateExplorerLink( state.availableNetworks, state.selectedNetwork, @@ -411,7 +411,7 @@ export const TransactionDetailsBasic: FunctionComponent<
    {fromName ? formatName(fromName, 12) @@ -448,7 +448,7 @@ export const TransactionDetailsBasic: FunctionComponent<
    {transaction.transactionCategory !== TransactionCategories.CONTRACT_DEPLOYMENT @@ -483,7 +483,7 @@ export const TransactionDetailsBasic: FunctionComponent< )} target="_blank" rel="noopener noreferrer" - className="flex flex-row items-center space-x-2 text-sm font-bold text-primary-300" + className="flex flex-row items-center space-x-2 text-sm font-semibold text-primary-blue-default" > View on {explorerName} = ({ item }) => { {capitalize(item.label)} {item.expandable ? ( - + {item.value ?? "N/A"} ) : ( @@ -41,7 +41,7 @@ const TransactionDetailItem: FC<{ item: DetailedItem }> = ({ item }) => { className={classnames( "text-sm", isNativeValue && - "whitespace-nowrap text-ellipsis overflow-hidden text-gray-600 allow-select", + "whitespace-nowrap text-ellipsis overflow-hidden text-primary-grey-dark allow-select", item.expandable ? "w-11/12 mt-1" : "" )} title={ @@ -57,7 +57,7 @@ const TransactionDetailItem: FC<{ item: DetailedItem }> = ({ item }) => { href={item.link} target="_blank" rel="noreferrer" - className="text-blue-500 flex items-center" + className="text-primary-blue-default flex items-center" > {item.value && isNativeValue ? item.decimals diff --git a/packages/ui/src/components/transactions/TransactionDetailsList.tsx b/packages/ui/src/components/transactions/TransactionDetailsList.tsx index 52ed142fe..317340187 100644 --- a/packages/ui/src/components/transactions/TransactionDetailsList.tsx +++ b/packages/ui/src/components/transactions/TransactionDetailsList.tsx @@ -1,4 +1,4 @@ -import { FC, ReactElement, ReactNode } from "react" +import { FC, ReactNode } from "react" import Divider from "../Divider" import TransactionDetailItem from "./TransactionDetailsItem" @@ -13,10 +13,10 @@ export interface DetailedItem { link?: string // Not to be used with expandable } -type TransactionDetailItem = DetailedItem | undefined +type TransactionDetailItemType = DetailedItem | undefined interface TransactionDetailsListProps { - details: TransactionDetailItem[] + details: TransactionDetailItemType[] } const TransactionDetailsList: FC = ({ diff --git a/packages/ui/src/components/transactions/TransactionItem.tsx b/packages/ui/src/components/transactions/TransactionItem.tsx index a314e065b..9379f4d35 100644 --- a/packages/ui/src/components/transactions/TransactionItem.tsx +++ b/packages/ui/src/components/transactions/TransactionItem.tsx @@ -1,4 +1,4 @@ -import { CSSProperties, useState } from "react" +import { CSSProperties } from "react" import { FaExchangeAlt } from "react-icons/fa" import { FiUpload } from "react-icons/fi" import { RiCopperCoinFill } from "react-icons/ri" @@ -8,7 +8,6 @@ import { ImSpinner } from "react-icons/im" import { BigNumber } from "@ethersproject/bignumber" import classNames from "classnames" import { Classes, classnames } from "../../styles" -import { AssetIcon } from "./../AssetsList" import Tooltip from "../../components/label/Tooltip" import eth from "../../assets/images/icons/ETH.svg" import blankLogo from "../../assets/images/logo.svg" @@ -37,13 +36,12 @@ import { RichedTransactionMeta } from "../../util/transactionUtils" import Dots from "../loading/LoadingDots" import useCurrencyFromatter from "../../util/hooks/useCurrencyFormatter" import useGetBridgeTransactionsData from "../../util/hooks/useGetBridgeTransactionsData" -import BridgeDetails from "../bridge/BridgeDetails" import { BRIDGE_PENDING_STATUS, getBridgePendingMessage, } from "../../util/bridgeUtils" -import TransactionDetails from "./TransactionDetails" import { formatUnits } from "ethers/lib/utils" +import TokenLogo from "../token/TokenLogo" const TRANSACTION_STATIC_MESSAGES = { [TransactionCategories.BLANK_DEPOSIT]: "Privacy Pool Deposit", @@ -149,7 +147,7 @@ const getTransactionItemStyles = ( return { formattedLabel, typeCss, amountCss } } -const transactionIcons = { +const transactionIcons: Record = { [TransactionCategories.BLANK_DEPOSIT]: blank, [TransactionCategories.BLANK_WITHDRAWAL]: ( BlockWallet @@ -159,27 +157,25 @@ const transactionIcons = { [TransactionCategories.CONTRACT_DEPLOYMENT]: , [TransactionCategories.CONTRACT_INTERACTION]: , [TransactionCategories.TOKEN_METHOD_APPROVE]: ( - + ), [TransactionCategories.TOKEN_METHOD_TRANSFER]: ( - + ), [TransactionCategories.TOKEN_METHOD_INCOMING_TRANSFER]: ( - + ), [TransactionCategories.TOKEN_METHOD_TRANSFER_FROM]: ( - - ), - [TransactionCategories.EXCHANGE]: , - [TransactionCategories.BRIDGE]: , - [TransactionCategories.INCOMING_BRIDGE]: ( - + ), + [TransactionCategories.EXCHANGE]: , + [TransactionCategories.BRIDGE]: , + [TransactionCategories.INCOMING_BRIDGE]: , [TransactionCategories.INCOMING_BRIDGE_REFUND]: ( - + ), [TransactionCategories.INCOMING_BRIDGE_PLACEHOLDER]: ( - + ), } @@ -192,8 +188,11 @@ const failedStatuses = [ const PendingSpinner: React.FC<{ size?: string -}> = ({ size = "1rem" }) => ( - +}> = ({ size = "16px" }) => ( + ) const TransactionIcon: React.FC<{ @@ -209,11 +208,11 @@ const TransactionIcon: React.FC<{
    {transactionStatus !== TransactionStatus.SUBMITTED ? ( transactionIcon ? ( - ) : category ? (
    @@ -236,7 +235,7 @@ const getTransactionTime = ( ) => { const [{ color, label }, extraInfo] = (() => { const displayTime = { - color: "text-gray-600", + color: "text-primary-grey-dark", label: getDisplayTime(new Date(time)), } // If the transaction that we wanted to cancel is sent @@ -259,7 +258,7 @@ const getTransactionTime = ( return [ displayTime, { - color: "text-blue-600", + color: "text-primary-blue-default", label: "Sped up", }, ] @@ -276,7 +275,7 @@ const getTransactionTime = ( failedStatuses.includes(status) && metaType === MetaType.REGULAR_CANCELLING ) - return [{ color: "text-blue-600", label: "Cancelled" }] + return [{ color: "text-primary-blue-default", label: "Cancelled" }] // /!\ Really specific case /!\ // the DROPPED + SPEEDING_UP is supposed to mean that the speed up work // However, the transaction is supposed to be filtered an not shown. @@ -302,23 +301,25 @@ const getTransactionTime = ( ) return [{ color: "text-red-600", label: "Cancelled" }] // If we're here, we're waiting to see if the transaction will be cancelled - else if (metaType === MetaType.REGULAR_CANCELLING) - return [{ color: "text-gray-600", label: "Cancelling..." }] + else if (metaType === MetaType.REGULAR_CANCELLING && !isQueued) + return [{ color: "text-primary-grey-dark", label: "Cancelling..." }] // If we're here, we're waiting to see if the transaction will be sped up - else if (metaType === MetaType.REGULAR_SPEEDING_UP) - return [{ color: "text-gray-600", label: "Speeding up..." }] + else if (metaType === MetaType.REGULAR_SPEEDING_UP && !isQueued) + return [ + { color: "text-primary-grey-dark", label: "Speeding up..." }, + ] else if (status === TransactionStatus.SUBMITTED) return !isQueued - ? [{ color: "text-gray-600", label: "Pending..." }] + ? [{ color: "text-primary-grey-dark", label: "Pending..." }] : [{ color: "text-yellow-600", label: "Queued" }] else return [displayTime] })() return ( <> - {label} + {label} {extraInfo && ( - + {extraInfo.label} )} @@ -380,7 +381,7 @@ const getTransactionTimeOrStatus = ( ) => { if (forceDrop) { return ( - + {capitalize(TransactionStatus.DROPPED.toLowerCase())} ) @@ -388,7 +389,7 @@ const getTransactionTimeOrStatus = ( if (failedStatuses.includes(status) && metaType === MetaType.REGULAR) { return ( - + {capitalize( status === TransactionStatus.CANCELLED ? "failed" @@ -403,7 +404,7 @@ const getTransactionTimeOrStatus = ( bridgeParams.role !== "RECEIVING" ) { return ( - + Failed bridge: Refunded ) @@ -420,7 +421,9 @@ const getTransactionTimeOrStatus = ( const TransactionItem: React.FC<{ transaction: RichedTransactionMeta index: number -}> = ({ index, transaction }) => { + itemHeight: number + onClick: () => void +}> = ({ index, transaction, onClick, itemHeight }) => { const { transactionParams: { value, hash }, methodSignature, @@ -438,6 +441,7 @@ const TransactionItem: React.FC<{ transactionCategory, advancedData, } = transaction + const bridgeTransactionsData = useGetBridgeTransactionsData(transaction) const history: any = useOnMountHistory() @@ -446,14 +450,12 @@ const TransactionItem: React.FC<{ const { nativeCurrency: networkNativeCurrency, defaultNetworkLogo } = useSelectedNetwork() - const [hasDetails, setHasDetails] = useState(false) - const txHash = hash let transfer = transferType ?? { amount: value ? value : BigNumber.from("0"), currency: networkNativeCurrency.symbol, decimals: networkNativeCurrency.decimals, - logo: defaultNetworkLogo, + logo: networkNativeCurrency.logo ?? defaultNetworkLogo, } const isBlankWithdraw: boolean = @@ -535,39 +537,28 @@ const TransactionItem: React.FC<{ TransactionCategories.INCOMING_BRIDGE_PLACEHOLDER && !isBlankWithdraw - const OperationDetails = - transactionCategory && - [ - TransactionCategories.BRIDGE, - TransactionCategories.INCOMING_BRIDGE_REFUND, - TransactionCategories.INCOMING_BRIDGE, - ].includes(transactionCategory) - ? BridgeDetails - : TransactionDetails - return ( <> - setHasDetails(false)} - /> -
    { if (txHash && transaction.transactionParams.from) { - setHasDetails(true) + onClick() } }} > - {/* Type */}
    {formattedLabel} @@ -602,13 +593,13 @@ const TransactionItem: React.FC<{ metaType === MetaType.REGULAR && (
    {transferCurrencyAmount} @@ -771,7 +762,7 @@ const TransactionItem: React.FC<{ bridgeParams && BRIDGE_PENDING_STATUS.includes(bridgeParams!.status! || "") ? (
    - + <> { getBridgePendingMessage( diff --git a/packages/ui/src/components/transactions/TransactionsList.tsx b/packages/ui/src/components/transactions/TransactionsList.tsx index 4f5607f88..e95256101 100644 --- a/packages/ui/src/components/transactions/TransactionsList.tsx +++ b/packages/ui/src/components/transactions/TransactionsList.tsx @@ -1,73 +1,150 @@ -import { Fragment, useRef, useState } from "react" -import useDOMElementObserver from "../../util/hooks/useDOMElementObserver" +import { useState, useRef, useEffect } from "react" +import AutoSizer from "react-virtualized/dist/commonjs/AutoSizer" +import List from "react-virtualized/dist/commonjs/List" import { RichedTransactionMeta } from "../../util/transactionUtils" -import dotLoading from "../../assets/images/icons/dot_loading.svg" -import { classnames } from "../../styles" import TransactionItem from "./TransactionItem" -import { useBlankState } from "../../context/background/backgroundHooks" +import { + MetaType, + TransactionCategories, + TransactionStatus, +} from "../../context/commTypes" +import BridgeDetails from "../bridge/BridgeDetails" +import TransactionDetails from "./TransactionDetails" +import { BRIDGE_PENDING_STATUS } from "../../util/bridgeUtils" +import { TransactionMeta } from "@block-wallet/background/controllers/transactions/utils/types" import TransactionsLoadingSkeleton from "../skeleton/TransactionsLoadingSkeleton" -const getInitialCount = (transactions: RichedTransactionMeta[]) => - transactions.length > 10 ? 10 : transactions.length +//Default tx height +const DEFAULT_TX_HEIGHT_IN_PX = 76 + +//Tx with captions (Sped up/Bridge information) +const TX_HEIHG_WITH_CAPTION = 95 + +//Speed-up/Cancel buttons +const TX_HEIGHT_WITH_BUTTONS = 110 + +const pendingSpeedingCancellingMetaTypes = [ + MetaType.REGULAR_SPEEDING_UP, + MetaType.REGULAR_CANCELLING, +] + +const confirmedSpedCancelMetaTypes = [MetaType.SPEED_UP, MetaType.CANCEL] + +const getItemHeightInPx = (tx: TransactionMeta) => { + if (tx.id) { + if ( + tx.status === TransactionStatus.SUBMITTED && + !pendingSpeedingCancellingMetaTypes.includes(tx.metaType) + ) { + return TX_HEIGHT_WITH_BUTTONS + } else if (tx.transactionCategory === TransactionCategories.BRIDGE) { + return BRIDGE_PENDING_STATUS.includes(tx.bridgeParams!.status!) + ? TX_HEIHG_WITH_CAPTION + : DEFAULT_TX_HEIGHT_IN_PX + } else if (confirmedSpedCancelMetaTypes.includes(tx.metaType)) { + return TX_HEIHG_WITH_CAPTION + } + } + return DEFAULT_TX_HEIGHT_IN_PX +} + +interface watchDetailsType { + transaction: TransactionMeta +} const TransactionsList: React.FC<{ transactions: RichedTransactionMeta[] -}> = ({ transactions }) => { - const state = useBlankState()! + isNetworkChanging: boolean +}> = ({ transactions, isNetworkChanging }) => { + const [watchDetails, setWatchDetails] = useState< + watchDetailsType | undefined + >() + const ref = useRef() - const isLoading = - state.isNetworkChanging || state.isRatesChangingAfterNetworkChange + useEffect(() => { + // react-virtualized does not recompute row height when the underlying transaction data changes. + // thats why we force a height recompution here and adjust tx height based on its state. + ref.current && ref.current.recomputeRowHeights(0) + }, [transactions]) - const [transactionCount, setTransactionCount] = useState(() => - getInitialCount(transactions) - ) - const [isLoadingMoreTransactions, setIsLoadingMoreTransactions] = - useState(false) - const loaderRef = useRef(null) + const OperationDetails = watchDetails + ? watchDetails.transaction.transactionCategory + ? [ + TransactionCategories.BRIDGE, + TransactionCategories.INCOMING_BRIDGE_REFUND, + TransactionCategories.INCOMING_BRIDGE, + ].includes(watchDetails.transaction.transactionCategory) + ? BridgeDetails + : TransactionDetails + : undefined + : undefined - useDOMElementObserver( - loaderRef, - async () => { - const countToLoad = transactions.length - transactionCount - if (countToLoad === 0) return - setIsLoadingMoreTransactions(true) - await new Promise((resolve) => setTimeout(resolve, 300)) - setTransactionCount( - transactionCount + (countToLoad > 10 ? 10 : countToLoad) - ) - setIsLoadingMoreTransactions(false) - }, - [transactionCount, transactions] - ) + if (isNetworkChanging) { + return ( +
    + +
    + ) + } return ( <> - {isLoading ? ( - - ) : ( - <> - {transactions.slice(0, transactionCount).map((t, i) => ( - - {i > 0 ?
    : null} - -
    - ))} - Loader - + {OperationDetails && watchDetails?.transaction && ( + setWatchDetails(undefined)} + /> )} + + {({ height }) => ( + { + return getItemHeightInPx(transactions[index]) + }} // height in px + className="hide-scroll" + rowRenderer={({ + style, + key, + index, + }: { + style: any + key: string + index: number + }) => ( +
    + {index > 0 ? ( +
    +
    +
    + ) : null} + + setWatchDetails({ + transaction: transactions[index], + }) + } + itemHeight={getItemHeightInPx( + transactions[index] + )} + transaction={transactions[index]} + index={index} + /> +
    + )} + >
    + )} +
    ) } diff --git a/packages/ui/src/components/ui/Alert.tsx b/packages/ui/src/components/ui/Alert.tsx index 2ff9405b5..f8294329c 100644 --- a/packages/ui/src/components/ui/Alert.tsx +++ b/packages/ui/src/components/ui/Alert.tsx @@ -1,5 +1,5 @@ import classnames from "classnames" -import { MouseEventHandler, PropsWithChildren } from "react" +import { PropsWithChildren } from "react" interface AlertProps { type: "error" | "warn" | "info" className?: string @@ -32,7 +32,7 @@ const Alert: React.FC> = ({ onClick={onClick} className={classnames( classes.background, - "opacity-90 rounded-md w-full p-4 flex space-x-2 items-center font-bold justify-center", + "opacity-90 rounded-md w-full p-4 flex space-x-2 items-center font-semibold justify-center", className )} > diff --git a/packages/ui/src/components/ui/Badge.tsx b/packages/ui/src/components/ui/Badge.tsx index eabe0b1ff..e182d8af6 100644 --- a/packages/ui/src/components/ui/Badge.tsx +++ b/packages/ui/src/components/ui/Badge.tsx @@ -15,9 +15,9 @@ const Badge: React.FC> = ( const offset = offsets[position || "top-right"] return (
    - {children} + {children}
    ) } diff --git a/packages/ui/src/components/ui/CodeBlock.tsx b/packages/ui/src/components/ui/CodeBlock.tsx index c1344b644..b89a100f0 100644 --- a/packages/ui/src/components/ui/CodeBlock.tsx +++ b/packages/ui/src/components/ui/CodeBlock.tsx @@ -11,7 +11,7 @@ const CodeBlock = ({ return (
    diff --git a/packages/ui/src/components/ui/Dropdown/DropdownButton.tsx b/packages/ui/src/components/ui/Dropdown/DropdownButton.tsx
    index 7bb358832..037597e43 100644
    --- a/packages/ui/src/components/ui/Dropdown/DropdownButton.tsx
    +++ b/packages/ui/src/components/ui/Dropdown/DropdownButton.tsx
    @@ -14,6 +14,8 @@ export interface DropdownButtonBaseProps {
     
     interface DropdownIconButtonProps extends DropdownButtonBaseProps {
         iconName: IconName
    +    buttonClassName?: string
    +    iconSize?: "sm" | "md" | "lg" | "xl" | "xxl" | undefined
     }
     
     export const DropdownButton: FC> &
    @@ -49,8 +51,8 @@ export const DropdownIconButton: React.FC = ({
             
    @@ -70,15 +72,21 @@ export const DropdownOutlinedIconButton: React.FC = ({ onClick, iconName, isShowingMenu, + buttonClassName, + iconSize, }) => { return ( ) diff --git a/packages/ui/src/components/ui/Dropdown/DropdownMenu.tsx b/packages/ui/src/components/ui/Dropdown/DropdownMenu.tsx index 85ea93835..37e3a1149 100644 --- a/packages/ui/src/components/ui/Dropdown/DropdownMenu.tsx +++ b/packages/ui/src/components/ui/Dropdown/DropdownMenu.tsx @@ -65,7 +65,7 @@ export const DropdownMenuItem: React.FC< className={classnames( "flex flex-row justify-between items-center w-full cursor-pointer hover:bg-gray-100", className || "", - selected && "text-primary-300" + selected && "text-primary-blue-default" )} > {children} diff --git a/packages/ui/src/components/ui/EmptyState.tsx b/packages/ui/src/components/ui/EmptyState.tsx index 70b2440e0..17d37324d 100644 --- a/packages/ui/src/components/ui/EmptyState.tsx +++ b/packages/ui/src/components/ui/EmptyState.tsx @@ -16,7 +16,7 @@ const EmptyState: React.FC> = ({ return (
    diff --git a/packages/ui/src/components/ui/Icon.tsx b/packages/ui/src/components/ui/Icon.tsx index 9fc87b94b..4bd42921e 100644 --- a/packages/ui/src/components/ui/Icon.tsx +++ b/packages/ui/src/components/ui/Icon.tsx @@ -17,6 +17,8 @@ import ClockIcon from "../icons/ClockIcon" import SwitchIcon from "../icons/SwitchIcon" import RefetchIcon from "../icons/RefetchIcon" import DisabledBridgeIcon from "../icons/DisabledBridgeIcon" +import FilterIcon from "../icons/FilterIcon" +import OrderIcon from "../icons/OrderIcon" export enum IconName { PENCIL = "PENCIL", @@ -37,6 +39,8 @@ export enum IconName { SWITCH = "SWITCH", REFETCH = "REFETCH", DISABLED_BRIDGE = "DISABLED_BRIDGE", + FILTER = "FILTER", + ORDER = "ORDER", } type ProfileType = "default" | "selected" | "danger" | "disabled" @@ -67,6 +71,8 @@ const ICONS = { [IconName.SWITCH]: SwitchIcon, [IconName.REFETCH]: RefetchIcon, [IconName.DISABLED_BRIDGE]: DisabledBridgeIcon, + [IconName.FILTER]: FilterIcon, + [IconName.ORDER]: OrderIcon, } const STROKED_ICONS = [IconName.RIGHT_CHEVRON] diff --git a/packages/ui/src/components/ui/OutlinedButton.tsx b/packages/ui/src/components/ui/OutlinedButton.tsx index b1ea416b6..5a2e2be49 100644 --- a/packages/ui/src/components/ui/OutlinedButton.tsx +++ b/packages/ui/src/components/ui/OutlinedButton.tsx @@ -4,23 +4,26 @@ interface OutlinedButtonProps { onClick?: (e: any) => void className?: string disabled?: boolean + title?: string } const OutlinedButton: FC> = ({ onClick, className = "", children, disabled = false, + title, }) => { return ( diff --git a/packages/ui/src/components/ui/Pagination/PagesList.tsx b/packages/ui/src/components/ui/Pagination/PagesList.tsx index 24a8c2e8c..e56cbae99 100644 --- a/packages/ui/src/components/ui/Pagination/PagesList.tsx +++ b/packages/ui/src/components/ui/Pagination/PagesList.tsx @@ -14,8 +14,8 @@ const PageButton: React.FC< "text-sm p-3", selected ? "text-gray-800 font-semibold cursor-default" - : "text-gray-500 cursor-pointer hover:text-gray-800 ", - disabled && "text-gray-500 !cursor-not-allowed" + : "text-primary-grey-dark cursor-pointer hover:text-gray-800 ", + disabled && "text-primary-grey-dark !cursor-not-allowed" )} onClick={!disabled ? onClick : void 0} > @@ -50,7 +50,7 @@ const StickyFirstPage = ({ return ( <> {button} - ... + ... ) } diff --git a/packages/ui/src/components/ui/Pagination/PaginationControls.tsx b/packages/ui/src/components/ui/Pagination/PaginationControls.tsx index e49f6cbc7..2d01c6542 100644 --- a/packages/ui/src/components/ui/Pagination/PaginationControls.tsx +++ b/packages/ui/src/components/ui/Pagination/PaginationControls.tsx @@ -12,8 +12,8 @@ const PageControlButton: React.FC< return (
    - + WITH ASSET: @@ -243,6 +243,7 @@ const WatchAsset: FunctionComponent = ({ timeout={DAPP_FEEDBACK_WINDOW_TIMEOUT} clickOutsideToClose={false} hideButton + showCloseButton /> = ({ {isUpdate ? UpdateAssetLayout() : null}
    -
    +
    icon
    - + Balance {balance === UNKNOWN_BALANCE ? (
    - + Unknown = ({ >
    ) : ( - + {balance} {token.symbol} )}
    - + {isUpdate ? "IN " : "TO "} ACCOUNT:
    @@ -307,7 +310,7 @@ const WatchAsset: FunctionComponent = ({ fill={getAccountColor(accountData.address)} />
    - + {formatName(accountData.name, 15)} {" ("} {formatNumberLength( @@ -319,7 +322,7 @@ const WatchAsset: FunctionComponent = ({ )} {` ${nativeToken.token.symbol})`} - + {formatHash(accountData.address)}
    @@ -328,19 +331,21 @@ const WatchAsset: FunctionComponent = ({
    - Decimals - {token.decimals} + Decimals + + {token.decimals} +
    {token!.image && !isBase64Image ? (
    - Image URL - + Image URL + {token!.image} - + Attention!{" "} Your IP address will be exposed to this diff --git a/packages/ui/src/routes/hardware-wallet/AccountsPage.tsx b/packages/ui/src/routes/hardware-wallet/AccountsPage.tsx index 283ef1acc..e53cf140f 100644 --- a/packages/ui/src/routes/hardware-wallet/AccountsPage.tsx +++ b/packages/ui/src/routes/hardware-wallet/AccountsPage.tsx @@ -1,15 +1,7 @@ -import { - useCallback, - useEffect, - useMemo, - useReducer, - useRef, - useState, -} from "react" +import { useCallback, useEffect, useMemo, useReducer, useState } from "react" import LoadingOverlay from "../../components/loading/LoadingOverlay" import { - getAccountBalance, getHardwareWalletAccounts, importHardwareWalletAccounts, getHardwareWalletHDPath, @@ -26,33 +18,19 @@ import { Classes } from "../../styles" import { ButtonWithLoading } from "../../components/button/ButtonWithLoading" import HardwareWalletSetupLayout from "./SetupLayout" import Select from "../../components/input/Select" -import { getAccountColor } from "../../util/getAccountColor" -import { formatHash } from "../../util/formatAccount" -import { ViewOnExplorerButton } from "../../components/button/ViewOnExplorerButtons" -import { formatUnits } from "@ethersproject/units" -import { useTokensList } from "../../context/hooks/useTokensList" -import { formatRounded } from "../../util/formatRounded" // Assets & icons -import AccountIcon from "../../components/icons/AccountIcon" -import EyeRevealIcon from "../../components/icons/EyeRevealIcon" import { mergeReducer } from "../../util/reducerUtils" import { useBlankState } from "../../context/background/backgroundHooks" import { BIP44_PATH, Devices, HDPaths } from "../../context/commTypes" import Spinner from "../../components/spinner/Spinner" import log from "loglevel" -import { useOnClickOutside } from "../../util/useOnClickOutside" -import FullScreenDialog from "../../components/dialog/FullScreenDialog" -import CloseIcon from "../../components/icons/CloseIcon" -import Divider from "../../components/Divider" import useAsyncInvoke, { Status } from "../../util/hooks/useAsyncInvoke" import PaginationControls from "../../components/ui/Pagination/PaginationControls" import HardwareDeviceNotLinkedDialog from "../../components/dialog/HardwareDeviceNotLinkedDialog" import { BigNumber } from "@ethersproject/bignumber" -import Icon, { IconName } from "../../components/ui/Icon" -import { AiFillInfoCircle } from "react-icons/ai" -import Tooltip from "../../components/label/Tooltip" -import { LINKS } from "../../util/constants" +import { AccountsPageAdvancedSettings } from "../../components/hardwareWallet/AdvancedSettings" +import { HardwareWalletAccount } from "../../components/hardwareWallet/HardwareWalletAccount" interface State { gettingAccounts: boolean @@ -77,7 +55,9 @@ const initialState: State = { const HardwareWalletAccountsPage = () => { const history = useOnMountHistory()! + const [enabledPagination, setEnabledPagination] = useState(true) const vendor = history.location.state.vendor as Devices + const isKeystoneConnected = history.location.state.isKeystoneConnected const { run, data: hdPath, @@ -116,6 +96,16 @@ const HardwareWalletAccountsPage = () => { setAccountBalances(accountsBalances) } + //Will check if this Keystone can Only synchronize 10 accounts (Ledger Live) + const checkKeystoneAccounts = useCallback(async () => { + setState({ gettingAccounts: true }) + try { + await getHardwareWalletAccounts(vendor, 2, 10) + } catch (e) { + setEnabledPagination(false) + } + }, [vendor]) + const getAccounts = useCallback(async () => { setState({ gettingAccounts: true }) try { @@ -136,9 +126,10 @@ const HardwareWalletAccountsPage = () => { useEffect(() => { if (hdPath) { + if (vendor === Devices.KEYSTONE) checkKeystoneAccounts() getAccounts() } - }, [getAccounts, hdPath]) + }, [checkKeystoneAccounts, getAccounts, hdPath, vendor]) const toggleAccount = (account: DeviceAccountInfo) => { const selected = state.selectedAccounts.some( @@ -213,7 +204,12 @@ const HardwareWalletAccountsPage = () => { disabled={isImportingAccounts} onClick={() => history.push({ - pathname: "/hardware-wallet/connect", + pathname: + vendor === Devices.KEYSTONE + ? isKeystoneConnected + ? "/hardware-wallet" + : "/hardware-wallet/keystone-connect" + : "/hardware-wallet/connect", state: { vendor }, }) } @@ -239,7 +235,7 @@ const HardwareWalletAccountsPage = () => { isOpen={state.deviceNotReady} /> {(isImportingAccounts || isLoadingHDPath) && } -
    +
    {state.deviceAccounts.length > 0 && !state.gettingAccounts ? ( @@ -269,278 +265,82 @@ const HardwareWalletAccountsPage = () => { )}
    -
    -
    - Show: - + 5 + 8 + 10 + +
    + - 5 - 8 - 10 - + stickyFirstPage + currentPage={state.currentPage} + onChangePage={(page) => + setState({ currentPage: page }) + } + pages={6} + />
    - + + setState({ currentPage: page }) + } + pages={2} + className="!w-full" + showArrows={false} + /> +
    + )} + {vendor !== Devices.KEYSTONE && ( + p.default)?.path || + BIP44_PATH + } disabled={state.gettingAccounts} - stickyFirstPage - currentPage={state.currentPage} - onChangePage={(page) => setState({ currentPage: page })} - pages={6} + vendor={vendor} + setHDPath={updateHDPath} /> -
    - p.default)?.path || - BIP44_PATH - } - disabled={state.gettingAccounts} - vendor={vendor} - setHDPath={updateHDPath} - /> -
    - - ) -} - -const HardwareWalletAccount = ({ - account, - accountsBalances, - selected = false, - disabled = false, - onChange, - onBalanceFetched, -}: { - account: DeviceAccountInfo - accountsBalances: { [address in string]: BigNumber } - selected: boolean - disabled: boolean - onChange: () => void - onBalanceFetched: (address: string, balance: BigNumber) => void -}) => { - const { nativeToken } = useTokensList() - const [isLoading, setIsLoading] = useState(false) - const [balance, setBalance] = useState( - account.address in accountsBalances - ? formatRounded( - formatUnits( - accountsBalances[account.address] || "0", - nativeToken.token.decimals - ), - 5 - ) + ` ${nativeToken.token.symbol}` - : "*******" - ) - - const fetchBalance = async () => { - try { - setIsLoading(true) - const balanceFetched = await getAccountBalance(account.address) - onBalanceFetched(account.address, balanceFetched) - setBalance( - formatRounded( - formatUnits( - balanceFetched || "0", - nativeToken.token.decimals - ), - 5 - ) + ` ${nativeToken.token.symbol}` - ) - } catch (error) { - setBalance("") - } finally { - setIsLoading(false) - } - } - return ( - - ) -} - -const AdvancedSettings = ({ - currentHDPath, - vendor, - disabled, - setHDPath, -}: { - currentHDPath: string - vendor: Devices - disabled?: boolean - setHDPath: (hdPath: string) => void -}) => { - const [openModal, setOpenModal] = useState(false) - const hdPaths = HDPaths[vendor] - const [selectedHDPath, setSelectedHDPath] = useState(currentHDPath) - - useEffect(() => { - setSelectedHDPath(currentHDPath) - }, [currentHDPath]) - - const ref = useRef(null) - useOnClickOutside(ref, () => { - setOpenModal(false) - }) - return ( - <> -
    !disabled && setOpenModal(true)} - className={classnames( - "w-full pl-2 pt-4 bg-white rounded-md cursor-pointer underline-offset-1 flex items-center justify-between", - disabled - ? "text-gray-200 !cursor-not-allowed" - : "hover:underline" + {vendor === Devices.KEYSTONE && isKeystoneConnected && ( + <> +
    + history.push({ + pathname: "/hardware-wallet/remove-device", + state: { isFromAccountsPage: true }, + }) + } + className={classnames( + "w-full px-40 !mt-6 !-mb-5 bg-white rounded-md cursor-pointer underline-offset-1", + "flex hover:underline" + )} + > + + Remove this device + +
    + )} - > - - Advanced Settings - -
    - -
    - - -
    - -
    setOpenModal(false)} - className=" cursor-pointer p-2 text-gray-900 transition duration-300 rounded-full hover:bg-primary-100 hover:text-primary-300" - > - -
    -
    - -
    - - If you don't see the accounts you're expecting, try - switching the HD path. - -
    - - -
    -
    - -
    -
    -
    - - -
    -
    - - + ) } diff --git a/packages/ui/src/routes/hardware-wallet/ConnectDeviceStepsLayout.tsx b/packages/ui/src/routes/hardware-wallet/ConnectDeviceStepsLayout.tsx index 9de91c489..1bcb0065f 100644 --- a/packages/ui/src/routes/hardware-wallet/ConnectDeviceStepsLayout.tsx +++ b/packages/ui/src/routes/hardware-wallet/ConnectDeviceStepsLayout.tsx @@ -16,6 +16,7 @@ interface ConnectDeviceProps { isLoading: boolean title: string subtitle: string + stepFontSize?: "sm" | "md" | "lg" | undefined } const ConnectDeviceStepsLayout: React.FC = ({ @@ -25,6 +26,7 @@ const ConnectDeviceStepsLayout: React.FC = ({ isLoading, title, subtitle, + stepFontSize, }) => { const history = useHistory() return ( @@ -57,12 +59,17 @@ const ConnectDeviceStepsLayout: React.FC = ({
    {steps.map((step, index) => (
    - + {step.info && (
    diff --git a/packages/ui/src/routes/hardware-wallet/KeystoneConnectionPage.tsx b/packages/ui/src/routes/hardware-wallet/KeystoneConnectionPage.tsx new file mode 100644 index 000000000..98789d01b --- /dev/null +++ b/packages/ui/src/routes/hardware-wallet/KeystoneConnectionPage.tsx @@ -0,0 +1,79 @@ +import { useState } from "react" +//Context +import { Devices } from "../../context/commTypes" +import { useOnMountHistory } from "../../context/hooks/useOnMount" +//Components +import QrContainer, { URParameter } from "../../components/qr/QRReader" +//Layout +import HardwareWalletSetupLayout from "./SetupLayout" +import ConnectDeviceStepsLayout from "./ConnectDeviceStepsLayout" +//Utils +import useHardwareWalletConnect from "../../util/hooks/useHardwareWalletConnect" +import { DEVICE_CONNECTION_STEPS } from "../../util/connectionStepUtils" +import classNames from "classnames" +import { Classes } from "../../styles" +import { URType } from "@keystonehq/animated-qr" + +const KeystoneConnectionPage = () => { + const vendor = Devices.KEYSTONE + const history = useOnMountHistory() + const { connect } = useHardwareWalletConnect(true) + const [deviceNotReady, setDeviceNotReady] = useState(false) + const deviceSteps = DEVICE_CONNECTION_STEPS[vendor] + const [showConnectSteps, setShowConnectSteps] = useState(true) + + const onQRRead = async (ur: URParameter) => { + const resultOk = await connect(vendor, ur) + if (resultOk) { + setDeviceNotReady(resultOk) + history.push({ + pathname: "/hardware-wallet/accounts", + state: { vendor }, + }) + } + return resultOk + } + + return showConnectSteps ? ( + setShowConnectSteps(false)} + steps={deviceSteps} + stepFontSize="md" + /> + ) : ( + +
    + +
    + + } + childrenClass={"items-center w-3/5 my-4"} + buttonClass={"w-full flex space-x-5"} + > + +
    + ) +} + +export default KeystoneConnectionPage diff --git a/packages/ui/src/routes/hardware-wallet/RemoveDevice.tsx b/packages/ui/src/routes/hardware-wallet/RemoveDevice.tsx index b8614e74f..7179c3389 100644 --- a/packages/ui/src/routes/hardware-wallet/RemoveDevice.tsx +++ b/packages/ui/src/routes/hardware-wallet/RemoveDevice.tsx @@ -1,14 +1,15 @@ -import { useState } from "react" +import { Dispatch, FC, SetStateAction, useState } from "react" import classnames from "classnames" import { useOnMountHistory } from "../../context/hooks/useOnMount" -import { AccountType, Devices } from "../../context/commTypes" +import { Devices } from "../../context/commTypes" import Divider from "../../components/Divider" // Assets & icons -import ledger from "../../assets/images/icons/ledger.svg" -import trezor from "../../assets/images/icons/trezor.svg" +import ledgerImg from "../../assets/images/icons/ledger.svg" +import trezorImg from "../../assets/images/icons/trezor.svg" +import keystoneImg from "../../assets/images/keystone.png" import { ButtonWithLoading } from "../../components/button/ButtonWithLoading" import { Classes } from "../../styles" @@ -16,12 +17,76 @@ import HardwareWalletSetupLayout from "./SetupLayout" import { useSortedAccounts } from "../../context/hooks/useSortedAccounts" import { AccountInfo } from "@block-wallet/background/controllers/AccountTrackerController" import { removeHardwareWallet } from "../../context/commActions" +import { getAccountTypeFromDevice } from "../../util/hardwareDevice" +import { capitalize } from "../../util/capitalize" + +type DeviceButtonProps = { + device: Devices + onClick: Dispatch> + className: string +} + +const DETAILS = { + [Devices.LEDGER]: { + imgSrc: ledgerImg, + name: capitalize(Devices.LEDGER), + }, + [Devices.TREZOR]: { + imgSrc: trezorImg, + name: capitalize(Devices.TREZOR), + }, + [Devices.KEYSTONE]: { + imgSrc: keystoneImg, + name: capitalize(Devices.KEYSTONE), + }, +} + +const DeviceButton: FC = ({ + device, + onClick, + className, +}) => { + const details = DETAILS[device] + const accounts = useSortedAccounts({ includeHiddenAccounts: true }) + const getImportedDevices = (device: Devices): number => { + const accountType = getAccountTypeFromDevice(device) + return accounts.filter( + (account: AccountInfo) => account.accountType === accountType + ).length + } + const importedDevices = getImportedDevices(device) + const isDeviceImported = importedDevices > 0 + return ( + + ) +} const HardwareWalletRemoveDevicePage = () => { const history = useOnMountHistory() - const accounts = useSortedAccounts({ includeHiddenAccounts: true }) const [selectedVendor, setSelectedVendor] = useState() const [isLoading, setIsLoading] = useState(false) + const isFromAccountsPage = + history.location.state && history.location.state.isFromAccountsPage + ? history.location.state.isFromAccountsPage + : false const next = async () => { if (!selectedVendor) { @@ -33,10 +98,16 @@ const HardwareWalletRemoveDevicePage = () => { const result = await removeHardwareWallet(selectedVendor) if (result) { - history.push({ - pathname: "/hardware-wallet/remove-device/success", - state: { vendor: selectedVendor }, - }) + if (!isFromAccountsPage) { + history.push({ + pathname: "/hardware-wallet/remove-device/success", + state: { vendor: selectedVendor }, + }) + } else { + history.push({ + pathname: "/hardware-wallet", + }) + } } } catch (e: any) { } finally { @@ -44,14 +115,6 @@ const HardwareWalletRemoveDevicePage = () => { } } - const isDeviceImported = (device: Devices): number => { - const accountType = - device === Devices.LEDGER ? AccountType.LEDGER : AccountType.TREZOR - return accounts.filter( - (account: AccountInfo) => account.accountType === accountType - ).length - } - return ( { >
    - - + : "border-primary-100" + } + /> +
    diff --git a/packages/ui/src/routes/hardware-wallet/RemoveSuccessPage.tsx b/packages/ui/src/routes/hardware-wallet/RemoveSuccessPage.tsx index b1d2d180f..218b26c3f 100644 --- a/packages/ui/src/routes/hardware-wallet/RemoveSuccessPage.tsx +++ b/packages/ui/src/routes/hardware-wallet/RemoveSuccessPage.tsx @@ -17,11 +17,11 @@ const HardwareWalletRemoveSuccessPage = () => {
    - + {vendorName} {" removed!"} -
    +
    Your {vendorName} accounts were removed successfully. diff --git a/packages/ui/src/routes/hardware-wallet/SetupLayout.tsx b/packages/ui/src/routes/hardware-wallet/SetupLayout.tsx index 5b5850d2b..affb16d02 100644 --- a/packages/ui/src/routes/hardware-wallet/SetupLayout.tsx +++ b/packages/ui/src/routes/hardware-wallet/SetupLayout.tsx @@ -6,7 +6,9 @@ import PageLayout from "../../components/PageLayout" type SetupLayoutProps = { title: string subtitle: string - buttons: React.ReactNode + buttons?: React.ReactNode + childrenClass?: string + buttonClass?: string } const HardwareWalletSetupLayout: FC> = ({ @@ -14,19 +16,31 @@ const HardwareWalletSetupLayout: FC> = ({ title, subtitle, buttons, + childrenClass, + buttonClass, }) => ( - {title} + {title} -
    - - {subtitle} - + {subtitle && ( +
    + + {subtitle} + +
    + )} +
    + {children}
    -
    {children}
    - -
    {buttons}
    + {buttons && ( + <> + +
    + {buttons} +
    + + )} ) diff --git a/packages/ui/src/routes/hardware-wallet/SuccessPage.tsx b/packages/ui/src/routes/hardware-wallet/SuccessPage.tsx index f4b0cb666..e07addc03 100644 --- a/packages/ui/src/routes/hardware-wallet/SuccessPage.tsx +++ b/packages/ui/src/routes/hardware-wallet/SuccessPage.tsx @@ -44,11 +44,11 @@ const HardwareWalletSuccessPage = () => {
    - + {vendorName}{" "} {!reconnect ? "import completed!" : "reconnected!"} -
    +
    {!reconnect ? ( Your {vendorName} accounts were imported diff --git a/packages/ui/src/routes/hardware-wallet/VendorsPage.tsx b/packages/ui/src/routes/hardware-wallet/VendorsPage.tsx index 7edbf3933..7f84fe9b1 100644 --- a/packages/ui/src/routes/hardware-wallet/VendorsPage.tsx +++ b/packages/ui/src/routes/hardware-wallet/VendorsPage.tsx @@ -1,105 +1,159 @@ -import { useState } from "react" +import { useEffect, useState } from "react" import classnames from "classnames" import { useOnMountHistory } from "../../context/hooks/useOnMount" -import { Devices } from "../../context/commTypes" +import { AccountType, Devices } from "../../context/commTypes" import Divider from "../../components/Divider" // Assets & icons import ledger from "../../assets/images/icons/ledger.svg" import trezor from "../../assets/images/icons/trezor.svg" +import keystone from "../../assets/images/keystone.png" import { ButtonWithLoading } from "../../components/button/ButtonWithLoading" import { Classes } from "../../styles" import HardwareWalletSetupLayout from "./SetupLayout" +import OpenExplorerIcon from "../../components/icons/OpenExplorerIcon" +import { Browsers, getBrowserInfo } from "../../util/window" +import Tooltip from "../../components/label/Tooltip" +import { useSortedAccounts } from "../../context/hooks/useSortedAccounts" + +const browser = getBrowserInfo() const HardwareWalletVendorsPage = () => { const history = useOnMountHistory() + const accounts = useSortedAccounts({ includeHiddenAccounts: true }) + const [keystoneDeviceConnected, setKeystoneDeviceConnected] = + useState(false) const [selectedVendor, setSelectedVendor] = useState() const next = () => { - history.push({ - pathname: "/hardware-wallet/connect", - state: { vendor: selectedVendor }, - }) + if (!keystoneDeviceConnected || selectedVendor !== Devices.KEYSTONE) { + history.push({ + pathname: + selectedVendor !== Devices.KEYSTONE + ? "/hardware-wallet/connect" + : "/hardware-wallet/keystone-connect", + state: { vendor: selectedVendor }, + }) + } else { + history.push({ + pathname: "/hardware-wallet/accounts", + state: { vendor: selectedVendor, isKeystoneConnected: true }, + }) + } } + + useEffect(() => { + if (selectedVendor === Devices.KEYSTONE) { + setKeystoneDeviceConnected( + accounts.filter((q) => q.accountType === AccountType.KEYSTONE) + .length > 0 + ) + } + }, [selectedVendor, accounts]) + return ( - } >
    +
    + + {browser === Browsers.FIREFOX && ( + + + Browser not compatible. Please try + using a different one. + + + } + /> + )} +
    -
    - - - +
    +
    ) } -const ExternalLink = ({ href, title }: { href: string; title: string }) => ( -
    - - {title} - - {">"} -
    -) - export default HardwareWalletVendorsPage diff --git a/packages/ui/src/routes/networks/NetworkDetailsPage.tsx b/packages/ui/src/routes/networks/NetworkDetailsPage.tsx index 597d4e112..03d13329f 100644 --- a/packages/ui/src/routes/networks/NetworkDetailsPage.tsx +++ b/packages/ui/src/routes/networks/NetworkDetailsPage.tsx @@ -12,7 +12,7 @@ const NetworkDetailsPage = () => { canDelete={canDeleteNetwork(network)} network={{ blockExplorerUrl: getFirstUrl(network?.blockExplorerUrls), - rpcUrl: getFirstUrl(network?.rpcUrls), + rpcUrl: network?.currentRpcUrl, name: network?.desc!, chainId: network?.chainId!, symbol: network?.nativeCurrency.symbol!, diff --git a/packages/ui/src/routes/networks/NetworkFormPage.tsx b/packages/ui/src/routes/networks/NetworkFormPage.tsx index 1d40c3b08..4e7517f47 100644 --- a/packages/ui/src/routes/networks/NetworkFormPage.tsx +++ b/packages/ui/src/routes/networks/NetworkFormPage.tsx @@ -14,6 +14,7 @@ import { getDefaultRpc, getSpecificChainDetails, removeNetwork, + changeNetwork, } from "../../context/commActions" import WaitingDialog from "../../components/dialog/WaitingDialog" import useAsyncInvoke from "../../util/hooks/useAsyncInvoke" @@ -28,10 +29,12 @@ import { LINKS } from "../../util/constants" import { useSelectedNetwork } from "../../context/hooks/useSelectedNetwork" import Alert from "../../components/ui/Alert" import Icon, { IconName } from "../../components/ui/Icon" -import ConfirmDialog from "../../components/dialog/ConfirmDialog" +import ConfirmDialog, { + ConfirmDialogState, +} from "../../components/dialog/ConfirmDialog" import { ChainListItem } from "@block-wallet/background/utils/chainlist" import { parseChainId } from "../../util/networkUtils" -import CollapsableWarning from "../../components/CollapsableWarning" +import CollapsableMessage from "../../components/CollapsableMessage" import { AiOutlineWarning } from "react-icons/ai" import usePersistedLocalStorageForm from "../../util/hooks/usePersistedLocalStorageForm" @@ -134,23 +137,31 @@ const NetworkFormPage = ({ const addNetworkInvoke = useAsyncInvoke() const removeNetworkInvoke = useAsyncInvoke() const [isValidating, setIsValidating] = useState(false) - const [confirmDeletion, setConfirmDeletion] = useState(false) const [rpcValidationStatus, setRpcValidationStatus] = useState(RPCUrlValidation.EMPTY) const [rpcChainId, setRpcChainId] = useState(0) const [isNativelySupported, setIsNativelySupported] = useState(false) - const { availableNetworks, isProviderNetworkOnline } = useBlankState()! + const { + availableNetworks, + providerStatus: { isCurrentProviderOnline }, + } = useBlankState()! const [defaultRpcUrl, setDefaultRpcUrl] = useState( undefined ) + const [confirmationDialog, setConfirmationDialog] = + useState({ open: false }) + + const [switchToNetwork, setSwitchToNetwork] = useState(false) + useEffect(() => { if (!network?.chainId) return getDefaultRpc(network?.chainId).then((defaultRpc) => { setDefaultRpcUrl(defaultRpc) }) + // eslint-disable-next-line react-hooks/exhaustive-deps }, []) const { @@ -250,6 +261,7 @@ const NetworkFormPage = ({ return () => { ref && clearTimeout(ref!) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [watchChainId, watchRPCUrl, setIsValidating]) const onSave = handleSubmit(async (data: networkFormData) => { @@ -260,20 +272,38 @@ const NetworkFormPage = ({ name: data.name!, rpcUrl: data.rpcUrl, test: !!data.test, + switchToNetwork: false, + } + + if (!isEdit) { + setConfirmationDialog({ + title: "Switch Network", + message: `Do you want to switch to ${networkData.name} network?`, + open: true, + confirmText: "Yes", + cancelText: "No", + onConfirm: async () => { + networkData.switchToNetwork = true + setSwitchToNetwork(true) + }, + onClose: () => { + setConfirmationDialog({ open: false }) + addNetworkInvoke.run(addNetwork(networkData)) + }, + }) + } else { + addNetworkInvoke.run( + editNetwork({ + chainId: parseChainId(data.chainId)!.toString(), + updates: { + rpcUrl: data.rpcUrl, + blockExplorerUrl: data.blockExplorerUrl, + name: data.name!, + test: !!data.test, + }, + }) + ) } - addNetworkInvoke.run( - isEdit - ? editNetwork({ - chainId: parseChainId(data.chainId)!.toString(), - updates: { - rpcUrl: data.rpcUrl, - blockExplorerUrl: data.blockExplorerUrl, - name: data.name!, - test: !!data.test, - }, - }) - : addNetwork(networkData) - ) }) useEffect(() => { const existingNetwork = Object.values(availableNetworks).find( @@ -282,6 +312,7 @@ const NetworkFormPage = ({ setIsNativelySupported( existingNetwork ? existingNetwork.nativelySupported : false ) + // eslint-disable-next-line react-hooks/exhaustive-deps }, [watchChainId]) const deleteNetwork = () => { removeNetworkInvoke.run(removeNetwork(network!.chainId!)) @@ -325,7 +356,7 @@ const NetworkFormPage = ({ const editingSelectedNetwork = isEdit && selectedChainId === Number(watchChainId) && - isProviderNetworkOnline + isCurrentProviderOnline const canSubmitForm = Object.keys(errors).length === 0 && @@ -352,7 +383,14 @@ const NetworkFormPage = ({
    { - setConfirmDeletion(true) + setConfirmationDialog({ + title: "Delete Network", + message: `Are you sure you want to delete ${network?.name}?`, + open: true, + onConfirm: () => { + deleteNetwork() + }, + }) }} className={ "text-red-500 cursor-pointer flex flex-row items-center p-2 hover:bg-gray-100 rounded-md w-40" @@ -385,7 +423,7 @@ const NetworkFormPage = ({ } > {!isNativelySupported && ( - +
    - + BlockWallet does not verify custom networks. @@ -446,7 +484,10 @@ const NetworkFormPage = ({ if (addNetworkInvoke.isError) { return addNetworkInvoke.reset() } - history.replace("/settings/networks") + + history.push( + isEdit || !switchToNetwork ? "/settings/networks" : "/" + ) }} /> setConfirmDeletion(false)} - onConfirm={() => { - deleteNetwork() - setConfirmDeletion(false) - }} + title={confirmationDialog.title!} + message={confirmationDialog.message!} + open={confirmationDialog.open} + confirmText={confirmationDialog.confirmText} + cancelText={confirmationDialog.cancelText} + onClose={ + confirmationDialog.onClose ?? + (() => setConfirmationDialog({ open: false })) + } + onConfirm={confirmationDialog.onConfirm!} /> -
    -
    +
    +
    { setValue("rpcUrl", defaultRpcUrl) }} @@ -567,8 +611,8 @@ const NetworkFormPage = ({ and it must match with the chain ID returned by the RPC endpoint configured above. You can enter a decimal or a{" "} - 0x prefixed - hexadecimal number. + 0x{" "} + prefixed hexadecimal number. } defaultValue={network?.chainId} @@ -613,7 +657,7 @@ const NetworkFormPage = ({ /> {networkAlreadyExistError && ( - Error: + Error: The network you're trying to add already exists. Try editing the existing network instead. diff --git a/packages/ui/src/routes/networks/NetworksPage.tsx b/packages/ui/src/routes/networks/NetworksPage.tsx index 8710c8a8f..1c749c5ca 100644 --- a/packages/ui/src/routes/networks/NetworksPage.tsx +++ b/packages/ui/src/routes/networks/NetworksPage.tsx @@ -1,11 +1,6 @@ import { DndProvider } from "react-dnd" - -import { keccak256 } from "@ethersproject/keccak256" -import { toUtf8Bytes } from "@ethersproject/strings" - import { HTML5Backend } from "react-dnd-html5-backend" import { useCallback, useEffect, useState } from "react" - import PopupHeader from "../../components/popup/PopupHeader" import PopupLayout from "../../components/popup/PopupLayout" import NetworkDisplay from "../../components/networks/NetworkDisplay" @@ -76,7 +71,7 @@ const NetworksPage = () => { setNetworks(newNetworks) }, - [mainNetworks, testNetworks] + [findNetworkCard, mainNetworks, testNetworks] ) function onSuccessfulDrop(isTestnet: boolean) { @@ -127,6 +122,7 @@ const NetworksPage = () => { ) setMainNetworks(parsedAvailableNetworks.mainnets) setTestNetworks(parsedAvailableNetworks.testnets) + // eslint-disable-next-line react-hooks/exhaustive-deps }, []) return ( @@ -148,7 +144,9 @@ const NetworksPage = () => { />
    - MAINNET + + MAINNET +
    {mainNetworks.map((network) => ( { /> ))}
    - + TESTNET
    diff --git a/packages/ui/src/routes/networks/SearchNetworkPage.tsx b/packages/ui/src/routes/networks/SearchNetworkPage.tsx index 3b570507d..1799dae7a 100644 --- a/packages/ui/src/routes/networks/SearchNetworkPage.tsx +++ b/packages/ui/src/routes/networks/SearchNetworkPage.tsx @@ -56,7 +56,7 @@ const SearchNetworkPage = () => { run(searchChainsByTerm(value)) } - const addOrEditNetwork = () => { + const addOrEditNetwork = async () => { const network = Object.values(availableNetworks).find( (network) => network.enable && network.chainId === pickedChain?.chain.chainId @@ -106,7 +106,15 @@ const SearchNetworkPage = () => { return ( } + header={ + { + history.push("/settings/networks") + }} + /> + } footer={ pickedChain ? ( @@ -122,12 +130,15 @@ const SearchNetworkPage = () => { ) : null } + submitOnEnter={{ + onSubmit: addOrEditNetwork, + isEnabled: !!pickedChain, + }} >
    { alt="search" className="w-7 h-7 absolute z-10" /> -
    +
    - + Search the networks you want to add by name or chain identification. Or add{" "} @@ -177,7 +188,7 @@ const SearchNetworkPage = () => { )} {isSuccess && (
    -
    +
    SEARCH NETWORKS
    diff --git a/packages/ui/src/routes/preferences/DefaultGasPreferencesPage.tsx b/packages/ui/src/routes/preferences/DefaultGasPreferencesPage.tsx index 8166e5629..3f2a0b010 100644 --- a/packages/ui/src/routes/preferences/DefaultGasPreferencesPage.tsx +++ b/packages/ui/src/routes/preferences/DefaultGasPreferencesPage.tsx @@ -41,7 +41,12 @@ const DefaultGasPreferencesPage = () => { return ( + history.push("/settings/preferences")} + /> } footer={ @@ -62,7 +67,7 @@ const DefaultGasPreferencesPage = () => { onDone={history.goBack} />
    - + Set your preferred gas setting for all future transactions.
    @@ -70,9 +75,9 @@ const DefaultGasPreferencesPage = () => {
    { setSelectedOption( @@ -92,14 +97,14 @@ const DefaultGasPreferencesPage = () => {
    {selectedOption === option.name && (
    - +
    )}
    ))}
    - + This setting will apply on all transactions on all networks. You will still be able to change the gas amount before submitting your transactions. diff --git a/packages/ui/src/routes/preferences/DefaultWalletPreferencesPage.tsx b/packages/ui/src/routes/preferences/DefaultWalletPreferencesPage.tsx index 6c6488f85..bef488e70 100644 --- a/packages/ui/src/routes/preferences/DefaultWalletPreferencesPage.tsx +++ b/packages/ui/src/routes/preferences/DefaultWalletPreferencesPage.tsx @@ -52,6 +52,9 @@ const DefaultWalletPreferencesPage: FC = ({ title="Default Browser Wallet" backButton={!isWelcome} close={isWelcome ? false : "/"} + onBack={() => + !isWelcome && history.push("/settings/preferences") + } /> } footer={ @@ -63,9 +66,10 @@ const DefaultWalletPreferencesPage: FC = ({ /> } + submitOnEnter={{ onSubmit: isWelcome ? onNext : onSave }} >
    -
    +
    {wasDefaultBrowserWallet ? ( BlockWallet is set as your default browser wallet. @@ -96,7 +100,7 @@ const DefaultWalletPreferencesPage: FC = ({ onToggle={setDefaultBrowserWallet} /> -
    +
    {wasDefaultBrowserWallet ? ( Turning this off will make BlockWallet unable to diff --git a/packages/ui/src/routes/preferences/LocalePreferencesPage.tsx b/packages/ui/src/routes/preferences/LocalePreferencesPage.tsx index 2eff8b70a..78d79c33c 100644 --- a/packages/ui/src/routes/preferences/LocalePreferencesPage.tsx +++ b/packages/ui/src/routes/preferences/LocalePreferencesPage.tsx @@ -3,7 +3,6 @@ import { Currency } from "@block-wallet/background/utils/currency" import { useHistory } from "react-router-dom" import { ButtonWithLoading } from "../../components/button/ButtonWithLoading" import SuccessDialog from "../../components/dialog/SuccessDialog" -import Select from "../../components/input/Select" import PopupFooter from "../../components/popup/PopupFooter" import PopupHeader from "../../components/popup/PopupHeader" import PopupLayout from "../../components/popup/PopupLayout" @@ -12,6 +11,7 @@ import { getValidCurrencies, setNativeCurrency, } from "../../context/commActions" +import { CurrencySelection } from "../../components/currency/CurrencySelection" const LocalePreferencesPage = () => { const history = useHistory() @@ -19,17 +19,25 @@ const LocalePreferencesPage = () => { const [validCurrencies, setValidCurrencies] = useState([]) const [showSuccessDialog, setShowSuccessDialog] = useState(false) const { nativeCurrency } = useBlankState()! - const [newCurrency, setNewCurrency] = useState(nativeCurrency) + const [newCurrency, setNewCurrency] = useState() + const onSave = useCallback(async () => { try { + if ( + !newCurrency || + newCurrency.code.toLowerCase() === nativeCurrency.toLowerCase() + ) + return + setIsLoading(true) - await setNativeCurrency(newCurrency) + await setNativeCurrency(newCurrency.code) setShowSuccessDialog(true) } catch (e) { throw new Error("Could not update the currency") } finally { setIsLoading(false) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [newCurrency]) useEffect(() => { @@ -37,19 +45,35 @@ const LocalePreferencesPage = () => { setValidCurrencies(currencies) }) }, []) + + useEffect(() => { + setNewCurrency( + validCurrencies.find( + (currency) => + currency.code.toLowerCase() === nativeCurrency.toLowerCase() + ) + ) + }, [nativeCurrency, validCurrencies]) + return ( } + header={ + history.push("/settings/preferences")} + /> + } footer={ @@ -64,20 +88,16 @@ const LocalePreferencesPage = () => { />
    {validCurrencies.length ? ( - + <> + + ) : null}
    diff --git a/packages/ui/src/routes/preferences/NotificationsAndWarningsPage.tsx b/packages/ui/src/routes/preferences/NotificationsAndWarningsPage.tsx new file mode 100644 index 000000000..cda5f9125 --- /dev/null +++ b/packages/ui/src/routes/preferences/NotificationsAndWarningsPage.tsx @@ -0,0 +1,202 @@ +import { useReducer, useRef } from "react" +import { useHistory } from "react-router-dom" +import { ButtonWithLoading } from "../../components/button/ButtonWithLoading" +import ToggleButton from "../../components/button/ToggleButton" +import SuccessDialog from "../../components/dialog/SuccessDialog" +import PopupFooter from "../../components/popup/PopupFooter" +import PopupHeader from "../../components/popup/PopupHeader" +import PopupLayout from "../../components/popup/PopupLayout" +import { useBlankState } from "../../context/background/backgroundHooks" +import { setUserSettings } from "../../context/commActions" +import useAsyncInvoke from "../../util/hooks/useAsyncInvoke" +import { deepEqual } from "../../util/objectUtils" +import { mergeReducer } from "../../util/reducerUtils" + +interface State { + subscribedToNotifications: boolean + hideAddressWarning: boolean + hideSendToContractWarning: boolean + hideSendToNullWarning: boolean + hideEstimatedGasExceedsThresholdWarning: boolean + hideBridgeInsufficientNativeTokenWarning: boolean +} + +const NotificationsAndWarningsPage = () => { + const { settings } = useBlankState()! + const { run, isSuccess, isError, isLoading } = useAsyncInvoke() + const history = useHistory() + + const initialState = useRef({ + subscribedToNotifications: settings.subscribedToNotifications, + hideAddressWarning: settings.hideAddressWarning, + hideSendToContractWarning: settings.hideSendToContractWarning, + hideSendToNullWarning: settings.hideSendToNullWarning, + hideEstimatedGasExceedsThresholdWarning: + settings.hideEstimatedGasExceedsThresholdWarning, + hideBridgeInsufficientNativeTokenWarning: + settings.hideBridgeInsufficientNativeTokenWarning, + }) + + const [preferencesConfig, setPreferencesConfig] = useReducer( + mergeReducer(), + initialState.current + ) + + const onSave = async () => { + run( + setUserSettings({ + ...settings, + subscribedToNotifications: + preferencesConfig.subscribedToNotifications, + hideAddressWarning: preferencesConfig.hideAddressWarning, + hideSendToContractWarning: + preferencesConfig.hideSendToContractWarning, + hideSendToNullWarning: preferencesConfig.hideSendToNullWarning, + hideEstimatedGasExceedsThresholdWarning: + preferencesConfig.hideEstimatedGasExceedsThresholdWarning, + hideBridgeInsufficientNativeTokenWarning: + preferencesConfig.hideBridgeInsufficientNativeTokenWarning, + }) + ) + } + if (isError) { + throw new Error( + "Could not update the address notifications and warning configuration." + ) + } + + return ( + history.push("/settings/preferences")} + /> + } + footer={ + + + + } + > +
    + + Receive BlockWallet's browser notifications. + + + + setPreferencesConfig({ + subscribedToNotifications: value, + }) + } + /> +
    + + Warn me when I try to send tokens to a smart contract + address + + + setPreferencesConfig({ + hideSendToContractWarning: !value, + }) + } + /> +
    + + Warn me when I try to send tokens to the null address + + + setPreferencesConfig({ + hideSendToNullWarning: !value, + }) + } + /> + +
    + + Warn me when my selected account address is different from + transaction's address. + + + + setPreferencesConfig({ + hideAddressWarning: !value, + }) + } + /> + +
    + + Warn me when a dApp suggests fees much lower/higher than + recommended. + + + setPreferencesConfig({ + hideEstimatedGasExceedsThresholdWarning: !value, + }) + } + /> + +
    + + Warn me when I haven't enough funds in the destination + network of a bridge + + + setPreferencesConfig({ + hideBridgeInsufficientNativeTokenWarning: !value, + }) + } + /> +
    + +
    + ) +} + +export default NotificationsAndWarningsPage diff --git a/packages/ui/src/routes/preferences/PhishingProtectionPreferencesPage.tsx b/packages/ui/src/routes/preferences/PhishingProtectionPreferencesPage.tsx index 3792e306a..19dac7686 100644 --- a/packages/ui/src/routes/preferences/PhishingProtectionPreferencesPage.tsx +++ b/packages/ui/src/routes/preferences/PhishingProtectionPreferencesPage.tsx @@ -59,7 +59,13 @@ const PhishingProtectionPreferencesPage = () => { } return ( } + header={ + history.push("/settings/preferences")} + /> + } footer={ { onDone={history.goBack} />
    - + The following image is uniquely created for you to prevent phishing attempts. Ensure this graphic is on every login/seed phrase page. diff --git a/packages/ui/src/routes/preferences/ReleaseNotesPreferencesPage.tsx b/packages/ui/src/routes/preferences/ReleaseNotesPreferencesPage.tsx index 8988b0460..82c0089df 100644 --- a/packages/ui/src/routes/preferences/ReleaseNotesPreferencesPage.tsx +++ b/packages/ui/src/routes/preferences/ReleaseNotesPreferencesPage.tsx @@ -28,7 +28,13 @@ const ReleaseNotesPreferencesPage = () => { subscribedReleaseNotes !== settings.subscribedToReleaseaNotes return ( } + header={ + history.push("/settings/preferences")} + /> + } footer={ { } >
    - + Be up-to-date with latest BlockWallet news. { - const { settings } = useBlankState()! - const { run, isSuccess, isError, isLoading } = useAsyncInvoke() - const history = useHistory() - - const initialState = useRef({ - hideAddressWarning: settings.hideAddressWarning, - hideEstimatedGasExceedsThresholdWarning: - settings.hideEstimatedGasExceedsThresholdWarning, - hideBridgeInsufficientNativeTokenWarning: - settings.hideBridgeInsufficientNativeTokenWarning, - }) - - const [warningsConfig, setWarningsConfig] = useReducer( - mergeReducer(), - initialState.current - ) - - const onSave = async () => { - run( - setUserSettings({ - ...settings, - hideAddressWarning: warningsConfig.hideAddressWarning, - hideEstimatedGasExceedsThresholdWarning: - warningsConfig.hideEstimatedGasExceedsThresholdWarning, - hideBridgeInsufficientNativeTokenWarning: - warningsConfig.hideBridgeInsufficientNativeTokenWarning, - }) - ) - } - if (isError) { - throw new Error("Could not update the address warning configuration.") - } - - return ( - } - footer={ - - - - } - > -
    - - Warn me when my selected account address is different from - transaction's address. - - - - setWarningsConfig({ - hideAddressWarning: !value, - }) - } - /> - -
    - - Warn me when a dApp suggests fees much lower/higher than - recommended. - - - setWarningsConfig({ - hideEstimatedGasExceedsThresholdWarning: !value, - }) - } - /> - -
    - - Warn me when I haven't enough funds in the destination - network of a bridge - - - setWarningsConfig({ - hideBridgeInsufficientNativeTokenWarning: !value, - }) - } - /> -
    - -
    - ) -} - -export default WarningsPreferencesPage diff --git a/packages/ui/src/routes/send/SendConfirmPage.tsx b/packages/ui/src/routes/send/SendConfirmPage.tsx index 9993dd87d..4ee516e6c 100644 --- a/packages/ui/src/routes/send/SendConfirmPage.tsx +++ b/packages/ui/src/routes/send/SendConfirmPage.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from "react" +import { useCallback, useEffect, useLayoutEffect, useState } from "react" import { useForm } from "react-hook-form" @@ -21,7 +21,7 @@ import * as yup from "yup" import { InferType } from "yup" import { BigNumber } from "@ethersproject/bignumber" import { formatUnits, parseUnits } from "@ethersproject/units" -import { Zero, One } from "@ethersproject/constants" +import { Zero } from "@ethersproject/constants" import { formatCurrency, toCurrencyAmount } from "../../util/formatCurrency" import { DEFAULT_DECIMALS, SEND_GAS_COST } from "../../util/constants" @@ -67,13 +67,15 @@ import { rejectTransaction } from "../../context/commActions" import { getValueByKey } from "../../util/objectUtils" import { AddressDisplay } from "../../components/addressBook/AddressDisplay" import { useAccountNameByAddress } from "../../context/hooks/useAccountNameByAddress" +import log from "loglevel" // Schema const GetAmountYupSchema = ( balance: BigNumber, asset: TokenWithBalance | undefined, selectedGas: TransactionFeeData, - isEIP1559Compatible: boolean | undefined + isEIP1559Compatible: boolean | undefined, + allowAmountZero: boolean ) => { return yup.object({ asset: yup @@ -108,71 +110,30 @@ const GetAmountYupSchema = ( return value === "0" || parseFloat(value) > 0 } ) + .test( + "is-zero-allowed", + "Transfer amount must greater than zero for this token.", + (value) => { + if (!allowAmountZero) { + if (typeof value != "string") return false + return parseFloat(value) > 0 + } + return true + } + ) .test("is-decimals", "Too many decimal numbers.", (value) => { if (typeof value != "string") return false if (!value.includes(".")) return true const decimals = asset?.token.decimals || DEFAULT_DECIMALS const valueDecimals = value.split(".")[1].length return valueDecimals <= decimals - }) - .test("is-correct", "Insufficient balance for gas cost.", () => { - return GasCostBalanceValidation( - balance, - selectedGas, - isEIP1559Compatible - ) - }) - .test("is-correct", "Insufficient balance.", (value) => { - try { - if (!asset) return false - const decimals = asset.token.decimals || DEFAULT_DECIMALS - const txAmount: BigNumber = parseUnits( - value!.toString(), - decimals - ) - - if (asset.token.address === "0x0") { - return EtherSendBalanceValidation( - asset.balance, - txAmount, - selectedGas, - isEIP1559Compatible - ) - } else { - if ( - !GasCostBalanceValidation( - balance, - selectedGas, - isEIP1559Compatible - ) - ) { - return false - } - return TokenSendBalanceValidation( - asset.balance, - txAmount - ) - } - } catch (e: any) { - return false - } }), selectedGas: yup.string(), isEIP1559Compatible: yup.boolean(), }) } -const schema = GetAmountYupSchema( - BigNumber.from("0"), - undefined, - { - gasPrice: BigNumber.from("0"), - gasLimit: BigNumber.from("0"), - maxFeePerGas: BigNumber.from("0"), - maxPriorityFeePerGas: BigNumber.from("0"), - }, - false -) -type AmountFormData = InferType + +type AmountFormData = InferType> // Tools @@ -227,23 +188,30 @@ interface SendConfirmPersistedState { amount: string submitted: boolean asset: TokenWithBalance | null + txId: string } const INITIAL_VALUE_PERSISTED_DATA = { asset: null, amount: "", submitted: false, + txId: "", } // Page const SendConfirmPage = () => { // Blank Hooks const { clear: clearLocationRecovery } = useLocationRecovery() + const [allowAmountZero, setAllowAmountZero] = useState(true) const blankState = useBlankState()! const network = useSelectedNetwork() const history: any = useOnMountHistory() const balance = useSelectedAccountBalance() const { address, accountType } = useSelectedAccount() + const receivingAddress = history.location.state.address + const accountNameByAddress = useAccountNameByAddress(receivingAddress) + const selectedAccountName = + history.location.state.name ?? accountNameByAddress // Get data from window.localStorage const [persistedData, setPersistedData] = @@ -256,13 +224,33 @@ const SendConfirmPage = () => { }) const { transaction: currentTransaction, clearTransaction } = - useInProgressInternalTransaction() + useInProgressInternalTransaction({ txId: persistedData.txId }) useEffect(() => { + if ( + currentTransaction?.id && + persistedData.submitted && + persistedData.txId !== currentTransaction?.id + ) { + if (isHardwareWallet(accountType)) { + setPersistedData((prev: SendConfirmPersistedState) => ({ + ...prev, + txId: currentTransaction?.id, + })) + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currentTransaction?.id]) + + useLayoutEffect(() => { // Tx was either rejected or submitted when the pop-up was closed. // If we opened back the pop-up, and there aren't any pending transactions, // we should redirect to the home page (this is only checked on component mount) - if (!currentTransaction?.id && persistedData.submitted) { + if ( + !currentTransaction?.id && + persistedData.submitted && + !persistedData.txId + ) { setPersistedData(() => ({ ...INITIAL_VALUE_PERSISTED_DATA, submitted: false, @@ -275,9 +263,6 @@ const SendConfirmPage = () => { // Restore persisted data on component mount in case the page // is being restored from a popup close const isEIP1559Compatible = network.isEIP1559Compatible - const receivingAddress = history.location.state.address - const selectedAccountName = - history.location.state.name ?? useAccountNameByAddress(receivingAddress) const preSelectedAsset = persistedData?.asset ? persistedData.asset : (history.location.state.asset as TokenWithBalance) @@ -329,6 +314,7 @@ const SendConfirmPage = () => { status: currentTransaction?.status, error: currentTransaction?.error as Error, epochTime: currentTransaction?.approveTime, + qrParams: currentTransaction.qrParams, } : undefined, HardwareWalletOpTypes.SIGN_TRANSACTION, @@ -372,7 +358,8 @@ const SendConfirmPage = () => { balance, selectedToken, selectedGas, - isEIP1559Compatible + isEIP1559Compatible, + allowAmountZero ) const { @@ -382,7 +369,7 @@ const SendConfirmPage = () => { setValue, getValues, trigger, - + watch, formState: { errors }, } = useForm({ resolver: yupResolver(schema), @@ -401,7 +388,6 @@ const SendConfirmPage = () => { data.amount.toString(), selectedToken!.token.decimals || DEFAULT_DECIMALS // Default to eth decimals ) - dispatch({ type: "open", payload: { status: "loading" } }) const isLinked = await checkDeviceIsLinked() @@ -499,7 +485,6 @@ const SendConfirmPage = () => { ...prev, submitted: false, })) - console.error(error) } }) @@ -580,6 +565,7 @@ const SendConfirmPage = () => { const handleChangeAsset = (asset: TokenWithBalance, cleanAmount = true) => { setUsingMax(false) + setAllowAmountZero(true) if (cleanAmount) { handleChangeAmount("") } @@ -604,54 +590,73 @@ const SendConfirmPage = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []) - useEffect(() => { - const fetch = async () => { - try { - setIsGasLoading(true) - - const hasTokenBalance = BigNumber.from( - selectedToken.balance - ).gt(Zero) - - const estimateValue = hasTokenBalance ? One : Zero - - let { gasLimit, estimationSucceeded } = - await getSendTransactionGasLimit( - selectedToken.token.address, - receivingAddress, - estimateValue - ) - - // In case the estimation failed but user has no balance on the selected token, we won't display the estimation error. - if (!hasTokenBalance && !estimationSucceeded) { - estimationSucceeded = true - } + const fetchGasLimit = useCallback(async () => { + try { + setIsGasLoading(true) - setGasEstimationFailed(!estimationSucceeded) + const amount = watch("amount") - let gasPrice - if (!isEIP1559Compatible) { - gasPrice = await getLatestGasPrice() - } + const hasTokenBalance = BigNumber.from(selectedToken.balance).gt( + Zero + ) - setDefaultGas({ - gasLimit: BigNumber.from(gasLimit), - gasPrice: isEIP1559Compatible - ? undefined - : BigNumber.from(gasPrice), - }) - - setSelectedGas({ - ...selectedGas, - gasLimit: BigNumber.from(gasLimit), - }) - } catch (error) { - console.log("error ", error) - } finally { - setIsGasLoading(false) + let estimateValue = hasTokenBalance + ? parseUnits(amount || "1", selectedToken.token.decimals) + : Zero + + //send a value bigger than the account's balance will make the request to fail + if (estimateValue.gt(selectedToken.balance)) { + estimateValue = BigNumber.from(selectedToken.balance) } + + let { gasLimit, estimationSucceeded } = + await getSendTransactionGasLimit( + selectedToken.token.address, + receivingAddress, + estimateValue + ) + + // In case the estimation failed but user has no balance on the selected token, we won't display the estimation error. + if (!hasTokenBalance && !estimationSucceeded) { + estimationSucceeded = true + } + + setGasEstimationFailed(!estimationSucceeded) + + let gasPrice + if (!isEIP1559Compatible) { + gasPrice = await getLatestGasPrice() + } + + setDefaultGas({ + gasLimit: BigNumber.from(gasLimit), + gasPrice: isEIP1559Compatible + ? undefined + : BigNumber.from(gasPrice), + }) + + setSelectedGas({ + ...selectedGas, + gasLimit: BigNumber.from(gasLimit), + }) + } catch (error) { + log.error("error ", error) + if (error.message.match(/bigger than zero/gi)) { + setAllowAmountZero(false) + } + } finally { + setIsGasLoading(false) } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + setIsGasLoading, + setSelectedGas, + selectedToken, + receivingAddress, + isEIP1559Compatible, + ]) + useEffect(() => { const checkIfSendingToTokenAddress = async () => { if ( receivingAddress.toLowerCase() === @@ -661,10 +666,10 @@ const SendConfirmPage = () => { } } - fetch() + fetchGasLimit() checkIfSendingToTokenAddress() // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedToken]) + }, [selectedToken, fetchGasLimit]) // Effect triggered on selected gas change to update max amount if needed and recalculate validations. useEffect(() => { @@ -693,12 +698,14 @@ const SendConfirmPage = () => { disabled={ errors.amount !== undefined || isLoading || - isGasLoading + isGasLoading || + inputFocus } onClick={onSubmit} /> } + showProviderStatus > { }} txHash={currentTransaction?.transactionParams.hash} clickOutsideToClose={false} - timeout={2900} + timeout={1500} gifs={gifs} onDone={() => { if (status === "error") { @@ -723,12 +730,14 @@ const SendConfirmPage = () => { setPersistedData((prev: SendConfirmPersistedState) => ({ ...prev, submitted: false, + txId: "", })) clearTransaction() return } history.push("/") }} + showCloseButton /> { !errors.asset?.message && "mb-3" )} > -

    +

    Asset

    {
    @@ -803,8 +812,8 @@ const SendConfirmPage = () => {
    @@ -824,7 +833,10 @@ const SendConfirmPage = () => { autoComplete="off" autoFocus={true} onFocus={() => setInputFocus(true)} - onBlur={() => setInputFocus(false)} + onBlur={() => { + setInputFocus(false) + fetchGasLimit() + }} onKeyDown={(e) => { setUsingMax(false) const amt = Number( @@ -843,11 +855,11 @@ const SendConfirmPage = () => { handleChangeAmount(e.target.value) } /> - + {formatCurrency(nativeCurrencyAmt, { currency: blankState.nativeCurrency, locale_info: blankState.localeInfo, - showSymbol: true, + showSymbol: false, })}
    @@ -856,10 +868,10 @@ const SendConfirmPage = () => { className={classnames( "float-right rounded-md cursor-pointer border p-1", usingMax - ? "bg-primary-300 border-primary-300 text-white hover:bg-blue-600 hover:border-blue-600" - : "bg-blue-200 border-blue-200 hover:bg-blue-300 hover:border-blue-300", + ? "bg-gray-500 border-gray-500 text-white hover:bg-gray-400 hover:border-gray-400" + : "bg-gray-300 border-gray-300 hover:bg-gray-400 hover:border-gray-400", !HasBalance(selectedToken) && - "pointer-events-none text-gray-600" + "pointer-events-none text-primary-grey-dark" )} title="Use all the available funds" onClick={() => { @@ -867,6 +879,7 @@ const SendConfirmPage = () => { setMaxTransactionAmount( !usingMax ) + fetchGasLimit() } }} > @@ -874,19 +887,23 @@ const SendConfirmPage = () => {
    -
    - - {errors.amount?.message} - -
    + {!error && ( +
    + + {errors.amount?.message} + +
    + )}
    {/* Speed */} -
    diff --git a/packages/ui/src/routes/send/SendPage.tsx b/packages/ui/src/routes/send/SendPage.tsx index da5300df0..c9a17cf0b 100644 --- a/packages/ui/src/routes/send/SendPage.tsx +++ b/packages/ui/src/routes/send/SendPage.tsx @@ -62,6 +62,7 @@ const SendPage = () => { const [canAddContact, setCanAddContact] = useState(false) const searchInputRef = useRef(null) + const [showSearchSkeleton, setShowSearchSkeleton] = useState(false) const { register, @@ -100,7 +101,7 @@ const SendPage = () => { }) const { ref } = register("address") - const onChangeHandler = (event: any) => { + const onChangeHandler = async (event: any) => { // Bind const value = event.target.value setValue("address", value) @@ -143,7 +144,7 @@ const SendPage = () => { } checkAddress() - }, [searchString]) + }, [addressBookAccounts, currentAccount.address, myAccounts, searchString]) const onAccountSelect = (account: any) => { setSelectedAccount(account) @@ -192,18 +193,18 @@ const SendPage = () => { /> } + showProviderStatus > {/* Search or Input */} -
    -
    +
    +
    { @@ -217,8 +218,10 @@ const SendPage = () => { }, 300) }} debounced + debounceTime={1000} + searchShowSkeleton={setShowSearchSkeleton} /> - {canAddContact && ( + {canAddContact && !showSearchSkeleton && ( {
    diff --git a/packages/ui/src/routes/settings/AboutPage.tsx b/packages/ui/src/routes/settings/AboutPage.tsx index ad2ec08b1..09e3b0d2a 100644 --- a/packages/ui/src/routes/settings/AboutPage.tsx +++ b/packages/ui/src/routes/settings/AboutPage.tsx @@ -4,90 +4,87 @@ import AppVersion from "../../components/AppVersion" import { IoLogoTwitter } from "react-icons/io" import { HiGlobeAlt } from "react-icons/hi" -import { FaGithub, FaTelegramPlane } from "react-icons/fa" -import classNames from "classnames" -import logo from "../../assets/images/logo.svg" +import { FaTelegramPlane } from "react-icons/fa" +import { RiDiscordFill } from "react-icons/ri" + import { LINKS } from "../../util/constants" -import { FiDownload } from "react-icons/fi" -import { Classes } from "../../styles" -import useStateLogs from "../../util/hooks/useStateLogs" +import logo from "../../assets/images/logo.svg" +import VerticalSelect from "../../components/input/VerticalSelect" const links = [ { - icon: , + icon: , link: LINKS.WEBSITE, - text: "blockwallet.io", + text: "BlockWallet Website", }, { - icon: , + icon: , link: LINKS.TELEGRAM, - text: "t.me/blockwallet", + text: "Telegram Group Chat", }, { - icon: , - link: LINKS.GITHUB, - text: "github.com/block-wallet", + icon: , + link: LINKS.DISCORD, + text: "Discord Server", }, { - icon: , + icon: , link: LINKS.TWITTER, - text: "twitter.com/GetBlockWallet", + text: "Twitter", }, ] const AboutPage = () => { - const { downloadStateLogsHandler } = useStateLogs() return ( }>
    -
    +
    Blockwallet logo - + BlockWallet
    -

    - BlockWallet is the most private, non-custodial browser - extension wallet where users can store funds and - interact with their favorite blockchain applications - anonymously. +

    + BlockWallet sets you free! Everything you need for a + secure, private and productive Web3 experience in one + lightweight package.

    -

    Join us today and reclaim your privacy.

    -
    - Contacts +
    - {links.map(({ text, link, icon }) => ( - - {icon} - {text} - - ))} + { + const anchor = document.createElement("a") + + Object.assign(anchor, { + target: "_blank", + href: option.link, + rel: "noopener noreferrer", + }).click() + }} + display={(option) => { + return ( +
    + {option.icon} + + {option.text} + +
    + ) + }} + />
    -
    - - Download state logs for support - - -
    ) diff --git a/packages/ui/src/routes/settings/AddContactPage.tsx b/packages/ui/src/routes/settings/AddContactPage.tsx index 6fad1fd5d..67cd7ac3f 100644 --- a/packages/ui/src/routes/settings/AddContactPage.tsx +++ b/packages/ui/src/routes/settings/AddContactPage.tsx @@ -117,7 +117,13 @@ const AddContactPage = () => { onSubmit, isFormValid: Object.keys(errors).length === 0, }} - header={} + header={ + history.push("/settings/addressBook")} + /> + } footer={ { />
    -
    +
    { header={ { history.push({ pathname: "/settings/tokens/add", @@ -74,6 +75,7 @@ const AddTokensConfirmPage = (props: any) => { > } + showProviderStatus > {/* MAIN */}
    diff --git a/packages/ui/src/routes/settings/AddTokensPage.tsx b/packages/ui/src/routes/settings/AddTokensPage.tsx index 9e67915a0..c28753315 100644 --- a/packages/ui/src/routes/settings/AddTokensPage.tsx +++ b/packages/ui/src/routes/settings/AddTokensPage.tsx @@ -16,6 +16,7 @@ import { isAddress } from "@ethersproject/address" import PopupFooter from "../../components/popup/PopupFooter" import { ButtonWithLoading } from "../../components/button/ButtonWithLoading" import useLocalStorageState from "../../util/hooks/useLocalStorageState" +import log from "loglevel" // Types export type TokenResponse = { @@ -59,14 +60,14 @@ const AddTokensPage = () => { return setResults([...exacts, ...others]) }) - .catch((err) => console.log(err)) + .catch((err) => log.error(err)) } else { setResults([]) } } else { setResults([]) } - }, [searchedValue]) + }, [searchedValue, isManualTokenView]) const handleSubmitEnabled = async (value: boolean) => { setSubmitEnabled(value) @@ -97,6 +98,21 @@ const AddTokensPage = () => { networkIndicator /> } + showProviderStatus + footer={ + + + + } // submitOnEnter={{ isEnabled: submitEnabled }} >
    @@ -131,20 +147,6 @@ const AddTokensPage = () => { /> )}
    -
    - {/* FOOTER */} - - -
    ) diff --git a/packages/ui/src/routes/settings/AddressBookPage.tsx b/packages/ui/src/routes/settings/AddressBookPage.tsx index a31ef410f..f6e20fd1e 100644 --- a/packages/ui/src/routes/settings/AddressBookPage.tsx +++ b/packages/ui/src/routes/settings/AddressBookPage.tsx @@ -41,7 +41,7 @@ const AddressBookPage: FunctionComponent<{ /> } > -
    +
    (
    history.push({ pathname: @@ -138,7 +139,7 @@ const AddressBookPage: FunctionComponent<{ )} {!Object.keys(addressBook).length && !Object.keys(recentAddresses).length && ( - + No contacts. )} diff --git a/packages/ui/src/routes/settings/ConnectedSiteAccountsPage.tsx b/packages/ui/src/routes/settings/ConnectedSiteAccountsPage.tsx index ef6ddd721..b4c969342 100644 --- a/packages/ui/src/routes/settings/ConnectedSiteAccountsPage.tsx +++ b/packages/ui/src/routes/settings/ConnectedSiteAccountsPage.tsx @@ -65,13 +65,13 @@ const ConnectedSiteAccount: FunctionComponent<{
    {formatName(account.name, 18)}{" "} {formatHashLastChars(account.address)} @@ -128,14 +128,14 @@ const ConnectedSiteAccount: FunctionComponent<{ {connected ? (
    {active && ( -
    +
    Active
    )} {account.address !== selectedAddress && ( @@ -99,12 +99,15 @@ const ConnectedSitesPage = () => { } >
    - - + + {formatName(account.name, 30)} {" "} diff --git a/packages/ui/src/routes/settings/ExportAccountPage.tsx b/packages/ui/src/routes/settings/ExportAccountPage.tsx index 80eb96f3e..441ce4bd8 100644 --- a/packages/ui/src/routes/settings/ExportAccountPage.tsx +++ b/packages/ui/src/routes/settings/ExportAccountPage.tsx @@ -204,7 +204,7 @@ const ExportAccountPage = () => { account.accountType ) && ( Seed Phrase can only be diff --git a/packages/ui/src/routes/settings/ExportDonePage.tsx b/packages/ui/src/routes/settings/ExportDonePage.tsx index 213b878e2..2d455c7d4 100644 --- a/packages/ui/src/routes/settings/ExportDonePage.tsx +++ b/packages/ui/src/routes/settings/ExportDonePage.tsx @@ -9,9 +9,15 @@ import CopyTooltip from "../../components/label/СopyToClipboardTooltip" import LinkButton from "../../components/button/LinkButton" import { useOnMountHistory } from "../../context/hooks/useOnMount" import ClickToReveal from "../../components/label/ClickToReveal" -import ReactJson from "react-json-view" +import { + JsonView, + defaultStyles, + collapseAllNested, +} from "react-json-view-lite" import DownloadIcon from "../../assets/images/icons/download.svg" +import "react-json-view-lite/dist/index.css" + const ExportDonePage = () => { const history: any = useOnMountHistory() const { exportData, exportType } = history.location.state @@ -51,7 +57,7 @@ const ExportDonePage = () => { />
    ) : (
    - {
    )}
    - Warning: + Warning: Never disclose this information. Anyone with your private keys can steal any assets held in your account. diff --git a/packages/ui/src/routes/settings/Hotkeys.tsx b/packages/ui/src/routes/settings/Hotkeys.tsx new file mode 100644 index 000000000..2431a5c0a --- /dev/null +++ b/packages/ui/src/routes/settings/Hotkeys.tsx @@ -0,0 +1,178 @@ +import { useState, useCallback } from "react" +import PopupHeader from "../../components/popup/PopupHeader" +import PopupLayout from "../../components/popup/PopupLayout" +import PopupFooter from "../../components/popup/PopupFooter" +import { setHotkeysEnabled } from "../../context/commActions" +import { ButtonWithLoading } from "../../components/button/ButtonWithLoading" +import WaitingDialog, { + useWaitingDialog, +} from "../../components/dialog/WaitingDialog" +import { useOnMountHistory } from "../../context/hooks/useOnMount" +import ToggleButton from "../../components/button/ToggleButton" +import { useBlankState } from "../../context/background/backgroundHooks" +import Divider from "../../components/Divider" +import { DisplayHotkey } from "../../components/hotkeys/DisplayHotkey" +import { getCurrentOS } from "../../context/util/platform" + +const LockTimeout = () => { + const history = useOnMountHistory()! + const hotkeysEnabledCurrentStatus = useBlankState()?.hotkeysEnabled ?? false + const [hotkeysEnabled, setHotkeysAllowed] = useState( + hotkeysEnabledCurrentStatus + ) + const { isOpen, status, dispatch } = useWaitingDialog() + const currentOS = getCurrentOS() + + const onSave = useCallback(async () => { + try { + dispatch({ type: "open", payload: { status: "loading" } }) + + await setHotkeysEnabled(hotkeysEnabled) + + dispatch({ type: "setStatus", payload: { status: "success" } }) + } catch (error) { + dispatch({ type: "setStatus", payload: { status: "error" } }) + // throw new Error("Could not update the lock timeout") + } + }, [dispatch, hotkeysEnabled]) + + return ( + history.push("/settings/preferences")} + /> + } + footer={ + + + + } + > + { + if (status === "error") { + dispatch({ type: "close" }) + return + } + + history.push("/") + }} + /> + + + ) +} + +export default LockTimeout diff --git a/packages/ui/src/routes/settings/LockTimeout.tsx b/packages/ui/src/routes/settings/LockTimeout.tsx index d4e669807..dced75af4 100644 --- a/packages/ui/src/routes/settings/LockTimeout.tsx +++ b/packages/ui/src/routes/settings/LockTimeout.tsx @@ -53,7 +53,13 @@ const LockTimeout = () => { onSubmit: onSave, isEnabled: selectedTimeout !== currentTimeout, }} - header={} + header={ + history.push("/settings/preferences")} + /> + } footer={ { }} />
    - + BlockWallet will automatically lock and require an additional login after the selected period. diff --git a/packages/ui/src/routes/settings/PreferencesPage.tsx b/packages/ui/src/routes/settings/PreferencesPage.tsx index e7e68d1df..92a9619ff 100644 --- a/packages/ui/src/routes/settings/PreferencesPage.tsx +++ b/packages/ui/src/routes/settings/PreferencesPage.tsx @@ -7,6 +7,7 @@ import gas from "../../assets/images/icons/gas.svg" import news from "../../assets/images/icons/news.svg" import bell from "../../assets/images/icons/bell.svg" import shield from "../../assets/images/icons/shield.svg" +import hotkeys from "../../assets/images/icons/hotkeys.svg" import { useHistory } from "react-router-dom" import classnames from "classnames" import VerticalSelect from "../../components/input/VerticalSelect" @@ -41,18 +42,33 @@ const PreferencesPage = () => { }, { icon: bell, - label: "Warnings", - to: "/settings/preferences/warnings", + label: "Notifications & Warnings", + to: "/settings/preferences/notificationsAndWarnings", }, { icon: shield, label: "Phishing Protection", to: "/settings/preferences/phishing", }, + { + icon: hotkeys, + label: "Keyboard Shortcuts", + to: "/settings/preferences/hotkeys", + }, ] return ( - }> + { + history.push("/settings") + }} + /> + } + >
    @@ -87,7 +103,7 @@ const PreferencesPage = () => { } />
    - + {option.label} diff --git a/packages/ui/src/routes/settings/SettingsPage.tsx b/packages/ui/src/routes/settings/SettingsPage.tsx index b1ff4a02d..9f2943545 100644 --- a/packages/ui/src/routes/settings/SettingsPage.tsx +++ b/packages/ui/src/routes/settings/SettingsPage.tsx @@ -2,7 +2,6 @@ import { useErrorHandler } from "react-error-boundary" // Components import PopupHeader from "../../components/popup/PopupHeader" -import PopupFooter from "../../components/popup/PopupFooter" import PopupLayout from "../../components/popup/PopupLayout" import VerticalSelect from "../../components/input/VerticalSelect" @@ -26,8 +25,10 @@ import { useOnMountHistory } from "../../context/hooks/useOnMount" import { useBlankState } from "../../context/background/backgroundHooks" import classNames from "classnames" import GenericTooltip from "../../components/label/GenericTooltip" -import AppVersion from "../../components/AppVersion" import { openHardwareConnect } from "../../context/commActions" +import browser from "webextension-polyfill" +import { useHotkeys } from "react-hotkeys-hook" +import { componentsHotkeys } from "../../util/hotkeys" const SettingsPage = () => { const { isSeedPhraseBackedUp, isImportingDeposits } = useBlankState()! @@ -77,6 +78,11 @@ const SettingsPage = () => { } } + const settingsPageHotkeys = componentsHotkeys.SettingsPage + useHotkeys(settingsPageHotkeys, () => { + openHardwareConnect() + }) + return ( { onBack={() => history.push("/")} /> } - footer={ - - - Please wait until deposits are done loading - before locking the wallet. This can take up to - 15 minutes -

    - } - > + > +
    + {!isSeedPhraseBackedUp && ( +
    + + Back up your seed phrase to secure your funds. + - - - } - > -
    +
    + )}
    { return } option.to.includes("https://") - ? chrome.tabs.create({ url: option.to }) + ? browser.tabs.create({ url: option.to }) : history.push({ pathname: option.to, state: { @@ -165,7 +148,7 @@ const SettingsPage = () => { } />
    - + {option.label} @@ -177,28 +160,43 @@ const SettingsPage = () => { ) }} /> - {!isSeedPhraseBackedUp && ( -
    - - Back your seed phrase up and store it in a - safe place. - - -
    - )} + + Please wait until deposits are done loading + before locking the wallet. This can take up + to 15 minutes +

    + } + > + +
    -
    - -
    ) } diff --git a/packages/ui/src/routes/setup/BackupConfirmPage.tsx b/packages/ui/src/routes/setup/BackupConfirmPage.tsx index 2c48796e0..165b4eb56 100644 --- a/packages/ui/src/routes/setup/BackupConfirmPage.tsx +++ b/packages/ui/src/routes/setup/BackupConfirmPage.tsx @@ -67,12 +67,12 @@ const SeedWordsInput: FunctionComponent<{ return (
    -
    +
    {value.map((wordObj, index) => (
    -

    Swap To

    +

    + Swap To +

    { switchInputs() } else { setQuote(undefined) + setError(undefined) setSwapDataState((prev: SwapState) => ({ ...prev, tokenTo: asset.token, @@ -576,7 +584,7 @@ const SwapPage = () => { }} /> {swapFee && ( -
    +
    {`BlockWallet fee (${BASE_SWAP_FEE}%): ${swapFee}`}
    )} diff --git a/packages/ui/src/routes/tokens/TokensPage.tsx b/packages/ui/src/routes/tokens/TokensPage.tsx new file mode 100644 index 000000000..99a56080f --- /dev/null +++ b/packages/ui/src/routes/tokens/TokensPage.tsx @@ -0,0 +1,124 @@ +import { DndProvider } from "react-dnd" +import { HTML5Backend } from "react-dnd-html5-backend" +import { useCallback, useEffect, useState } from "react" +import PopupHeader from "../../components/popup/PopupHeader" +import PopupLayout from "../../components/popup/PopupLayout" +import { editAccountTokensOrder } from "../../context/commActions" +import { useOnMountHistory } from "../../context/hooks/useOnMount" +import { + TokenWithBalance, + useTokenListWithNativeToken, +} from "../../context/hooks/useTokensList" +import TokenDisplayDragDrop from "../../components/token/TokenDisplayDragDrop" +import PopupFooter from "../../components/popup/PopupFooter" +import { ButtonWithLoading } from "../../components/button/ButtonWithLoading" +import SuccessDialog from "../../components/dialog/SuccessDialog" +import { AssetsSortOptions } from "../../util/tokenUtils" +import { AccountTokenOrder } from "@block-wallet/background/controllers/AccountTrackerController" +import { useBlankState } from "../../context/background/backgroundHooks" + +const TokensPage = () => { + const { hideSmallBalances } = useBlankState()! + const history = useOnMountHistory() + const availableTokens = useTokenListWithNativeToken( + AssetsSortOptions.CUSTOM, + hideSmallBalances + ) + const [tokens, setTokens] = useState([]) + const isFromHomePage = history.location.state?.isFromHomePage ?? false + const [successOpen, setSuccessOpen] = useState(false) + + const findTokenCard = useCallback( + (address: string) => { + const token = tokens.find((n) => n.token.address === address)! + + return { + token, + index: tokens.indexOf(token), + } + }, + [tokens] + ) + + const moveTokenCard = useCallback( + (address: string, hoveredOnIndex: number) => { + const { token, index: draggedIndex } = findTokenCard(address) + + const newTokens = structuredClone(tokens) + newTokens.splice(draggedIndex, 1) // removing what is being dragged. + newTokens.splice(hoveredOnIndex, 0, token) // adding the dragged item to the new hovered on index. + + setTokens(newTokens) + }, + [findTokenCard, tokens] + ) + + function onSuccessfulDrop() { + let tokensOrder: AccountTokenOrder = {} + + tokens.forEach((token, index) => { + tokensOrder = { ...tokensOrder, [token.token.address]: index + 1 } + }) + + editAccountTokensOrder(tokensOrder) + } + + useEffect(() => { + setTokens(availableTokens) + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + return ( + + history.push(isFromHomePage ? "/" : "/accounts/menu") + } + /> + } + footer={ + + setSuccessOpen(true)} + /> + + } + > + { + setSuccessOpen(false) + history.push(isFromHomePage ? "/" : "/accounts/menu") + }} + timeout={1000} + /> +
    + +
    +
    + {tokens.map((tokenWithBalance) => ( + + ))} +
    +
    +
    +
    +
    + ) +} + +export default TokensPage diff --git a/packages/ui/src/routes/transaction/ApprovePage.tsx b/packages/ui/src/routes/transaction/ApprovePage.tsx index e6ca223b4..fb272fba4 100644 --- a/packages/ui/src/routes/transaction/ApprovePage.tsx +++ b/packages/ui/src/routes/transaction/ApprovePage.tsx @@ -11,6 +11,7 @@ import { FunctionComponent, useMemo, useCallback, + useLayoutEffect, } from "react" import { approveBridgeAllowance, @@ -28,10 +29,10 @@ import { ButtonWithLoading } from "../../components/button/ButtonWithLoading" import { GasPriceSelector } from "../../components/transactions/GasPriceSelector" import { formatHash, - formatHashLastChars, + formatHashFirstLastChars, formatName, } from "../../util/formatAccount" -import { formatRounded } from "../../util/formatRounded" +import { formatRounded, formatRoundedUp } from "../../util/formatRounded" import { formatUnits, parseUnits } from "@ethersproject/units" import { getAccountColor } from "../../util/getAccountColor" import { useGasPriceData } from "../../context/hooks/useGasPriceData" @@ -49,7 +50,6 @@ import { useTransactionWaitingDialog } from "../../context/hooks/useTransactionW import { HardwareWalletOpTypes } from "../../context/commTypes" import { rejectTransaction } from "../../context/commActions" import { SwapConfirmPageLocalState } from "../swap/SwapConfirmPage" -import { ExchangeType } from "../../context/commTypes" import { TransactionAdvancedData } from "@block-wallet/background/controllers/transactions/utils/types" import { BridgeConfirmPageLocalState } from "../bridge/BridgeConfirmPage" import { useBlankState } from "../../context/background/backgroundHooks" @@ -61,6 +61,7 @@ import { useSelectedAccountBalance } from "../../context/hooks/useSelectedAccoun import unknownTokenIcon from "../../assets/images/unknown_token.svg" import { generateExplorerLink } from "../../util/getExplorer" +import { DEFAULT_EXCHANGE_TYPE } from "../../util/exchangeUtils" const UNLIMITED_ALLOWANCE = MaxUint256 @@ -72,11 +73,13 @@ export enum ApproveOperation { interface ApprovePageState { assetAllowance: BigNumber submitted: boolean + txId: string } const INITIAL_VALUE_PERSISTED_DATA = { assetAllowance: UNLIMITED_ALLOWANCE, submitted: false, + txId: "", } export interface ApprovePageLocalState { @@ -99,6 +102,7 @@ const ApprovePage: FunctionComponent<{}> = () => { () => history.location.state as ApprovePageLocalState, [history.location.state] ) + const selectedAccount = useSelectedAccount() // Get data from window.localStorage const [persistedData, setPersistedData] = @@ -109,19 +113,43 @@ const ApprovePage: FunctionComponent<{}> = () => { // Hooks const { transaction: inProgressTransaction, clearTransaction } = - useInProgressInternalTransaction() + useInProgressInternalTransaction({ txId: persistedData.txId }) + useEffect(() => { + if ( + inProgressTransaction?.id && + persistedData.submitted && + persistedData.txId !== inProgressTransaction?.id + ) { + if (isHardwareWallet(selectedAccount.accountType)) { + setPersistedData((prev: ApprovePageState) => ({ + ...prev, + txId: inProgressTransaction?.id, + })) + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [inProgressTransaction?.id]) + + useLayoutEffect(() => { // Tx was either rejected or submitted when the pop-up was closed. // If we opened back the pop-up, and there aren't any pending transactions, // we should redirect to the home page (this is only checked on component mount) - if (!inProgressTransaction?.id && persistedData.submitted) { + if ( + !inProgressTransaction?.id && + persistedData.submitted && + !persistedData.txId + ) { + setPersistedData(() => ({ + ...INITIAL_VALUE_PERSISTED_DATA, + submitted: false, + })) history.push("/") } // eslint-disable-next-line react-hooks/exhaustive-deps }, []) const selectedAccountBalance = useSelectedAccountBalance() - const selectedAccount = useSelectedAccount() const { chainId, isEIP1559Compatible } = useSelectedNetwork() const { defaultGasOption, selectedNetwork, availableNetworks } = useBlankState()! @@ -134,6 +162,7 @@ const ApprovePage: FunctionComponent<{}> = () => { status: inProgressTransaction?.status, error: inProgressTransaction?.error as Error, epochTime: inProgressTransaction?.approveTime, + qrParams: inProgressTransaction?.qrParams, } : undefined, HardwareWalletOpTypes.APPROVE_ALLOWANCE, @@ -147,8 +176,6 @@ const ApprovePage: FunctionComponent<{}> = () => { } ) - const { assetAllowance } = persistedData || INITIAL_VALUE_PERSISTED_DATA - const [isGasUpdating, setIsGasUpdating] = useState(false) const [hasBalance, setHasBalance] = useState(false) const [customNonce, setCustomNonce] = useState() @@ -166,7 +193,7 @@ const ApprovePage: FunctionComponent<{}> = () => { const [isAllowanceValid, setIsAllowanceValid] = useState(true) const [allowanceAmount, setAllowanceAmount] = useState( minAllowance - ? formatUnits(minAllowance, assetDecimals) + ? formatRoundedUp(formatUnits(minAllowance, assetDecimals)) : formatUnits(UNLIMITED_ALLOWANCE, assetDecimals) ) @@ -236,7 +263,7 @@ const ApprovePage: FunctionComponent<{}> = () => { setSpenderName( currentSpenderAllowances?.groupBy.name ?? - `Spender ${formatHashLastChars(spenderAddress)}` + `Spender ${formatHashFirstLastChars(spenderAddress)}` ) setSpenderAddressExplorerLink( @@ -249,42 +276,41 @@ const ApprovePage: FunctionComponent<{}> = () => { ) } else { const nextState = nextLocationState as SwapConfirmPageLocalState - getExchangeSpender(ExchangeType.SWAP_1INCH).then( - (spenderAddress) => { - const currentSpenderAllowances = currentAllowances.find( + getExchangeSpender(DEFAULT_EXCHANGE_TYPE).then((spenderAddress) => { + const currentSpenderAllowances = currentAllowances.find( + (allowance) => + allowance.groupBy.address.toLowerCase() === + spenderAddress.toLowerCase() + ) + + const currentAllowance = + currentSpenderAllowances?.allowances?.find( (allowance) => - allowance.groupBy.address.toLowerCase() === - spenderAddress.toLowerCase() + allowance.displayData.address.toLowerCase() === + nextState.swapQuote.fromToken.address.toLowerCase() ) - const currentAllowance = - currentSpenderAllowances?.allowances?.find( - (allowance) => - allowance.displayData.address.toLowerCase() === - nextState.swapQuote.fromToken.address.toLowerCase() - ) - - setCurrentAllowanceValue( - currentAllowance?.allowance?.value ?? BigNumber.from(0) - ) - setIsCurrentAllowanceUnlimited( - currentAllowance?.allowance?.isUnlimited - ) - setSpenderName( - currentSpenderAllowances?.groupBy.name ?? - `Spender ${formatHashLastChars(spenderAddress)}` - ) - setSpenderAddressExplorerLink( - generateExplorerLink( - availableNetworks, - selectedNetwork, - spenderAddress, - "address" - ) + setCurrentAllowanceValue( + currentAllowance?.allowance?.value ?? BigNumber.from(0) + ) + setIsCurrentAllowanceUnlimited( + currentAllowance?.allowance?.isUnlimited + ) + setSpenderName( + currentSpenderAllowances?.groupBy.name ?? + `Spender ${formatHashFirstLastChars(spenderAddress)}` + ) + setSpenderAddressExplorerLink( + generateExplorerLink( + availableNetworks, + selectedNetwork, + spenderAddress, + "address" ) - } - ) + ) + }) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []) // Fees @@ -357,7 +383,7 @@ const ApprovePage: FunctionComponent<{}> = () => { allowanceResponse = await approveExchange( parseUnits(allowanceAmount, assetDecimals), BigNumber.from(nextState.swapQuote.fromTokenAmount), - ExchangeType.SWAP_1INCH, + DEFAULT_EXCHANGE_TYPE, { gasPrice: !isEIP1559Compatible ? selectedGasPrice @@ -422,6 +448,7 @@ const ApprovePage: FunctionComponent<{}> = () => { setPersistedData((prev: ApprovePageState) => ({ ...prev, submitted: false, + txId: "", })) clearTransaction() return @@ -477,7 +504,7 @@ const ApprovePage: FunctionComponent<{}> = () => { href={spenderAddressExplorerLink} target="_blank" rel="noreferrer" - className="text-primary-300 hover:underline" + className="text-primary-blue-default hover:underline" > {spenderName} {" "} @@ -490,7 +517,7 @@ const ApprovePage: FunctionComponent<{}> = () => { href={spenderAddressExplorerLink} target="_blank" rel="noreferrer" - className="text-primary-300 hover:underline" + className="text-primary-blue-default hover:underline" > {spenderName} {" "} @@ -503,11 +530,13 @@ const ApprovePage: FunctionComponent<{}> = () => { const mainSection = ( <>
    -

    {`You are about to update your ${assetName} allowance`}

    -

    {mainSectionText}

    +

    {`You are about to update your ${assetName} allowance`}

    +

    + {mainSectionText} +

    {currentAllowanceValue && (

    = () => { currentAllowance={currentAllowanceValue} /> - + {!isEIP1559Compatible ? ( = () => { }} buttonDisplay={false} /> - + {hasBalance ? undefined : "Insufficient funds"}

    @@ -617,6 +648,7 @@ const ApprovePage: FunctionComponent<{}> = () => { /> } + showProviderStatus > = () => { "There was an error while approving the asset.", }} txHash={inProgressTransaction?.transactionParams.hash} - timeout={2900} + timeout={1500} onDone={onDone} clickOutsideToClose={false} gifs={gifs} + showCloseButton />
    = () => { fill={getAccountColor(selectedAccount.address)} />
    - + {formatName(selectedAccount.name, 15)} {formatHash(selectedAccount.address)} @@ -664,7 +697,7 @@ const ApprovePage: FunctionComponent<{}> = () => { formatUnits(assetBalance || "0", assetDecimals) )} ${assetName}`} > - + {`${formatRounded( formatUnits(assetBalance || "0", assetDecimals) )}`} @@ -693,7 +726,7 @@ const ApprovePage: FunctionComponent<{}> = () => { 18 )} ${nativeToken.token.symbol}`} > - + {formatName( formatRounded( formatUnits( diff --git a/packages/ui/src/stories/Button.stories.tsx b/packages/ui/src/stories/Button.stories.tsx index 84358ca13..831a55b55 100644 --- a/packages/ui/src/stories/Button.stories.tsx +++ b/packages/ui/src/stories/Button.stories.tsx @@ -18,18 +18,18 @@ export const DarkButton = () => ( export const ArrowHoverButton = () => (
    - + - + Receive
    - - + Send
    diff --git a/packages/ui/src/stories/Loading.stories.tsx b/packages/ui/src/stories/Loading.stories.tsx index b56f5e0cd..52e1ed339 100644 --- a/packages/ui/src/stories/Loading.stories.tsx +++ b/packages/ui/src/stories/Loading.stories.tsx @@ -1,7 +1,5 @@ import { Meta } from "@storybook/react" import classnames from "classnames" - -import { Classes } from "../styles" import Placeholder from "../components/Placeholder" export const BasePlaceholder = () => ( diff --git a/packages/ui/src/stories/views/ExtensionViews.stories.tsx b/packages/ui/src/stories/views/ExtensionViews.stories.tsx index 3c058a43c..23ee21d27 100644 --- a/packages/ui/src/stories/views/ExtensionViews.stories.tsx +++ b/packages/ui/src/stories/views/ExtensionViews.stories.tsx @@ -10,7 +10,6 @@ import { import { parseEther } from "@ethersproject/units" import { CurrencyAmountPair } from "@block-wallet/background/controllers/privacy/types" import { DappReq } from "../../context/hooks/useDappRequest" -import { TransactionMeta } from "@block-wallet/background/controllers/transactions/utils/types" import { NormalizedAddEthereumChainParameter } from "@block-wallet/background/utils/types/ethereum" export const Popup = () => ( @@ -62,10 +61,10 @@ const generateTx = (i: number) => ({ }) const mockTxs = [0, 1, 2, 3, 4].map(generateTx) -const mockTxsByHash = mockTxs.reduce((o, tx) => { +/* const mockTxsByHash = mockTxs.reduce((o, tx) => { o[tx.id] = tx return o -}, {} as any) +}, {} as any) */ export const PopupTransactions = () => ( li:first-child { border-top-right-radius: 6px; border-top-left-radius: 6px; @@ -152,3 +156,9 @@ textarea:focus { .fullscreen-x-scrollbar::-webkit-scrollbar { height: 1.5px; } + +input[type="search"]::-webkit-search-cancel-button { + filter: grayscale(100%); + cursor: pointer; + margin-top: 1px; +} diff --git a/packages/ui/src/util/__tests__/account.test.ts b/packages/ui/src/util/__tests__/account.test.ts index f2e232760..76312931c 100644 --- a/packages/ui/src/util/__tests__/account.test.ts +++ b/packages/ui/src/util/__tests__/account.test.ts @@ -54,11 +54,13 @@ describe("Account helpers tests", () => { it("Should return that it is a hardware wallet", () => { expect(isHardwareWallet(AccountType.TREZOR)).toBeTruthy() expect(isHardwareWallet(AccountType.LEDGER)).toBeTruthy() + expect(isHardwareWallet(AccountType.KEYSTONE)).toBeTruthy() }) it("Should return that it is not an internal account", () => { expect(isInternalAccount(AccountType.TREZOR)).toBeFalsy() expect(isInternalAccount(AccountType.EXTERNAL)).toBeFalsy() expect(isInternalAccount(AccountType.LEDGER)).toBeFalsy() + expect(isInternalAccount(AccountType.KEYSTONE)).toBeFalsy() }) it("Should return that it is an internal account", () => { expect(isInternalAccount(AccountType.HD_ACCOUNT)).toBeTruthy() diff --git a/packages/ui/src/util/account.ts b/packages/ui/src/util/account.ts index 44f62f882..88b3d3263 100644 --- a/packages/ui/src/util/account.ts +++ b/packages/ui/src/util/account.ts @@ -23,7 +23,11 @@ export const getDefaultAccountName = (accountNumber: number): string => { /** * The list of account types that are considered hardware wallets. */ -export const HARDWARE_TYPES = [AccountType.TREZOR, AccountType.LEDGER] +export const HARDWARE_TYPES = [ + AccountType.TREZOR, + AccountType.LEDGER, + AccountType.KEYSTONE, +] /** * isHardwareWallet diff --git a/packages/ui/src/util/approval.ts b/packages/ui/src/util/approval.ts index 9fdec0abd..ef3806876 100644 --- a/packages/ui/src/util/approval.ts +++ b/packages/ui/src/util/approval.ts @@ -1,5 +1,6 @@ -import { parseUnits } from "@ethersproject/units" +import { parseUnits, formatUnits } from "@ethersproject/units" import { stripHexPrefix } from "ethereumjs-util" +import { MaxUint256 } from "@ethersproject/constants" /** * Given a token unit and the token decimals, it returns a hex string @@ -12,7 +13,14 @@ export const parseAllowance = (allowance: string, decimals: number) => { const parsedCustomAllowance = parseUnits(allowance, decimals)._hex if (stripHexPrefix(parsedCustomAllowance).length > 64) { - throw new Error("Custom allowance is larger than u256") + console.warn( + `Custom allowance is larger than u256. Fallbacking to: ${MaxUint256}` + ) + const maxAllowance = parseUnits( + formatUnits(MaxUint256, decimals), + decimals + )._hex + return maxAllowance } return parsedCustomAllowance diff --git a/packages/ui/src/util/bridgeUtils.ts b/packages/ui/src/util/bridgeUtils.ts index a146c1d67..6e18b53cb 100644 --- a/packages/ui/src/util/bridgeUtils.ts +++ b/packages/ui/src/util/bridgeUtils.ts @@ -25,7 +25,6 @@ import { } from "@block-wallet/background/controllers/transactions/utils/types" import { DetailedItem } from "../components/transactions/TransactionDetailsList" import { BridgeTransactionsData } from "./hooks/useGetBridgeTransactionsData" -import isNil from "./isNil" import { formatUnits } from "@ethersproject/units" import { bnOr0 } from "./numberUtils" diff --git a/packages/ui/src/util/connectionStepUtils.tsx b/packages/ui/src/util/connectionStepUtils.tsx index 01fab3dcf..2ce5f4b17 100644 --- a/packages/ui/src/util/connectionStepUtils.tsx +++ b/packages/ui/src/util/connectionStepUtils.tsx @@ -1,4 +1,5 @@ import { Devices } from "../context/commTypes" + export interface ConnectionSeptInfo { label: string | string[] | React.ReactElement[] info?: string @@ -20,6 +21,7 @@ export const DEVICE_RECONNECTION_WARNING_STEPS: StepData = { { label: "Only a single Trezor is plugged in" }, { label: "You have followed the steps on Trezor Connect page" }, ], + KEYSTONE: [{ label: "Show Keystone QR code to camera" }], } export const DEVICE_CONNECTION_STEPS: StepData = { @@ -37,4 +39,19 @@ export const DEVICE_CONNECTION_STEPS: StepData = { { label: 'Click "Continue" below' }, { label: "Follow steps on Trezor Connect page" }, ], + KEYSTONE: [ + { + label: "Make sure your camera is connected and installed properly.", + }, + { + label: "Unlock your Keystone.", + }, + { + label: 'Select "Options" then "Connect Software Wallet".', + }, + { + label: 'Click "Continue" below', + }, + { label: "Show the Keystone QR code to the camera." }, + ], } diff --git a/packages/ui/src/util/constants.ts b/packages/ui/src/util/constants.ts index 885df7003..66f45f9c9 100644 --- a/packages/ui/src/util/constants.ts +++ b/packages/ui/src/util/constants.ts @@ -41,6 +41,7 @@ export const SWAP_QUOTE_REFRESH_TIMEOUT = 1000 * 15 export const LINKS = { WEBSITE: "https://blockwallet.io/", TELEGRAM: "https://t.me/blockwallet", + DISCORD: "https://discord.gg/blockwallet", GITHUB: "https://github.com/block-wallet/", TWITTER: "https://twitter.com/GetBlockWallet", WEBSITE_BUG_REPORT: "https://blockwallet.io/bug-report.html", @@ -48,14 +49,19 @@ export const LINKS = { "https://github.com/block-wallet/extension/issues/new?assignees=&labels=&template=bug_report.md&title=", ARTICLES: { HD_PATH: - "https://help.blockwallet.io/hc/en-us/articles/6670471581841-What-is-an-HD-Path-", + "https://blockwallet.io/docs/what-is-an-hd-path", LOCK_TIMEOUT: - "https://help.blockwallet.io/hc/en-us/articles/6815376575249", + "https://blockwallet.io/docs/what-is-lock-timeout", CUSTOM_NETWORK_RISKS: - "https://help.blockwallet.io/hc/en-us/articles/7786071253649-How-to-Add-a-Custom-Network-RPC", + "https://blockwallet.io/docs/add-a-network-or-custom-rpc", MALICIOUS_DAPPS: - "https://help.blockwallet.io/hc/en-us/articles/10374731466769", - BRIDGES: "https://help.blockwallet.io/hc/en-us/articles/10669678195473", + "https://blockwallet.io/docs/stay-safe-when-connecting-to-dapps", + BRIDGES: "https://blockwallet.io/docs/bridge-tokens-using-block-wallet", CHANGELOG: "https://bit.ly/bw-release", }, } + +/** + * Time ellapsed before refreshing the gas prices + */ +export const GAS_PRICE_UPDATE_INTERVAL = 10000 // 10s diff --git a/packages/ui/src/util/exchangeUtils.ts b/packages/ui/src/util/exchangeUtils.ts index 0679b640a..a27a7dd51 100644 --- a/packages/ui/src/util/exchangeUtils.ts +++ b/packages/ui/src/util/exchangeUtils.ts @@ -9,13 +9,14 @@ import { BigNumber } from "@ethersproject/bignumber" import { formatUnits } from "@ethersproject/units" import { RichedTransactionMeta } from "./transactionUtils" import { Rates } from "@block-wallet/background/controllers/ExchangeRatesController" -import { BasicToken } from "@block-wallet/background/utils/types/1inch" import { getValueByKey } from "./objectUtils" import { toCurrencyAmount } from "./formatCurrency" +import { BasicToken } from "@block-wallet/background/utils/swaps/1inch" -const ONEINCH_NATIVE_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" +const SWAP_NATIVE_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" +export const DEFAULT_EXCHANGE_TYPE = ExchangeType.SWAP_OPENOCEAN export const isSwapNativeTokenAddress = (address: string): boolean => { - return address.toLowerCase() === ONEINCH_NATIVE_ADDRESS.toLowerCase() + return address.toLowerCase() === SWAP_NATIVE_ADDRESS.toLowerCase() } /** @@ -53,7 +54,7 @@ export const populateExchangeTransaction = ( transactionCategory: TransactionCategories.EXCHANGE, methodSignature: swapParameters.methodSignature, exchangeParams: { - exchangeType: ExchangeType.SWAP_1INCH, + exchangeType: DEFAULT_EXCHANGE_TYPE, fromToken: swapParameters.fromToken, toToken: swapParameters.toToken, fromTokenAmount: swapParameters.fromTokenAmount, diff --git a/packages/ui/src/util/formatAccount.ts b/packages/ui/src/util/formatAccount.ts index e4ef485a3..24ac3a5dc 100644 --- a/packages/ui/src/util/formatAccount.ts +++ b/packages/ui/src/util/formatAccount.ts @@ -13,7 +13,7 @@ export const formatName = ( maxLength: number = 13 ): string => { if (!accountName) { - return 'Account' + return "Account" } else { if (accountName.length < maxLength) { return accountName @@ -24,6 +24,18 @@ export const formatName = ( } export const formatHashLastChars = (hash: string, chars: number = 4) => { - return `(...${hash.slice(-chars)})` -} \ No newline at end of file +} + +export const formatHashFirstLastChars = ( + hash: string, + first: number = 6, + last: number = 4 +) => { + if (first + last >= hash.length) { + return hash + } + const firstPart = hash.slice(0, first) + const lastPart = hash.slice(-last) + return `(${firstPart}...${lastPart})` +} diff --git a/packages/ui/src/util/formatRounded.ts b/packages/ui/src/util/formatRounded.ts index 4a8244602..d175996e2 100644 --- a/packages/ui/src/util/formatRounded.ts +++ b/packages/ui/src/util/formatRounded.ts @@ -9,3 +9,22 @@ import { FixedNumber } from "@ethersproject/bignumber" export const formatRounded = (value: string, decimals: number = 4): string => { return FixedNumber.fromString(value).round(decimals).toString() } + +/** + * Receives a string containing numbers with decimals and applies ceil according to param. + * @param value string to ceil + * @param decimals number of decimals to ceil. Defaults to 4. + * @returns ceiled string + * @example + * formatRoundedUp('1.00009', 4) // '1.0001' + * formatRoundedUp('1.001', 2) // '1.01' + */ +export const formatRoundedUp = ( + value: string, + decimals: number = 4 +): string => { + const fixedNum = FixedNumber.fromString(value) + const factor = FixedNumber.from(Math.pow(10, decimals)) + const roundedNum = fixedNum.mulUnsafe(factor).ceiling().divUnsafe(factor) + return roundedNum.toString() +} diff --git a/packages/ui/src/util/gasPrice.ts b/packages/ui/src/util/gasPrice.ts index a165c2fd6..ecb581ec8 100644 --- a/packages/ui/src/util/gasPrice.ts +++ b/packages/ui/src/util/gasPrice.ts @@ -50,7 +50,7 @@ const gasPriceToNativeCurrency = ( currency: localeInfo.currency, locale_info: localeInfo.language, showCurrency: true, - showSymbol: true, + showSymbol: false, precision: 2, } ) diff --git a/packages/ui/src/util/getExplorer.ts b/packages/ui/src/util/getExplorer.ts index dcf333379..b90e6bdc0 100644 --- a/packages/ui/src/util/getExplorer.ts +++ b/packages/ui/src/util/getExplorer.ts @@ -6,7 +6,6 @@ import { createCustomExplorerLink, createCustomAccountLink, } from "@block-wallet/explorer-link" -import { capitalize } from "./capitalize" export const getChainIdFromNetwork = (networks: Networks, network?: String) => { if (!network) { diff --git a/packages/ui/src/util/hardwareDevice.ts b/packages/ui/src/util/hardwareDevice.ts index f8c6accad..c28d68222 100644 --- a/packages/ui/src/util/hardwareDevice.ts +++ b/packages/ui/src/util/hardwareDevice.ts @@ -19,6 +19,21 @@ export const getDeviceFromAccountType = ( return Devices.LEDGER case AccountType.TREZOR: return Devices.TREZOR + case AccountType.KEYSTONE: + return Devices.KEYSTONE } return undefined } + +export const getAccountTypeFromDevice = ( + device: Devices +): AccountType.LEDGER | AccountType.TREZOR | AccountType.KEYSTONE => { + switch (device) { + case Devices.LEDGER: + return AccountType.LEDGER + case Devices.TREZOR: + return AccountType.TREZOR + case Devices.KEYSTONE: + return AccountType.KEYSTONE + } +} diff --git a/packages/ui/src/util/hooks/token/useTokenSearch.tsx b/packages/ui/src/util/hooks/token/useTokenSearch.tsx new file mode 100644 index 000000000..a2048322e --- /dev/null +++ b/packages/ui/src/util/hooks/token/useTokenSearch.tsx @@ -0,0 +1,32 @@ +import { useMemo, useState } from "react" +import { TokenWithBalance } from "../../../context/hooks/useTokensList" + +const matchByTerm = + (term?: string) => + (token: TokenWithBalance): boolean => { + if (!term) return true + + return ( + token.token.address.includes(term) || + token.token.symbol.toLowerCase().includes(term) || + token.token.name.toLowerCase().includes(term) + ) + } + +const filterTokens = (tokens: TokenWithBalance[], term?: string) => { + return tokens.filter(matchByTerm(term)) +} + +const useTokenSearch = (tokens: TokenWithBalance[]) => { + const [search, setSearch] = useState("") + const result = useMemo(() => { + return filterTokens(tokens, search.toLowerCase()) + }, [search, tokens]) + return { + tokensResult: result, + search, + onChangeSearch: setSearch, + } +} + +export default useTokenSearch diff --git a/packages/ui/src/util/hooks/useCountdown.ts b/packages/ui/src/util/hooks/useCountdown.ts index d1aa227ea..09b9f1c34 100644 --- a/packages/ui/src/util/hooks/useCountdown.ts +++ b/packages/ui/src/util/hooks/useCountdown.ts @@ -1,18 +1,18 @@ -import React from "react" +import { useRef, useEffect, useState } from "react" import { calculateRemainingSeconds } from "../time" const useCountdown = ( startTime: number | undefined, timeout: number | undefined ) => { - const [remainingSeconds, setRemainingSeconds] = React.useState< + const [remainingSeconds, setRemainingSeconds] = useState< number | undefined >(() => { return calculateRemainingSeconds(startTime, timeout) }) - const intervalRef = React.useRef() + const intervalRef = useRef() - React.useEffect(() => { + useEffect(() => { if (startTime !== undefined && timeout !== undefined) { //don't wait 1 second to execute first calculation. setRemainingSeconds(calculateRemainingSeconds(startTime, timeout)) diff --git a/packages/ui/src/util/hooks/useCurrencyFormatter.ts b/packages/ui/src/util/hooks/useCurrencyFormatter.ts index ff0071d81..0a2cdb866 100644 --- a/packages/ui/src/util/hooks/useCurrencyFormatter.ts +++ b/packages/ui/src/util/hooks/useCurrencyFormatter.ts @@ -25,7 +25,7 @@ const useCurrencyFromatter = () => { currency: state.nativeCurrency, locale_info: state.localeInfo, returnNonBreakingSpace: true, - showSymbol: true, + showSymbol: false, }) } return { format } diff --git a/packages/ui/src/util/hooks/useHardwareWalletConnect.tsx b/packages/ui/src/util/hooks/useHardwareWalletConnect.tsx index bacb5c154..b1c3cc0d3 100644 --- a/packages/ui/src/util/hooks/useHardwareWalletConnect.tsx +++ b/packages/ui/src/util/hooks/useHardwareWalletConnect.tsx @@ -1,17 +1,31 @@ -import { connectHardwareWallet } from "../../context/commActions" +import { + connectHardwareWallet, + hardwareQrSubmitCryptoHdKeyOrAccount, +} from "../../context/commActions" import { Devices } from "../../context/commTypes" import { requestConnectDevice } from "../../context/util/requestConnectDevice" import useAsyncInvoke from "./useAsyncInvoke" import log from "loglevel" import useClearStickyStorage from "../../context/hooks/useClearStickyStorage" +import { URParameter } from "../../components/qr/QRReader" -const executeConnect = async (vendor: Devices): Promise => { +const executeConnect = async ( + vendor: Devices, + ur?: URParameter +): Promise => { // If we add transport type selection, here we should validate if it's WebHID if (vendor === Devices.LEDGER) { const connectionOk = await requestConnectDevice() if (!connectionOk) { return Promise.resolve(false) } + } else if (vendor === Devices.KEYSTONE) { + const submissionOk = await hardwareQrSubmitCryptoHdKeyOrAccount( + ur || { type: "", cbor: "" } + ) + if (!submissionOk) { + return Promise.resolve(false) + } } try { return await connectHardwareWallet(vendor) @@ -26,14 +40,14 @@ const useHardwareWalletConnect = (isReconnecting = false) => { const { clear: clearStickyStorage } = useClearStickyStorage() return { - connect: async (vendor: Devices) => { + connect: async (vendor: Devices, ur?: URParameter) => { // Get rid of the sticky storage data // as the user should see the home page after the connection // when opening the extension again. if (!isReconnecting) { clearStickyStorage() } - return run(executeConnect(vendor)) + return run(executeConnect(vendor, ur)) }, isLoading, isError, diff --git a/packages/ui/src/util/hooks/useHotKey.ts b/packages/ui/src/util/hooks/useHotKey.ts new file mode 100644 index 000000000..75e3fdd84 --- /dev/null +++ b/packages/ui/src/util/hooks/useHotKey.ts @@ -0,0 +1,100 @@ +import { useHotkeys } from "react-hotkeys-hook" +import { useBlankState } from "../../context/background/backgroundHooks" +import { lockApp } from "../../context/commActions" +import { useOnMountHistory } from "../../context/hooks/useOnMount" +import { getActionByHotkeyAndPath, getHotkeyByPath } from "../hotkeys" +/** + * Hook to handle the hotkeys for each page + */ + +export interface UseHotKeyProps { + onClose?: (event: any) => void + onBack?: (event: any) => void // in case we want to replace default back behavior + permissions?: { [action: string]: boolean } +} + +const useHotKey = ( + { onClose, onBack, permissions }: UseHotKeyProps = { + onClose: undefined, + onBack: undefined, + } +) => { + const { hotkeysEnabled } = useBlankState()! + const history = useOnMountHistory() + const currentLocation = history.location.pathname + const hotKeys = getHotkeyByPath(currentLocation) + + useHotkeys( + hotKeys + ",ctrl+alt+l,alt+backspace,alt+q", + (e) => { + if (!hotkeysEnabled) return + if (!e.key) { + return + } + const keyPressed = e.code + .replace(/key/i, "") + .replace(/digit/i, "") + .replace(/numpad/i, "") + .toLowerCase() + + //logout --ctrl+alt+l + if (e.ctrlKey && e.altKey && keyPressed === "l") { + lockApp() + return + } + + //Page back --alt + backspace + if (e.altKey && keyPressed === "backspace") { + if (onBack) return onBack(e) + return + } + + //Close extension --alt+q + if (e.altKey && keyPressed === "q") { + if (onClose) return onClose(e) + return + } + + const permissionsIndex = currentLocation + .concat( + e.altKey && e.ctrlKey + ? "/ctrlalt/" + : e.altKey + ? "/alt/" + : "/ctrl/" + ) + .concat(keyPressed) + if ( + permissions && + permissions[permissionsIndex] !== undefined && + !permissions[permissionsIndex] + ) { + return + } + + const navigateTo = getActionByHotkeyAndPath( + currentLocation, + keyPressed, + e.altKey && e.ctrlKey ? "CTRLALT" : e.altKey ? "ALT" : "CTRL" + ) + + if (navigateTo) { + if (typeof navigateTo === "string") { + //Need to validate permissions + history.push({ + pathname: navigateTo, + state: { + from: history.location.pathname, + }, + }) + return + } + + navigateTo() + } + }, + { enableOnFormTags: true, preventDefault: true } + ) +} + +export default useHotKey diff --git a/packages/ui/src/util/hooks/useTokenTransactions.ts b/packages/ui/src/util/hooks/useTokenTransactions.ts index 6f248f0f7..99b74b2dd 100644 --- a/packages/ui/src/util/hooks/useTokenTransactions.ts +++ b/packages/ui/src/util/hooks/useTokenTransactions.ts @@ -5,12 +5,12 @@ import { Token } from "@block-wallet/background/controllers/erc-20/Token" import { BigNumber } from "@ethersproject/bignumber" const useTokenTransactions = (token: Token | undefined) => { + const { transactions } = useTransactions() if (!token) { return [] as RichedTransactionMeta[] } try { const contractToLower = token.address.toLowerCase() - const { transactions } = useTransactions() return transactions.filter( ({ transactionParams, transactionReceipt, transferType }) => { if (isNativeTokenAddress(token.address)) { @@ -23,9 +23,9 @@ const useTokenTransactions = (token: Token | undefined) => { ) } else { return ( - transactionReceipt?.contractAddress?.toLowerCase() == + transactionReceipt?.contractAddress?.toLowerCase() === contractToLower || - transactionParams.to?.toLowerCase() == contractToLower + transactionParams.to?.toLowerCase() === contractToLower ) } } diff --git a/packages/ui/src/util/hooks/useTransactions.ts b/packages/ui/src/util/hooks/useTransactions.ts index 4300db74a..9f067ed7f 100644 --- a/packages/ui/src/util/hooks/useTransactions.ts +++ b/packages/ui/src/util/hooks/useTransactions.ts @@ -34,7 +34,7 @@ const useTransactions = () => { amount: value ? value : BigNumber.from("0"), currency: networkNativeCurrency.symbol, decimals: networkNativeCurrency.decimals, - logo: defaultNetworkLogo, + logo: networkNativeCurrency.logo ?? defaultNetworkLogo, }, } }) diff --git a/packages/ui/src/util/hotkeys.ts b/packages/ui/src/util/hotkeys.ts new file mode 100644 index 000000000..c47d8ef20 --- /dev/null +++ b/packages/ui/src/util/hotkeys.ts @@ -0,0 +1,454 @@ +import { updatePopupTab } from "../context/commActions" +import { useOnMountHistory } from "../context/hooks/useOnMount" + +type Action = string | ((p?: any) => Promise) + +type HotkeyAction = { + hotkey: string + action: Action + description: string + hotkeyDescription: string +} + +type HotkeyMap = { + [path: string]: { + CTRL: HotkeyAction[] + ALT: HotkeyAction[] + CTRLALT: HotkeyAction[] + } +} + +const locations: HotkeyMap = { + "/home": { + ALT: [ + { + hotkey: "A", + action: "/accounts", + hotkeyDescription: "ALT+A", + description: "Accounts", + }, + { + hotkey: "B", + action: "/bridge", + hotkeyDescription: "ALT+B", + description: "Bridge", + }, + { + hotkey: "S", + action: "/send", + hotkeyDescription: "ALT+S", + description: "Send", + }, + { + hotkey: "U", + action: "/buy", + hotkeyDescription: "ALT+U", + description: "Buy", + }, + { + hotkey: "W", + action: "/swap", + hotkeyDescription: "ALT+W", + description: "Swap", + }, + { + hotkey: "R", + action: "/accounts/menu/receive", + hotkeyDescription: "ALT+R", + description: "Receive funds", + }, + { + hotkey: "1", + action: () => updatePopupTab("activity"), + hotkeyDescription: "ALT+1", + description: "Switch to activity list", + }, + { + hotkey: "2", + action: () => updatePopupTab("assets"), + hotkeyDescription: "ALT+2", + description: "Switch to assets list", + }, + { + //Leave empty to show it on Hotkeys popup, but it is executed from ActivityAssetsView + hotkey: "N", + action: "", + hotkeyDescription: "", + description: "Add token (on assets)", + }, + { + hotkey: "G", + action: "", + hotkeyDescription: "", + description: "View gas prices", + }, + { + hotkey: "V", + action: "", + hotkeyDescription: "", + description: "View on explorer", + }, + ], + CTRLALT: [ + { + hotkey: "S", + action: "/settings", + hotkeyDescription: "CTRL+ALT+S", + description: "Settings", + }, + { + hotkey: "R", + action: "/accounts/menu/reset", + hotkeyDescription: "CTRL+ALT+R", + description: "Reset account", + }, + ], + CTRL: [], + }, + "/settings": { + ALT: [ + { + hotkey: "1", + action: "/accounts/menu", + hotkeyDescription: "ALT+1", + description: "Accounts", + }, + { + hotkey: "2", + action: "/settings/networks", + hotkeyDescription: "ALT+2", + description: "Networks", + }, + { + hotkey: "3", + action: "/settings/addressBook", + hotkeyDescription: "ALT+3", + description: "Address book", + }, + { + hotkey: "4", + action: "/settings/preferences", + hotkeyDescription: "ALT+4", + description: "Preferences", + }, + { + hotkey: "5", + action: "", + hotkeyDescription: "", + description: "Connect hardware wallet", + }, + { + hotkey: "6", + action: "/settings/about", + hotkeyDescription: "ALT+6", + description: "About", + }, + ], + CTRL: [], + CTRLALT: [], + }, + "/accounts": { + ALT: [ + { + hotkey: "C", + action: "/accounts/create", + hotkeyDescription: "ALT+C", + description: "New account", + }, + ], + CTRL: [], + CTRLALT: [], + }, + "/accounts/create": { + ALT: [ + { + hotkey: "C", + action: "/accounts/create/add", + hotkeyDescription: "ALT+C", + description: "New account", + }, + { + hotkey: "I", + action: "/accounts/create/import", + hotkeyDescription: "ALT+I", + description: "Import account", + }, + { + hotkey: "H", + action: "", + hotkeyDescription: "", + description: "Connect hardware wallet", + }, + ], + CTRL: [], + CTRLALT: [], + }, + "/accounts/menu": { + ALT: [ + { + hotkey: "1", + action: "/accounts/menu/receive", + hotkeyDescription: "ALT+1", + description: "Receive funds", + }, + { + hotkey: "2", + action: "/accounts/menu/connectedsites", + hotkeyDescription: "ALT+2", + description: "Connected sites", + }, + { + hotkey: "3", + action: "/accounts/menu/export", + hotkeyDescription: "ALT+3", + description: "Export", + }, + { + hotkey: "4", + action: "", + hotkeyDescription: "", + description: "View on explorer", + }, + { + hotkey: "5", + action: "/accounts/menu/allowances", + hotkeyDescription: "ALT+5", + description: "Token allowances", + }, + { + hotkey: "6", + action: "/accounts", + hotkeyDescription: "ALT+6", + description: "My accounts", + }, + { + hotkey: "7", + action: "/accounts/menu/tokensOrder", + hotkeyDescription: "ALT+7", + description: "Assets order", + }, + { + hotkey: "8", + action: "/accounts/menu/order", + hotkeyDescription: "ALT+8", + description: "Accounts order", + }, + { + hotkey: "9", + action: "/accounts/menu/reset", + hotkeyDescription: "ALT+9", + description: "Reset account", + }, + ], + CTRL: [], + CTRLALT: [], + }, + "/accounts/menu/allowances": { + ALT: [ + { + hotkey: "R", + action: "", + hotkeyDescription: "", + description: "Refresh allowances", + }, + { + hotkey: "S", + action: "", + hotkeyDescription: "", + description: "Group by spender", + }, + { + hotkey: "T", + action: "", + hotkeyDescription: "", + description: "Group by token", + }, + ], + CTRL: [], + CTRLALT: [], + }, + "/settings/networks": { + ALT: [ + { + hotkey: "N", + action: "/settings/networks/search", + hotkeyDescription: "ALT+N", + description: "Add network", + }, + ], + CTRL: [], + CTRLALT: [], + }, + "/settings/addressBook": { + ALT: [ + { + hotkey: "N", + action: "/settings/addressBook/add", + hotkeyDescription: "ALT+N", + description: "New contact", + }, + ], + CTRL: [], + CTRLALT: [], + }, + "/settings/preferences": { + ALT: [ + { + hotkey: "1", + action: "/settings/preferences/locktimeout", + hotkeyDescription: "ALT+1", + description: "Lock timeout", + }, + { + hotkey: "2", + action: "/settings/preferences/locale", + hotkeyDescription: "ALT+2", + description: "Locale configuration", + }, + { + hotkey: "3", + action: "/settings/preferences/releasenotes", + hotkeyDescription: "ALT+3", + description: "Release notes", + }, + { + hotkey: "4", + action: "/settings/preferences/defaultwallet", + hotkeyDescription: "ALT+4", + description: "Default wallet", + }, + { + hotkey: "5", + action: "/settings/preferences/defaultGas", + hotkeyDescription: "ALT+5", + description: "Default gas setting", + }, + { + hotkey: "6", + action: "/settings/preferences/notificationsAndWarnings", + hotkeyDescription: "ALT+6", + description: "Notifications & warnings", + }, + { + hotkey: "7", + action: "/settings/preferences/phishing", + hotkeyDescription: "ALT+7", + description: "Phishing protection", + }, + { + hotkey: "8", + action: "/settings/preferences/hotkeys", + hotkeyDescription: "ALT+8", + description: "Keyboard shortcuts", + }, + ], + CTRL: [], + CTRLALT: [], + }, +} + +export const componentsHotkeys = { + ActivityAssetsView: "ALT+N", + GasPricesInfo: "ALT+G,ENTER", + HotkeysCollapsedMessage: "ALT+K, ENTER", + AllowancesPage: "ALT+R, ALT+S, ALT+T", + AccountMenu: "ALT+4", + PopupPage: "ALT+V", + CreateAccountPage: "ALT+H", + SettingsPage: "ALT+5", +} + +export const getActionByHotkeyAndPath = ( + currentLocation: string, + hotkey: string, + handler: "CTRL" | "ALT" | "CTRLALT" +): Action => { + return locations[currentLocation][handler].filter( + (hotkeyAction) => + hotkeyAction.hotkey.toLowerCase() === hotkey.toLowerCase() + )[0].action +} + +export const getHotkeyByPath = (currentLocation: string) => { + if (!locations[currentLocation]) { + return "" + } + + const ctrlHotkeysByPath = locations[currentLocation]["CTRL"] + .map((hotkeyAction) => { + return hotkeyAction.hotkeyDescription + }) + .join(",") + + const altHotkeysByPath = locations[currentLocation]["ALT"] + .map((hotkeyAction) => { + return hotkeyAction.hotkeyDescription + }) + .join(",") + + const ctrlAltHotkeysByPath = locations[currentLocation]["CTRLALT"] + .map((hotkeyAction) => { + return hotkeyAction.hotkeyDescription + }) + .join(",") + + return ( + altHotkeysByPath + "," + ctrlHotkeysByPath + "," + ctrlAltHotkeysByPath + ) +} + +//Return boolean indicating if current path has hotkeys (checks by permissions) +export const useCheckLocationHotkeys = ( + permissions?: { [action: string]: boolean }, + currentLocation?: string +): boolean => { + const history = useOnMountHistory() + currentLocation = currentLocation ?? history.location.pathname + + if (!locations[currentLocation]) { + return false + } + + const hotkeyAndDesc = locations[currentLocation]["ALT"].map( + (hotkeyAction) => { + /// Will only add permissions logic to ALT and OPT. If any other permission appear it should be added to the handler key + if (permissions) { + if ( + permissions[ + currentLocation + + "/alt/" + + hotkeyAction.hotkey.toLowerCase() + ] === undefined || + permissions[ + currentLocation + + "/alt/" + + hotkeyAction.hotkey.toLowerCase() + ] + ) { + return hotkeyAction.description + } + } else { + return hotkeyAction.description + } + + return undefined + } + ) + + return ( + hotkeyAndDesc.length > 0 || + locations[currentLocation]["CTRL"].length > 0 || + locations[currentLocation]["CTRLALT"].length > 0 + ) +} + +export const useHotkeysByPath = (currentLocation?: string) => { + const history = useOnMountHistory() + currentLocation = currentLocation ?? history.location.pathname + + if (!locations[currentLocation]) { + return "" + } + + return locations[currentLocation] +} diff --git a/packages/ui/src/util/objectUtils.ts b/packages/ui/src/util/objectUtils.ts index 627ca3056..3f005275f 100644 --- a/packages/ui/src/util/objectUtils.ts +++ b/packages/ui/src/util/objectUtils.ts @@ -39,7 +39,7 @@ function existsKeyInObject(key: any, object: Object) { * @param defaultValue * @returns */ -function getValueByKey(object: T, key: keyof T, defaultValue: typeof object[keyof T]) { +function getValueByKey(object: T, key: keyof T, defaultValue: typeof object[keyof T]) { return existsKeyInObject(key, object) ? object[key] diff --git a/packages/ui/src/util/onrampUtils.ts b/packages/ui/src/util/onrampUtils.ts new file mode 100644 index 000000000..7c14d3fa8 --- /dev/null +++ b/packages/ui/src/util/onrampUtils.ts @@ -0,0 +1 @@ +export const ONRAMPER_API_KEY = "pk_prod_01GYCJHNRP0V65F272K4Z02JY0" diff --git a/packages/ui/src/util/searchUD.ts b/packages/ui/src/util/searchUD.ts index a4bfaa3e0..a06938834 100644 --- a/packages/ui/src/util/searchUD.ts +++ b/packages/ui/src/util/searchUD.ts @@ -12,6 +12,8 @@ export const supportedSuffixes = [ "888", "coin", "zil", + "polygon", + "blockchain" ] export const searchUD = async ( diff --git a/packages/ui/src/util/tokenUtils.ts b/packages/ui/src/util/tokenUtils.ts index 66a7aea93..b9f0571fd 100644 --- a/packages/ui/src/util/tokenUtils.ts +++ b/packages/ui/src/util/tokenUtils.ts @@ -1,3 +1,12 @@ +import { AccountTokenOrder } from "@block-wallet/background/controllers/AccountTrackerController" +import { TokenWithBalance } from "../context/hooks/useTokensList" +import { BigNumber } from "@ethersproject/bignumber" +import { Rates } from "@block-wallet/background/controllers/ExchangeRatesController" +import { formatRounded } from "./formatRounded" +import { formatUnits } from "ethers/lib/utils" +import { toCurrencyAmount } from "./formatCurrency" +import { getValueByKey } from "./objectUtils" + const native_token_address_reduced = "0x0" const native_token_address = "0x0000000000000000000000000000000000000000" @@ -30,3 +39,207 @@ export const isNativeTokenAddress = (address: string) => { compareAddresses(address, native_token_address) ) } + +export enum AssetsSortOptions { + NAME = "NAME", + BALANCE = "BALANCE", + USD_VALUE = "USD_VALUE", + STABLECOINS = "STABLECOINS", + CUSTOM = "CUSTOM", +} + +export const Stablecoins: string[] = [ + "USDT", + "USDC", + "DAI", + "BUSD", + "TUSD", + "USDD", + "USDP", + "XAUt", + "GUSD", + "JST", + "USTC", + "FRAX", + "USDJ", + "LUSD", + "EDGT", + "EURS", + "USDX", + "VAI", + "XSGD", + "CUSD", + "SUSD", + "EURt", + "FEI", + "RSV", + "USDK", + "OUSD", + "GYEN", + "KRT", + "CEUR", + "BIDR", + "HUSD", + "IDRT", + "DJED", + "XTN", + "STAKE", + "UNB", + "ZUSD", + "EOSDT", + "XTUSD", + "AGEUR", + "BRZ", + "BITCNY", + "HBD", + "DFD", + "XCHF", +] + +export const sortTokensByValue = ( + sortValue: AssetsSortOptions, + tokensList: TokenWithBalance[], + accountTokensOrder: AccountTokenOrder, + exchangeRates: Rates, + hideSmallBalances: boolean, + networkNativeCurrencySymbol: string +): TokenWithBalance[] => { + let accountTokens: TokenWithBalance[] = [] + + if (hideSmallBalances) { + accountTokens = tokensList.filter((token) => { + const isNativeCurrencyA = isNativeTokenAddress(token.token.address) + + const currencyAmountA = toCurrencyAmount( + BigNumber.from(token.balance), + getValueByKey( + exchangeRates, + isNativeCurrencyA + ? token.token.symbol.toUpperCase() + : token.token.symbol, + 0 + ), + token.token.decimals + ) + + return ( + currencyAmountA > 0.01 || + token.token.symbol.toLowerCase() === + networkNativeCurrencySymbol.toLowerCase() + ) + }) + } else accountTokens = [...tokensList] + + if (accountTokens.length > 1) { + switch (sortValue) { + case AssetsSortOptions.BALANCE: + accountTokens.sort((tokenA, tokenB) => { + const tokenABalance = Number( + formatRounded( + formatUnits( + tokenA.balance || "0", + tokenA.token.decimals + ), + 4 + ) + ) + const tokenBBalance = Number( + formatRounded( + formatUnits( + tokenB.balance || "0", + tokenB.token.decimals + ), + 4 + ) + ) + + return tokenABalance > tokenBBalance + ? -1 + : tokenBBalance > tokenABalance + ? 1 + : 0 + }) + break + case AssetsSortOptions.USD_VALUE: + accountTokens.sort((tokenA, tokenB) => { + const isNativeCurrencyA = isNativeTokenAddress( + tokenA.token.address + ) + const isNativeCurrencyB = isNativeTokenAddress( + tokenB.token.address + ) + const currencyAmountA = toCurrencyAmount( + BigNumber.from(tokenA.balance), + getValueByKey( + exchangeRates, + isNativeCurrencyA + ? tokenA.token.symbol.toUpperCase() + : tokenA.token.symbol, + 0 + ), + tokenA.token.decimals + ) + const currencyAmountB = toCurrencyAmount( + BigNumber.from(tokenB.balance), + getValueByKey( + exchangeRates, + isNativeCurrencyB + ? tokenB.token.symbol.toUpperCase() + : tokenB.token.symbol, + 0 + ), + tokenB.token.decimals + ) + + return currencyAmountA > currencyAmountB + ? -1 + : currencyAmountB > currencyAmountA + ? 1 + : 0 + }) + break + case AssetsSortOptions.NAME: + accountTokens.sort((tokenA, tokenB) => + tokenA.token.symbol > tokenB.token.symbol + ? 1 + : tokenB.token.symbol > tokenA.token.symbol + ? -1 + : 0 + ) + break + case AssetsSortOptions.STABLECOINS: + const tokens: TokenWithBalance[] = [] + Stablecoins.forEach((stablecoin) => { + const stableTokenIndex = accountTokens.findIndex( + (token) => token.token.symbol === stablecoin + ) + + if (stableTokenIndex !== -1) { + tokens.push(accountTokens[stableTokenIndex]) + accountTokens.splice(stableTokenIndex, 1) + } + }) + + if (tokens.length > 0) { + accountTokens.unshift(...tokens) + } + break + case AssetsSortOptions.CUSTOM: + if (accountTokensOrder) { + accountTokens.forEach((token) => { + token.token.order = + accountTokensOrder[token.token.address] ?? 0 + }) + + accountTokens.sort( + (a, b) => (a.token.order ?? 0) - (b.token.order ?? 0) + ) + } + + break + } + return accountTokens + } + + return accountTokens +} diff --git a/packages/ui/src/util/transactionUtils.ts b/packages/ui/src/util/transactionUtils.ts index 2fdb9dd44..015fbcc88 100644 --- a/packages/ui/src/util/transactionUtils.ts +++ b/packages/ui/src/util/transactionUtils.ts @@ -125,6 +125,27 @@ export const isTransactionOrRequestAwaitingSigning = ( return validStatuses.includes(status) } +export const isTransactionOrRequestRejected = ( + status: TransactionStatus | DappRequestSigningStatus, + trezorVendor: boolean +): boolean => { + return ( + [ + TransactionStatus.REJECTED, + DappRequestSigningStatus.REJECTED, + ].includes(status) || + (trezorVendor && + ![ + TransactionStatus.UNAPPROVED, + TransactionStatus.SUBMITTED, + TransactionStatus.CONFIRMED, + TransactionStatus.SIGNED, + DappRequestSigningStatus.SIGNED, + DappRequestSigningStatus.PENDING, + ].includes(status)) + ) +} + export const canUserSubmitTransaction = ( status: TransactionStatus ): boolean => { diff --git a/packages/ui/src/util/useEnterKey.hook.ts b/packages/ui/src/util/useEnterKey.hook.ts index 78931b659..3824401eb 100644 --- a/packages/ui/src/util/useEnterKey.hook.ts +++ b/packages/ui/src/util/useEnterKey.hook.ts @@ -12,5 +12,6 @@ export const useEnterKey = (submitCallback: Function) => { return () => { document.removeEventListener("keydown", listener) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []) } diff --git a/packages/ui/src/util/window.ts b/packages/ui/src/util/window.ts index bcf2cab61..3b9197968 100644 --- a/packages/ui/src/util/window.ts +++ b/packages/ui/src/util/window.ts @@ -1,9 +1,11 @@ +import browser from "webextension-polyfill" + /** * Checks for runtime error * */ const checkForError = () => { - const error = chrome.runtime.lastError + const error = browser.runtime.lastError if (!error) { return undefined } @@ -16,13 +18,13 @@ const checkForError = () => { */ export const closeCurrentTab = (): Promise => { return new Promise((resolve, reject) => { - chrome.tabs && - chrome.tabs.getCurrent((tab) => { + browser.tabs && + browser.tabs.getCurrent().then((tab) => { const error = checkForError() if (error) { reject(error) } - chrome.tabs.remove(tab?.id!, () => { + browser.tabs.remove(tab?.id!).then(() => { const error = checkForError() if (error) { reject(error) @@ -32,3 +34,27 @@ export const closeCurrentTab = (): Promise => { }) }) } + +export enum Browsers { + CHROME = "chrome", + OPERA = "opera", + FIREFOX = "firefox", +} + +/** + * Detects chromium based browsers. + * @returns true if the browser is chromium based + */ + +export function getBrowserInfo(): Browsers { + const userAgent = window.navigator.userAgent.toLowerCase() + + if (userAgent.indexOf("opera") !== -1 || userAgent.indexOf("opr") !== -1) + return Browsers.OPERA + + if (userAgent.indexOf("firefox") !== -1) return Browsers.FIREFOX + + if (userAgent.indexOf("chrome") !== -1) return Browsers.CHROME + + return Browsers.CHROME +} diff --git a/packages/ui/tailwind.config.js b/packages/ui/tailwind.config.js index 0ad5a3387..87c73bcd9 100644 --- a/packages/ui/tailwind.config.js +++ b/packages/ui/tailwind.config.js @@ -7,6 +7,31 @@ module.exports = { 100: "#E7F1FB", 200: "#D9E9FA", 300: "#1673FF", + grey: { + default: "#EEF1EF", + hover: "#E3E5E4", + disabled: "#F7F8F7", + dark: "#707270", + }, + black: { + default: "#08090A", + hover: "#292E33", + disabled: "#838484", + }, + blue: { + default: "#3742F7", + hover: "#2D37CC", + disabled: "#9BA0FB", + }, + }, + secondary: { + red: { + 100: "#FFECE5", + default: "#FF3E00", + }, + green: { + default: "#3CBF88", + }, }, gray: { 900: "#0A121E", @@ -53,7 +78,7 @@ module.exports = { gray: "#8093AB", danger: "red", black: theme("colors.black"), - selected: theme("colors.primary.300"), + selected: theme("colors.primary.blue.default"), }), stroke: () => ({ gray: "#8093AB", diff --git a/packages/ui/webpack/config/env.js b/packages/ui/webpack/config/env.js index 99f7d9793..36a7a8b13 100644 --- a/packages/ui/webpack/config/env.js +++ b/packages/ui/webpack/config/env.js @@ -90,8 +90,9 @@ function getClientEnvironment(publicUrl) { // which is why it's disabled by default. // It is defined here so it is available in the webpackHotDevClient. FAST_REFRESH: process.env.FAST_REFRESH !== "false", - VERSION: require("../../../../public/manifest.json").version, - VERSION_NAME: require("../../../../public/manifest.json").version_name, + VERSION: require("../../../../manifest/base.json").version, + VERSION_NAME: require("../../../../manifest/base.json") + .version_name, } ) // Stringify all values so we can feed into webpack DefinePlugin diff --git a/packages/ui/webpack/config/jest/babelTransform.js b/packages/ui/webpack/config/jest/babelTransform.js new file mode 100644 index 000000000..5b391e405 --- /dev/null +++ b/packages/ui/webpack/config/jest/babelTransform.js @@ -0,0 +1,29 @@ +'use strict'; + +const babelJest = require('babel-jest').default; + +const hasJsxRuntime = (() => { + if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') { + return false; + } + + try { + require.resolve('react/jsx-runtime'); + return true; + } catch (e) { + return false; + } +})(); + +module.exports = babelJest.createTransformer({ + presets: [ + [ + require.resolve('babel-preset-react-app'), + { + runtime: hasJsxRuntime ? 'automatic' : 'classic', + }, + ], + ], + babelrc: false, + configFile: false, +}); diff --git a/packages/ui/webpack/config/jest/jest.setup.js b/packages/ui/webpack/config/jest/jest.setup.js index 4d3fdd6db..c9eac8d13 100644 --- a/packages/ui/webpack/config/jest/jest.setup.js +++ b/packages/ui/webpack/config/jest/jest.setup.js @@ -1,3 +1,11 @@ -Object.assign(global, require('jest-chrome')) import 'jest-canvas-mock'; +const { chrome } = require('jest-chrome') +chrome.runtime.id = "testid"; +chrome.tabs.query = () => [] +chrome.runtime.sendMessage.mockImplementation( + (message, callback) => { + callback("response") + }, +) +Object.assign(global, { chrome }) diff --git a/packages/ui/webpack/config/paths.js b/packages/ui/webpack/config/paths.js index 319fcf2db..bf2746da2 100644 --- a/packages/ui/webpack/config/paths.js +++ b/packages/ui/webpack/config/paths.js @@ -21,6 +21,8 @@ const publicUrlOrPath = getPublicUrlOrPath( process.env.PUBLIC_URL ); +const buildPath = process.env.BUILD_PATH || 'build'; + const moduleFileExtensions = [ 'web.mjs', 'mjs', @@ -54,6 +56,8 @@ module.exports = { appPath: resolveApp('.'), appBuild: resolveApp('build'), appPublic: resolveApp('../../public'), + appScripts: resolveApp('../../scripts'), + appManifest: resolveApp('../../manifest'), appHtml: resolveApp('../../public/index.html'), appIndexJs: resolveModule(resolveApp, 'src/index'), appPackageJson: resolveApp('package.json'), @@ -64,6 +68,8 @@ module.exports = { testsSetup: resolveModule(resolveApp, 'src/setupTests'), proxySetup: resolveApp('src/setupProxy.js'), appNodeModules: resolveApp('node_modules'), + appWebpackCache: resolveApp('node_modules/.cache'), + appTsBuildInfoFile: resolveApp('node_modules/.cache/tsconfig.tsbuildinfo'), swSrc: resolveModule(resolveApp, 'src/service-worker'), publicUrlOrPath, }; diff --git a/packages/ui/webpack/config/webpack.config.js b/packages/ui/webpack/config/webpack.config.js index 92751a204..1253719b2 100644 --- a/packages/ui/webpack/config/webpack.config.js +++ b/packages/ui/webpack/config/webpack.config.js @@ -4,18 +4,15 @@ const fs = require("fs") const path = require("path") const webpack = require("webpack") const resolve = require("resolve") -const PnpWebpackPlugin = require("pnp-webpack-plugin") const HtmlWebpackPlugin = require("html-webpack-plugin") const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin") const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin") const TerserPlugin = require("terser-webpack-plugin") const MiniCssExtractPlugin = require("mini-css-extract-plugin") -const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin") -const safePostCssParser = require("postcss-safe-parser") -const ManifestPlugin = require("webpack-manifest-plugin") +const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") +const { WebpackManifestPlugin } = require("webpack-manifest-plugin") const InterpolateHtmlPlugin = require("react-dev-utils/InterpolateHtmlPlugin") const WorkboxWebpackPlugin = require("workbox-webpack-plugin") -const WatchMissingNodeModulesPlugin = require("react-dev-utils/WatchMissingNodeModulesPlugin") const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin") const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent") const ESLintPlugin = require("eslint-webpack-plugin") @@ -23,28 +20,37 @@ const paths = require("./paths") const modules = require("./modules") const getClientEnvironment = require("./env") const ModuleNotFoundPlugin = require("react-dev-utils/ModuleNotFoundPlugin") -const ForkTsCheckerWebpackPlugin = require("react-dev-utils/ForkTsCheckerWebpackPlugin") -const typescriptFormatter = require("react-dev-utils/typescriptFormatter") +const ForkTsCheckerWebpackPlugin = + process.env.TSC_COMPILE_ON_ERROR === "true" + ? require("react-dev-utils/ForkTsCheckerWarningWebpackPlugin") + : require("react-dev-utils/ForkTsCheckerWebpackPlugin") const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin") -const postcssNormalize = require("postcss-normalize") - -const appPackageJson = require(paths.appPackageJson) +const createEnvironmentHash = require("./webpack/persistentCache/createEnvironmentHash") // Source maps are resource heavy and can cause out of memory issue for large source files. const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false" -const webpackDevClientEntry = require.resolve( - "react-dev-utils/webpackHotDevClient" +const reactRefreshRuntimeEntry = require.resolve("react-refresh/runtime") +const reactRefreshWebpackPluginRuntimeEntry = require.resolve( + "@pmmmwh/react-refresh-webpack-plugin" ) -const reactRefreshOverlayEntry = require.resolve( - "react-dev-utils/refreshOverlayInterop" +const babelRuntimeEntry = require.resolve("babel-preset-react-app") +const babelRuntimeEntryHelpers = require.resolve( + "@babel/runtime/helpers/esm/assertThisInitialized", + { paths: [babelRuntimeEntry] } ) +const babelRuntimeRegenerator = require.resolve("@babel/runtime/regenerator", { + paths: [babelRuntimeEntry], +}) // Some apps do not need the benefits of saving a web request, so not inlining the chunk // makes for a smoother build process. const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== "false" +const emitErrorsAsWarnings = process.env.ESLINT_NO_DEV_ERRORS === "true" +const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === "true" + const imageInlineSizeLimit = parseInt( process.env.IMAGE_INLINE_SIZE_LIMIT || "10000" ) @@ -52,6 +58,11 @@ const imageInlineSizeLimit = parseInt( // Check if TypeScript is setup const useTypeScript = fs.existsSync(paths.appTsConfig) +// Check if Tailwind config exists +const useTailwind = fs.existsSync( + path.join(paths.appPath, "tailwind.config.js") +) + // Get the path to the uncompiled service worker (if it exists). const swSrc = paths.swSrc @@ -115,22 +126,42 @@ module.exports = function (webpackEnv) { // package.json loader: require.resolve("postcss-loader"), options: { - // Necessary for external CSS imports to work - // https://github.com/facebook/create-react-app/issues/2677 - ident: "postcss", - plugins: () => [ - require("postcss-flexbugs-fixes"), - require("postcss-preset-env")({ - autoprefixer: { - flexbox: "no-2009", - }, - stage: 3, - }), - // Adds PostCSS Normalize as the reset css with default options, - // so that it honors browserslist config in package.json - // which in turn let's users customize the target behavior as per their needs. - postcssNormalize(), - ], + postcssOptions: { + // Necessary for external CSS imports to work + // https://github.com/facebook/create-react-app/issues/2677 + ident: "postcss", + config: false, + plugins: !useTailwind + ? [ + "postcss-flexbugs-fixes", + [ + "postcss-preset-env", + { + autoprefixer: { + flexbox: "no-2009", + }, + stage: 3, + }, + ], + // Adds PostCSS Normalize as the reset css with default options, + // so that it honors browserslist config in package.json + // which in turn let's users customize the target behavior as per their needs. + "postcss-normalize", + ] + : [ + "tailwindcss", + "postcss-flexbugs-fixes", + [ + "postcss-preset-env", + { + autoprefixer: { + flexbox: "no-2009", + }, + stage: 3, + }, + ], + ], + }, sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, @@ -160,6 +191,9 @@ module.exports = function (webpackEnv) { } return { + target: ["browserslist"], + // Webpack noise constrained to errors and warnings + stats: "errors-warnings", mode: isEnvProduction ? "production" : isEnvDevelopment && "development", @@ -172,34 +206,10 @@ module.exports = function (webpackEnv) { : isEnvDevelopment && "cheap-module-source-map", // These are the "entry points" to our application. // This means they will be the "root" imports that are included in JS bundle. - entry: - isEnvDevelopment && !shouldUseReactRefresh - ? [ - // Include an alternative client for WebpackDevServer. A client's job is to - // connect to WebpackDevServer by a socket and get notified about changes. - // When you save a file, the client will either apply hot updates (in case - // of CSS changes), or refresh the page (in case of JS changes). When you - // make a syntax error, this client will display a syntax error overlay. - // Note: instead of the default WebpackDevServer client, we use a custom one - // to bring better experience for Create React App users. You can replace - // the line below with these two lines if you prefer the stock client: - // - // require.resolve('webpack-dev-server/client') + '?/', - // require.resolve('webpack/hot/dev-server'), - // - // When using the experimental react-refresh integration, - // the webpack plugin takes care of injecting the dev client for us. - webpackDevClientEntry, - // Finally, this is your app's code: - paths.appIndexJs, - // We include the app code last so that if there is a runtime error during - // initialization, it doesn't blow up the WebpackDevServer client, and - // changing JS code would still trigger a refresh. - ] - : paths.appIndexJs, + entry: paths.appIndexJs, output: { // The build folder. - path: isEnvProduction ? paths.appBuild : undefined, + path: paths.appBuild, // Add /* filename */ comments to generated require()s in the output. pathinfo: isEnvDevelopment, // There will be one main bundle, and one file per asynchronous chunk. @@ -207,11 +217,11 @@ module.exports = function (webpackEnv) { filename: isEnvProduction ? "static/js/[name].[contenthash:8].js" : isEnvDevelopment && "static/js/bundle.js", - futureEmitAssets: true, // There are also additional JS chunk files if you use code splitting. chunkFilename: isEnvProduction ? "static/js/[name].[contenthash:8].chunk.js" : isEnvDevelopment && "static/js/[name].chunk.js", + assetModuleFilename: "static/media/[name].[hash][ext]", // webpack uses `publicPath` to determine where the app is being served from. // It requires a trailing slash, or the file assets will get an incorrect path. // We inferred the "public path" (such as / or /my-project) from homepage. @@ -219,22 +229,33 @@ module.exports = function (webpackEnv) { // Point sourcemap entries to original disk location (format as URL on Windows) devtoolModuleFilenameTemplate: isEnvProduction ? (info) => - path - .relative(paths.appSrc, info.absoluteResourcePath) - .replace(/\\/g, "/") + path + .relative(paths.appSrc, info.absoluteResourcePath) + .replace(/\\/g, "/") : isEnvDevelopment && - ((info) => - path - .resolve(info.absoluteResourcePath) - .replace(/\\/g, "/")), - // Prevents conflicts when multiple webpack runtimes (from different apps) - // are used on the same page. - jsonpFunction: `webpackJsonp${appPackageJson.name}`, - // this defaults to 'window', but by setting it to 'this' then - // module chunks which are built will work in web workers as well. - globalObject: "this", + ((info) => + path + .resolve(info.absoluteResourcePath) + .replace(/\\/g, "/")), + }, + cache: { + type: "filesystem", + version: createEnvironmentHash(env.raw), + cacheDirectory: paths.appWebpackCache, + store: "pack", + buildDependencies: { + defaultWebpack: ["webpack/lib/"], + config: [__filename], + tsconfig: [paths.appTsConfig, paths.appJsConfig].filter((f) => + fs.existsSync(f) + ), + }, + }, + infrastructureLogging: { + level: "none", }, optimization: { + usedExports: true, minimize: isEnvProduction, minimizer: [ // This is only used in production mode @@ -276,43 +297,15 @@ module.exports = function (webpackEnv) { ascii_only: true, }, }, - sourceMap: shouldUseSourceMap, }), // This is only used in production mode - new OptimizeCSSAssetsPlugin({ - cssProcessorOptions: { - parser: safePostCssParser, - map: shouldUseSourceMap - ? { - // `inline: false` forces the sourcemap to be output into a - // separate file - inline: false, - // `annotation: true` appends the sourceMappingURL to the end of - // the css file, helping the browser find the sourcemap - annotation: true, - } - : false, - }, - cssProcessorPluginOptions: { - preset: [ - "default", - { minifyFontValues: { removeQuotes: false } }, - ], - }, - }), + new CssMinimizerPlugin(), ], // Automatically split vendor and commons // https://twitter.com/wSokra/status/969633336732905474 // https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366 splitChunks: { chunks: "all", - name: false, - }, - // Keep the runtime chunk separated to enable long term caching - // https://twitter.com/wSokra/status/969679223278505985 - // https://github.com/facebook/create-react-app/issues/5358 - runtimeChunk: { - name: (entrypoint) => `runtime-${entrypoint.name}`, }, }, resolve: { @@ -342,12 +335,8 @@ module.exports = function (webpackEnv) { "scheduler/tracing": "scheduler/tracing-profiling", }), ...(modules.webpackAliases || {}), - // '@extension/background': path.resolve(__dirname, "./packages/background/src") }, plugins: [ - // Adds support for installing with Plug'n'Play, leading to faster installs and adding - // guards against forgotten dependencies and such. - PnpWebpackPlugin, // Prevents users from importing files from outside of src/ (or node_modules/). // This often causes confusion because we only process files within src/ with babel. // To fix this, we prevent you from importing files out of src/ -- if you'd like to, @@ -355,35 +344,50 @@ module.exports = function (webpackEnv) { // Make sure your source files are compiled, as they will not be processed in any way. new ModuleScopePlugin(paths.appSrc, [ paths.appPackageJson, - reactRefreshOverlayEntry, + reactRefreshRuntimeEntry, + reactRefreshWebpackPluginRuntimeEntry, + babelRuntimeEntry, + babelRuntimeEntryHelpers, + babelRuntimeRegenerator, ]), ], - }, - resolveLoader: { - plugins: [ - // Also related to Plug'n'Play, but this time it tells webpack to load its loaders - // from the current package. - PnpWebpackPlugin.moduleLoader(module), - ], + fallback: { + assert: require.resolve("assert"), + stream: require.resolve("stream-browserify"), + crypto: require.resolve("crypto-browserify"), + }, }, module: { strictExportPresence: true, rules: [ - // Disable require.ensure as it's not a standard language feature. - { parser: { requireEnsure: false } }, + { + test: /\.m?js$/, + resolve: { + fullySpecified: false, + }, + }, + // Handle node_modules packages that contain sourcemaps + shouldUseSourceMap && { + enforce: "pre", + exclude: /@babel(?:\/|\\{1,2})runtime/, + test: /\.(js|mjs|jsx|ts|tsx|css)$/, + loader: require.resolve("source-map-loader"), + }, { // "oneOf" will traverse all following loaders until one will // match the requirements. When no loader matches it will fall // back to the "file" loader at the end of the loader list. oneOf: [ + // TODO: Merge this config once `image/avif` is in the mime-db // https://github.com/jshttp/mime-db { test: [/\.avif$/], - loader: require.resolve("url-loader"), - options: { - limit: imageInlineSizeLimit, - mimetype: "image/avif", - name: "static/media/[name].[hash:8].[ext]", + type: "asset", + mimetype: "image/avif", + parser: { + dataUrlCondition: { + maxSize: imageInlineSizeLimit, + }, }, }, // "url" loader works like "file" loader except that it embeds assets @@ -391,10 +395,37 @@ module.exports = function (webpackEnv) { // A missing `test` is equivalent to a match. { test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], - loader: require.resolve("url-loader"), - options: { - limit: imageInlineSizeLimit, - name: "static/media/[name].[hash:8].[ext]", + type: "asset", + parser: { + dataUrlCondition: { + maxSize: imageInlineSizeLimit, + }, + }, + }, + { + test: /\.svg$/, + use: [ + { + loader: require.resolve("@svgr/webpack"), + options: { + prettier: false, + svgo: false, + svgoConfig: { + plugins: [{ removeViewBox: false }], + }, + titleProp: true, + ref: true, + }, + }, + { + loader: require.resolve("file-loader"), + options: { + name: "static/media/[name].[hash].[ext]", + }, + }, + ], + issuer: { + and: [/\.(ts|tsx|js|jsx|md|mdx)$/], }, }, // Process application JS with Babel. @@ -421,22 +452,9 @@ module.exports = function (webpackEnv) { ], plugins: [ - [ - require.resolve( - "babel-plugin-named-asset-import" - ), - { - loaderMap: { - svg: { - ReactComponent: - "@svgr/webpack?-svgo,+titleProp,+ref![path]", - }, - }, - }, - ], isEnvDevelopment && - shouldUseReactRefresh && - require.resolve("react-refresh/babel"), + shouldUseReactRefresh && + require.resolve("react-refresh/babel"), ].filter(Boolean), // This is a feature of `babel-loader` for webpack (not Babel itself). // It enables caching results in ./node_modules/.cache/babel-loader/ @@ -491,6 +509,9 @@ module.exports = function (webpackEnv) { sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, + modules: { + mode: "icss", + }, }), // Don't consider CSS imports dead code even if the // containing package claims to have no side effects. @@ -508,6 +529,7 @@ module.exports = function (webpackEnv) { ? shouldUseSourceMap : isEnvDevelopment, modules: { + mode: "local", getLocalIdent: getCSSModuleLocalIdent, }, }), @@ -524,6 +546,9 @@ module.exports = function (webpackEnv) { sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, + modules: { + mode: "icss", + }, }, "sass-loader" ), @@ -544,6 +569,7 @@ module.exports = function (webpackEnv) { ? shouldUseSourceMap : isEnvDevelopment, modules: { + mode: "local", getLocalIdent: getCSSModuleLocalIdent, }, }, @@ -556,27 +582,32 @@ module.exports = function (webpackEnv) { // This loader doesn't use a "test" so it will catch all modules // that fall through the other loaders. { - loader: require.resolve("file-loader"), // Exclude `js` files to keep "css" loader working as it injects // its runtime that would otherwise be processed through "file" loader. // Also exclude `html` and `json` extensions so they get processed // by webpacks internal loaders. exclude: [ + /^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/, ], - options: { - name: "static/media/[name].[hash:8].[ext]", - }, + type: "asset/resource", }, // ** STOP ** Are you adding a new loader? // Make sure to add the new loader(s) before the "file" loader. ], }, - ], + ].filter(Boolean), }, plugins: [ + new webpack.ProvidePlugin({ + process: "process/browser", + Buffer: ["buffer", "Buffer"], + }), + new webpack.ProvidePlugin({ + process: "process/browser", + }), // Generates an `index.html` file with the

    qEIEEraMLk+K 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 z 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� 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 z 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 literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/images/icons/about.svg b/packages/ui/src/assets/images/icons/about.svg index 8dd7332c4..75aebdefb 100644 --- a/packages/ui/src/assets/images/icons/about.svg +++ b/packages/ui/src/assets/images/icons/about.svg @@ -1,5 +1,12 @@ - - - + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/account_add.svg b/packages/ui/src/assets/images/icons/account_add.svg index a3512f381..9692a5189 100644 --- a/packages/ui/src/assets/images/icons/account_add.svg +++ b/packages/ui/src/assets/images/icons/account_add.svg @@ -1,12 +1,12 @@ - - - - - + + + + + - + diff --git a/packages/ui/src/assets/images/icons/accounts.svg b/packages/ui/src/assets/images/icons/accounts.svg index 5a18ded1e..39201afa7 100644 --- a/packages/ui/src/assets/images/icons/accounts.svg +++ b/packages/ui/src/assets/images/icons/accounts.svg @@ -1,13 +1,12 @@ - - - - - - + + + + + - + diff --git a/packages/ui/src/assets/images/icons/accounts_order.svg b/packages/ui/src/assets/images/icons/accounts_order.svg new file mode 100644 index 000000000..f36b1eb41 --- /dev/null +++ b/packages/ui/src/assets/images/icons/accounts_order.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/assets_order.svg b/packages/ui/src/assets/images/icons/assets_order.svg new file mode 100644 index 000000000..536547742 --- /dev/null +++ b/packages/ui/src/assets/images/icons/assets_order.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/bell.svg b/packages/ui/src/assets/images/icons/bell.svg index 5c9c3de7b..ab7d96081 100644 --- a/packages/ui/src/assets/images/icons/bell.svg +++ b/packages/ui/src/assets/images/icons/bell.svg @@ -1,7 +1,11 @@ - - - - - - + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/bluecircle_skeleton.json b/packages/ui/src/assets/images/icons/bluecircle_skeleton.json index 7e2c1b348..0c9cf663c 100644 --- a/packages/ui/src/assets/images/icons/bluecircle_skeleton.json +++ b/packages/ui/src/assets/images/icons/bluecircle_skeleton.json @@ -1 +1,135 @@ -{"v":"5.7.13","fr":60,"ip":0,"op":120,"w":14,"h":14,"nm":"Bluecircle1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Ellipse 288","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-180,"ix":10},"p":{"a":0,"k":[7,7,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-107,-107,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[13.027,13.027],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":5,"k":{"a":0,"k":[0,0.504,0.702,1,0.242,0.621,0.772,1,0.484,0.738,0.842,1,0.742,0.622,0.772,1,1,0.506,0.702,1],"ix":9}},"s":{"a":1,"k":[{"i":{"x":0.348,"y":1},"o":{"x":0.531,"y":0},"t":0,"s":[-10.451,0.598],"to":[0,0],"ti":[0,0]},{"i":{"x":0.348,"y":1},"o":{"x":0.531,"y":0},"t":60,"s":[-1.013,0.473],"to":[0,0],"ti":[0,0]},{"t":120,"s":[-10.451,0.598]}],"ix":5},"e":{"a":1,"k":[{"i":{"x":0.348,"y":1},"o":{"x":0.531,"y":0},"t":0,"s":[0.451,0.624],"to":[0,0],"ti":[0,0]},{"i":{"x":0.348,"y":1},"o":{"x":0.531,"y":0},"t":60,"s":[9.701,0.436],"to":[0,0],"ti":[0,0]},{"t":120,"s":[0.451,0.624]}],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 288","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]} \ No newline at end of file +{ + "v": "5.7.13", + "fr": 60, + "ip": 0, + "op": 120, + "w": 14, + "h": 14, + "nm": "Bluecircle1", + "ddd": 0, + "assets": [], + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 4, + "nm": "Ellipse 288", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 0, "k": -180, "ix": 10 }, + "p": { "a": 0, "k": [7, 7, 0], "ix": 2, "l": 2 }, + "a": { "a": 0, "k": [0, 0, 0], "ix": 1, "l": 2 }, + "s": { "a": 0, "k": [-107, -107, 100], "ix": 6, "l": 2 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "d": 1, + "ty": "el", + "s": { "a": 0, "k": [13.027, 13.027], "ix": 2 }, + "p": { "a": 0, "k": [0, 0], "ix": 3 }, + "nm": "Ellipse Path 1", + "mn": "ADBE Vector Shape - Ellipse", + "hd": false + }, + { + "ty": "gf", + "o": { "a": 0, "k": 100, "ix": 10 }, + "r": 1, + "bm": 0, + "g": { + "p": 5, + "k": { + "a": 0, + "k": [ + 0, 0.504, 0.702, 1, 0.242, 0.621, 0.772, + 1, 0.484, 0.738, 0.842, 1, 0.742, 0.622, + 0.772, 1, 1, 0.506, 0.702, 1 + ], + "ix": 9 + } + }, + "s": { + "a": 1, + "k": [ + { + "i": { "x": 0.348, "y": 1 }, + "o": { "x": 0.531, "y": 0 }, + "t": 0, + "s": [-10.451, 0.598], + "to": [0, 0], + "ti": [0, 0] + }, + { + "i": { "x": 0.348, "y": 1 }, + "o": { "x": 0.531, "y": 0 }, + "t": 60, + "s": [-1.013, 0.473], + "to": [0, 0], + "ti": [0, 0] + }, + { "t": 120, "s": [-10.451, 0.598] } + ], + "ix": 5 + }, + "e": { + "a": 1, + "k": [ + { + "i": { "x": 0.348, "y": 1 }, + "o": { "x": 0.531, "y": 0 }, + "t": 0, + "s": [0.451, 0.624], + "to": [0, 0], + "ti": [0, 0] + }, + { + "i": { "x": 0.348, "y": 1 }, + "o": { "x": 0.531, "y": 0 }, + "t": 60, + "s": [9.701, 0.436], + "to": [0, 0], + "ti": [0, 0] + }, + { "t": 120, "s": [0.451, 0.624] } + ], + "ix": 6 + }, + "t": 1, + "nm": "Gradient Fill 1", + "mn": "ADBE Vector Graphic - G-Fill", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [0, 0], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "Ellipse 288", + "np": 2, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 600, + "st": 0, + "bm": 0 + } + ], + "markers": [] +} diff --git a/packages/ui/src/assets/images/icons/book.svg b/packages/ui/src/assets/images/icons/book.svg index d62469e8e..89e5f4613 100644 --- a/packages/ui/src/assets/images/icons/book.svg +++ b/packages/ui/src/assets/images/icons/book.svg @@ -1,7 +1,14 @@ - - - - - + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/bridge.json b/packages/ui/src/assets/images/icons/bridge.json index 17b0a2997..5393e7968 100644 --- a/packages/ui/src/assets/images/icons/bridge.json +++ b/packages/ui/src/assets/images/icons/bridge.json @@ -66,7 +66,7 @@ "ty": "fl", "c": { "a": 0, - "k": [0.087499976158, 0.452499866486, 1, 1], + "k": [0.21568627451, 0.25882352941, 0.96862745098, 1], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, @@ -159,7 +159,7 @@ "ty": "fl", "c": { "a": 0, - "k": [0.087499976158, 0.452499866486, 1, 1], + "k": [0.21568627451, 0.25882352941, 0.96862745098, 1], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, @@ -595,7 +595,7 @@ "ty": "fl", "c": { "a": 0, - "k": [0.087499976158, 0.452499866486, 1, 1], + "k": [0.21568627451, 0.25882352941, 0.96862745098, 1], "ix": 4 }, "o": { "a": 0, "k": 100, "ix": 5 }, diff --git a/packages/ui/src/assets/images/icons/cash.svg b/packages/ui/src/assets/images/icons/cash.svg new file mode 100644 index 000000000..b2592ba2e --- /dev/null +++ b/packages/ui/src/assets/images/icons/cash.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/images/icons/connected_sites.svg b/packages/ui/src/assets/images/icons/connected_sites.svg index f91656c54..0b8ca36b5 100644 --- a/packages/ui/src/assets/images/icons/connected_sites.svg +++ b/packages/ui/src/assets/images/icons/connected_sites.svg @@ -1,4 +1,11 @@ - - + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/cross.svg b/packages/ui/src/assets/images/icons/cross.svg index e871c9dca..87c61f3b0 100644 --- a/packages/ui/src/assets/images/icons/cross.svg +++ b/packages/ui/src/assets/images/icons/cross.svg @@ -1,7 +1,7 @@ - - + + - - + + diff --git a/packages/ui/src/assets/images/icons/export.svg b/packages/ui/src/assets/images/icons/export.svg index 1839fab8c..18258713c 100644 --- a/packages/ui/src/assets/images/icons/export.svg +++ b/packages/ui/src/assets/images/icons/export.svg @@ -1,5 +1,12 @@ - - - + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/eye_close.svg b/packages/ui/src/assets/images/icons/eye_close.svg index 584f72038..3c421fbe6 100644 --- a/packages/ui/src/assets/images/icons/eye_close.svg +++ b/packages/ui/src/assets/images/icons/eye_close.svg @@ -1,4 +1,4 @@ - - + + diff --git a/packages/ui/src/assets/images/icons/eye_open.svg b/packages/ui/src/assets/images/icons/eye_open.svg index fbc3e82bc..6d2e645da 100644 --- a/packages/ui/src/assets/images/icons/eye_open.svg +++ b/packages/ui/src/assets/images/icons/eye_open.svg @@ -1,3 +1,3 @@ - + diff --git a/packages/ui/src/assets/images/icons/filter.svg b/packages/ui/src/assets/images/icons/filter.svg new file mode 100644 index 000000000..5a9cddaed --- /dev/null +++ b/packages/ui/src/assets/images/icons/filter.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/images/icons/gas.svg b/packages/ui/src/assets/images/icons/gas.svg index 0f786c2f3..9c5438ef6 100644 --- a/packages/ui/src/assets/images/icons/gas.svg +++ b/packages/ui/src/assets/images/icons/gas.svg @@ -1,11 +1,11 @@ - - - - + + + + - + diff --git a/packages/ui/src/assets/images/icons/hotkeys.svg b/packages/ui/src/assets/images/icons/hotkeys.svg new file mode 100644 index 000000000..c58df19bf --- /dev/null +++ b/packages/ui/src/assets/images/icons/hotkeys.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/import_seed.svg b/packages/ui/src/assets/images/icons/import_seed.svg new file mode 100644 index 000000000..0f79e288f --- /dev/null +++ b/packages/ui/src/assets/images/icons/import_seed.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/key.svg b/packages/ui/src/assets/images/icons/key.svg new file mode 100644 index 000000000..70230d84e --- /dev/null +++ b/packages/ui/src/assets/images/icons/key.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/keystone.svg b/packages/ui/src/assets/images/icons/keystone.svg new file mode 100644 index 000000000..3c0367302 --- /dev/null +++ b/packages/ui/src/assets/images/icons/keystone.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/ui/src/assets/images/icons/logout.svg b/packages/ui/src/assets/images/icons/logout.svg index 469b83dcd..fe3dd2b64 100644 --- a/packages/ui/src/assets/images/icons/logout.svg +++ b/packages/ui/src/assets/images/icons/logout.svg @@ -1,7 +1,7 @@ - + diff --git a/packages/ui/src/assets/images/icons/new_account.svg b/packages/ui/src/assets/images/icons/new_account.svg new file mode 100644 index 000000000..97e549571 --- /dev/null +++ b/packages/ui/src/assets/images/icons/new_account.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/news.svg b/packages/ui/src/assets/images/icons/news.svg index 2be53e13c..eea7eccc6 100644 --- a/packages/ui/src/assets/images/icons/news.svg +++ b/packages/ui/src/assets/images/icons/news.svg @@ -1 +1,12 @@ - \ No newline at end of file + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/no_camera.svg b/packages/ui/src/assets/images/icons/no_camera.svg new file mode 100644 index 000000000..c4c51f266 --- /dev/null +++ b/packages/ui/src/assets/images/icons/no_camera.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/images/icons/onramper.svg b/packages/ui/src/assets/images/icons/onramper.svg new file mode 100644 index 000000000..f382dd30f --- /dev/null +++ b/packages/ui/src/assets/images/icons/onramper.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/images/icons/open_external.svg b/packages/ui/src/assets/images/icons/open_external.svg index 82d9d8d38..e2a66227f 100644 --- a/packages/ui/src/assets/images/icons/open_external.svg +++ b/packages/ui/src/assets/images/icons/open_external.svg @@ -1,5 +1,12 @@ - - - + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/order.svg b/packages/ui/src/assets/images/icons/order.svg new file mode 100644 index 000000000..e479d8a56 --- /dev/null +++ b/packages/ui/src/assets/images/icons/order.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/ui/src/assets/images/icons/pin.svg b/packages/ui/src/assets/images/icons/pin.svg index 2ee05a64f..77430a8e4 100644 --- a/packages/ui/src/assets/images/icons/pin.svg +++ b/packages/ui/src/assets/images/icons/pin.svg @@ -1,12 +1,4 @@ - - - - - - - - - - \ No newline at end of file + + + + diff --git a/packages/ui/src/assets/images/icons/plane.svg b/packages/ui/src/assets/images/icons/plane.svg index 1d04c94e7..0f411e347 100644 --- a/packages/ui/src/assets/images/icons/plane.svg +++ b/packages/ui/src/assets/images/icons/plane.svg @@ -1,4 +1,4 @@ - - + + diff --git a/packages/ui/src/assets/images/icons/refresh.svg b/packages/ui/src/assets/images/icons/refresh.svg index 375ae3184..94cbb6bc9 100644 --- a/packages/ui/src/assets/images/icons/refresh.svg +++ b/packages/ui/src/assets/images/icons/refresh.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/packages/ui/src/assets/images/icons/safe.svg b/packages/ui/src/assets/images/icons/safe.svg new file mode 100644 index 000000000..afa0c3624 --- /dev/null +++ b/packages/ui/src/assets/images/icons/safe.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/search.svg b/packages/ui/src/assets/images/icons/search.svg index 8e1524cec..941595aea 100644 --- a/packages/ui/src/assets/images/icons/search.svg +++ b/packages/ui/src/assets/images/icons/search.svg @@ -1,3 +1,3 @@ - + diff --git a/packages/ui/src/assets/images/icons/searchnotfound.svg b/packages/ui/src/assets/images/icons/searchnotfound.svg new file mode 100644 index 000000000..89f7a31e8 --- /dev/null +++ b/packages/ui/src/assets/images/icons/searchnotfound.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/ui/src/assets/images/icons/shield.svg b/packages/ui/src/assets/images/icons/shield.svg index 17d56fa44..e52104725 100644 --- a/packages/ui/src/assets/images/icons/shield.svg +++ b/packages/ui/src/assets/images/icons/shield.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + diff --git a/packages/ui/src/assets/images/icons/spanner.svg b/packages/ui/src/assets/images/icons/spanner.svg index 6034adb98..e8b834614 100644 --- a/packages/ui/src/assets/images/icons/spanner.svg +++ b/packages/ui/src/assets/images/icons/spanner.svg @@ -1 +1,15 @@ - \ No newline at end of file + + + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/switch.svg b/packages/ui/src/assets/images/icons/switch.svg new file mode 100644 index 000000000..7e0ae334b --- /dev/null +++ b/packages/ui/src/assets/images/icons/switch.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ui/src/assets/images/icons/usb.svg b/packages/ui/src/assets/images/icons/usb.svg index 83c6f7a95..d19b7a64b 100644 --- a/packages/ui/src/assets/images/icons/usb.svg +++ b/packages/ui/src/assets/images/icons/usb.svg @@ -1,8 +1,8 @@ - - - - - - + + + + + + diff --git a/packages/ui/src/assets/images/icons/wallet.json b/packages/ui/src/assets/images/icons/wallet.json new file mode 100644 index 000000000..59cc72157 --- /dev/null +++ b/packages/ui/src/assets/images/icons/wallet.json @@ -0,0 +1 @@ +{"v":"5.7.13","fr":60,"ip":0,"op":86,"w":32,"h":32,"nm":"Walleticon","ddd":0,"assets":[{"id":"comp_0","nm":"monyy","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Rectangle 1746","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[-29]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":98,"s":[0]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":109,"s":[0]},{"t":146,"s":[-29]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[74.881,97.525,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":98,"s":[75.381,64.025,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":109,"s":[75.381,64.025,0],"to":[0,0,0],"ti":[0,0,0]},{"t":146,"s":[74.881,97.525,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[5.974,9.957],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.215686276555,0.258823543787,0.96862745285,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[500,500],"ix":3},"r":{"a":0,"k":30,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1746","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":720,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Rectangle 1747","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[-29]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":98,"s":[0]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":109,"s":[0]},{"t":146,"s":[-29]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":60,"s":[81.118,110.697,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":98,"s":[87.618,71.572,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":109,"s":[87.618,71.572,0],"to":[0,0,0],"ti":[0,0,0]},{"t":146,"s":[81.118,110.697,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[5.974,8.827],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.215686276555,0.258823543787,0.96862745285,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[500,500],"ix":3},"r":{"a":0,"k":58.571,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1747","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":720,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 4","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":38,"s":[-5]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":49,"s":[-5]},{"t":86,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[16.75,14.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":38,"s":[16.75,12.85,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":49,"s":[16.75,12.85,0],"to":[0,0,0],"ti":[0,0,0]},{"t":86,"s":[16.75,14.5,0]}],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":0,"k":[20,20,100],"ix":6,"l":2}},"ao":0,"ip":-60,"op":660,"st":-60,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Vector","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[66.25,67.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"t":38,"s":[66.25,84.625,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":49,"s":[66.25,84.625,0],"to":[0,0,0],"ti":[0,0,0]},{"t":86,"s":[66.25,67.5,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[500,500,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1,-1],[-1,-1],[-1,1],[1,1]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":38,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1,-1],[-1,-1.538],[-1,0.462],[1,1]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":49,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1,-1],[-1,-1.538],[-1,0.462],[1,1]],"c":true}]},{"t":86,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[1,-1],[-1,-1],[-1,1],[1,1]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-60,"op":660,"st":-60,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Vector 1","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[46.25,67.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[500,500,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[-1.105,0]],"o":[[0,0],[0,0],[0,0],[0,1.105],[0,0]],"v":[[7,5],[7,-5],[-7,-5],[-7,3],[-5,5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":38,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[-1.575,-0.462]],"o":[[0,0],[0,0],[0,0],[0,1.105],[0,0]],"v":[[7,9.125],[7,-0.875],[-7,-5],[-7,2.163],[-5,5]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":49,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[-1.575,-0.462]],"o":[[0,0],[0,0],[0,0],[0,1.105],[0,0]],"v":[[7,9.125],[7,-0.875],[-7,-5],[-7,2.163],[-5,5]],"c":true}]},{"t":86,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[-1.105,0]],"o":[[0,0],[0,0],[0,0],[0,1.105],[0,0]],"v":[[7,5],[7,-5],[-7,-5],[-7,3],[-5,5]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.215686276555,0.258823543787,0.96862745285,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-60,"op":660,"st":-60,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"monyy","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[16,16,0],"ix":2,"l":2},"a":{"a":0,"k":[80,80,0],"ix":1,"l":2},"s":{"a":0,"k":[20,20,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[149.5,31],[52.5,31],[52.5,108.5],[149.5,108.5]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"w":160,"h":160,"ip":-60,"op":660,"st":-60,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Vector 2","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33.75,32.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[500,500,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[-1.105,0],[0,0],[0,0]],"o":[[0,0],[0,-1.105],[0,0],[0,0],[0,0]],"v":[[-4.5,2],[-4.5,0],[-2.5,-2],[4.5,-2],[4.5,8.1]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":38,"s":[{"i":[[0,0],[0,0],[-0.95,0.25],[0,0],[0,0]],"o":[[0,0],[0,-1.425],[0,0],[0,0],[0,0]],"v":[[-4.5,2],[-4.5,0.325],[-2.5,-2],[4.5,-4.1],[4.5,7.125]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":49,"s":[{"i":[[0,0],[0,0],[-0.95,0.25],[0,0],[0,0]],"o":[[0,0],[0,-1.425],[0,0],[0,0],[0,0]],"v":[[-4.5,2],[-4.5,0.325],[-2.5,-2],[4.5,-4.1],[4.5,7.125]],"c":false}]},{"t":86,"s":[{"i":[[0,0],[0,0],[-1.105,0],[0,0],[0,0]],"o":[[0,0],[0,-1.105],[0,0],[0,0],[0,0]],"v":[[-4.5,2],[-4.5,0],[-2.5,-2],[4.5,-2],[4.5,8.1]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-60,"op":660,"st":-60,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/packages/ui/src/assets/images/icons/wallet.svg b/packages/ui/src/assets/images/icons/wallet.svg new file mode 100644 index 000000000..904712eb4 --- /dev/null +++ b/packages/ui/src/assets/images/icons/wallet.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/world.svg b/packages/ui/src/assets/images/icons/world.svg index 0dfce2838..bb3de6ffc 100644 --- a/packages/ui/src/assets/images/icons/world.svg +++ b/packages/ui/src/assets/images/icons/world.svg @@ -1 +1,13 @@ - \ No newline at end of file + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/icons/your_backup.svg b/packages/ui/src/assets/images/icons/your_backup.svg new file mode 100644 index 000000000..11ffc2500 --- /dev/null +++ b/packages/ui/src/assets/images/icons/your_backup.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ui/src/assets/images/keystone.png b/packages/ui/src/assets/images/keystone.png new file mode 100644 index 0000000000000000000000000000000000000000..a9968c53dc2ab3c89fabe8c6f337a37ab7fdad1f GIT binary patch literal 4483 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 G^Zx_*fQC~5 literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/images/logo.svg b/packages/ui/src/assets/images/logo.svg index c886e2594..f63d1170d 100644 --- a/packages/ui/src/assets/images/logo.svg +++ b/packages/ui/src/assets/images/logo.svg @@ -1,3 +1,137 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/src/components/ActivityAssetsView.tsx b/packages/ui/src/components/ActivityAssetsView.tsx deleted file mode 100644 index 24426d188..000000000 --- a/packages/ui/src/components/ActivityAssetsView.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { FunctionComponent, useState } from "react" -import { PopupTabs } from "@block-wallet/background/controllers/PreferencesController" -import { updatePopupTab } from "../context/commActions" -import ActivityList from "./ActivityList" -import AssetsList from "./AssetsList" -import HorizontalSelect from "./input/HorizontalSelect" - -const tabs = [ - { - label: "Activity", - component: ActivityList, - }, - { - label: "Assets", - component: AssetsList, - }, -] - -const ActivityAssetsView: FunctionComponent<{ initialTab: PopupTabs }> = ({ - initialTab, -}) => { - const initialTabIndex = initialTab === "activity" ? 0 : 1 - const [tab, setTab] = useState(tabs[initialTabIndex]) - const TabComponent = tab.component - - const onTabChange = async (value: any) => { - setTab(value) - updatePopupTab(value.label.toLowerCase() as PopupTabs) - } - - return ( -