diff --git a/packages/hdwallet-native/package.json b/packages/hdwallet-native/package.json index 2455d4573..0a43b6b9a 100644 --- a/packages/hdwallet-native/package.json +++ b/packages/hdwallet-native/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@bitcoinerlab/secp256k1": "^1.1.1", + "@noble/curves": "^1.4.0", "@shapeshiftoss/bitcoinjs-lib": "7.0.0-shapeshift.0", "@shapeshiftoss/fiosdk": "1.2.1-shapeshift.6", "@shapeshiftoss/hdwallet-core": "1.57.1", @@ -57,5 +58,6 @@ "bs58": "^4.0.1", "cosmjs-types": "^0.4.1", "msw": "^0.27.1" - } + }, + "gitHead": "a59c5a12b265b6f64c65920cf330358a250227c2" } diff --git a/packages/hdwallet-native/src/crypto/isolation/adapters/bip32ed25519.ts b/packages/hdwallet-native/src/crypto/isolation/adapters/bip32ed25519.ts new file mode 100644 index 000000000..05f5bb01f --- /dev/null +++ b/packages/hdwallet-native/src/crypto/isolation/adapters/bip32ed25519.ts @@ -0,0 +1,41 @@ +import { Ed25519Node } from "../core/ed25519"; +import { ByteArray } from "../types"; + +export class BIP32Ed25519Adapter { + readonly node: Ed25519Node; + + private constructor(node: Ed25519Node) { + this.node = node; + } + + static async fromNode(node: Ed25519Node): Promise { + return new BIP32Ed25519Adapter(node); + } + + async getPublicKey(): Promise { + const publicKey = await this.node.getPublicKey(); + return Buffer.from(publicKey); + } + + async derivePath(path: string): Promise { + let currentNode = this.node; + + if (path === "m" || path === "M" || path === "m'" || path === "M'") { + return this; + } + + const segments = path + .toLowerCase() + .split("/") + .filter((segment) => segment !== "m"); + + for (const segment of segments) { + const index = parseInt(segment.replace("'", "")); + currentNode = await currentNode.derive(index); + } + + return new BIP32Ed25519Adapter(currentNode); + } +} + +export default BIP32Ed25519Adapter; diff --git a/packages/hdwallet-native/src/crypto/isolation/adapters/index.ts b/packages/hdwallet-native/src/crypto/isolation/adapters/index.ts index 58a1b28ce..d6ef2914d 100644 --- a/packages/hdwallet-native/src/crypto/isolation/adapters/index.ts +++ b/packages/hdwallet-native/src/crypto/isolation/adapters/index.ts @@ -5,3 +5,4 @@ export { default as FIO } from "./fio"; export { default as Binance } from "./binance"; export { default as Cosmos } from "./cosmos"; export { default as CosmosDirect } from "./cosmosDirect"; +export { default as SolanaDirect } from "./solana"; diff --git a/packages/hdwallet-native/src/crypto/isolation/adapters/solana.ts b/packages/hdwallet-native/src/crypto/isolation/adapters/solana.ts new file mode 100644 index 000000000..85ccdb086 --- /dev/null +++ b/packages/hdwallet-native/src/crypto/isolation/adapters/solana.ts @@ -0,0 +1,36 @@ +import * as core from "@shapeshiftoss/hdwallet-core"; +import { PublicKey, VersionedTransaction } from "@solana/web3.js"; + +import BIP32Ed25519Adapter from "./bip32ed25519"; + +export class SolanaDirectAdapter { + protected readonly nodeAdapter: BIP32Ed25519Adapter; + + constructor(nodeAdapter: BIP32Ed25519Adapter) { + this.nodeAdapter = nodeAdapter; + } + + async getAddress(addressNList: core.BIP32Path): Promise { + const nodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToBIP32(addressNList)); + const publicKeyBuffer = await nodeAdapter.getPublicKey(); + + const bufferForHex = Buffer.from(publicKeyBuffer); + + const pubKey = new PublicKey(bufferForHex); + + return pubKey.toBase58(); + } + + async signDirect(transaction: VersionedTransaction, addressNList: core.BIP32Path): Promise { + const nodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToBIP32(addressNList)); + const pubkey = await this.getAddress(addressNList); + + const messageToSign = transaction.message.serialize(); + const signature = await nodeAdapter.node.sign(messageToSign); + + transaction.addSignature(new PublicKey(pubkey), signature); + return transaction; + } +} + +export default SolanaDirectAdapter; diff --git a/packages/hdwallet-native/src/crypto/isolation/core/bip32/interfaces.ts b/packages/hdwallet-native/src/crypto/isolation/core/bip32/interfaces.ts index a5eba403a..5b490aca8 100644 --- a/packages/hdwallet-native/src/crypto/isolation/core/bip32/interfaces.ts +++ b/packages/hdwallet-native/src/crypto/isolation/core/bip32/interfaces.ts @@ -1,11 +1,13 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import { Revocable } from ".."; +import { Ed25519Node } from "../ed25519"; import * as SecP256K1 from "../secp256k1"; import { ChainCode } from "."; export interface Seed extends Partial { toMasterKey(hmacKey?: string | Uint8Array): Promise; + toEd25519MasterKey(): Promise; } export interface Node extends Partial, SecP256K1.ECDSAKey, Partial { diff --git a/packages/hdwallet-native/src/crypto/isolation/core/ed25519/index.ts b/packages/hdwallet-native/src/crypto/isolation/core/ed25519/index.ts new file mode 100644 index 000000000..bc2016ee3 --- /dev/null +++ b/packages/hdwallet-native/src/crypto/isolation/core/ed25519/index.ts @@ -0,0 +1,77 @@ +import { ExtPointConstructor } from "@noble/curves/abstract/edwards"; +import { ed25519 } from "@noble/curves/ed25519"; +import * as bip32crypto from "bip32/src/crypto"; + +import { Revocable, revocable } from "../../engines/default/revocable"; +import { ByteArray } from "../../types"; + +export type Ed25519Key = { + getPublicKey(): Promise; + sign(message: Uint8Array): Promise; + verify(message: Uint8Array, signature: Uint8Array): Promise; +}; + +export class Ed25519Node extends Revocable(class {}) { + readonly #privateKey: ByteArray; + readonly #chainCode: ByteArray; + readonly explicitPath?: string; + + protected constructor(privateKey: ByteArray, chainCode: ByteArray, explicitPath?: string) { + super(); + this.#privateKey = privateKey; + this.#chainCode = chainCode; + this.explicitPath = explicitPath; + } + + static async create(privateKey: ByteArray, chainCode: ByteArray, explicitPath?: string): Promise { + const obj = new Ed25519Node(privateKey, chainCode, explicitPath); + return revocable(obj, (x) => obj.addRevoker(x)); + } + + async getPublicKey(): Promise { + // Generate public key from private key + return Buffer.from(ed25519.getPublicKey(this.#privateKey)); + } + + async getChainCode(): Promise { + return this.#chainCode; + } + + async sign(message: Uint8Array): Promise { + return Buffer.from(ed25519.sign(message, this.#privateKey)); + } + + async derive(index: number): Promise { + // Ensure hardened derivation + if (index < 0x80000000) { + index += 0x80000000; + } + + const indexBuffer = Buffer.alloc(4); + indexBuffer.writeUInt32BE(index, 0); + + // SLIP-0010 for Ed25519 + const data = Buffer.concat([Buffer.from([0x00]), Buffer.from(this.#privateKey), indexBuffer]); + + const I = bip32crypto.hmacSHA512(Buffer.from(this.#chainCode), data); + const IL = I.slice(0, 32); + const IR = I.slice(32); + + // Ed25519 clamping + IL[0] &= 0xf8; + IL[31] &= 0x7f; + IL[31] |= 0x40; + + const path = this.explicitPath + ? `${this.explicitPath}/${index >= 0x80000000 ? index - 0x80000000 + "'" : index}` + : undefined; + + return Ed25519Node.create(IL, IR, path); + } +} + +export type Point = ExtPointConstructor; +export const Ed25519Point = { + BASE_POINT: ed25519.getPublicKey(new Uint8Array(32)), +}; +export type { Point as ExtendedPoint }; diff --git a/packages/hdwallet-native/src/crypto/isolation/engines/default/bip32.ts b/packages/hdwallet-native/src/crypto/isolation/engines/default/bip32.ts index a3e0daeb5..136ba42aa 100644 --- a/packages/hdwallet-native/src/crypto/isolation/engines/default/bip32.ts +++ b/packages/hdwallet-native/src/crypto/isolation/engines/default/bip32.ts @@ -3,6 +3,7 @@ import * as bip32crypto from "bip32/src/crypto"; import { TextEncoder } from "web-encoding"; import { BIP32, Digest, SecP256K1 } from "../../core"; +import { Ed25519Node } from "../../core/ed25519"; import { assertType, ByteArray, checkType, safeBufferFrom, Uint32 } from "../../types"; import { Revocable, revocable } from "./revocable"; @@ -172,4 +173,16 @@ export class Seed extends Revocable(class {}) implements BIP32.Seed { this.addRevoker(() => out.revoke?.()); return out; } + + async toEd25519MasterKey(): Promise { + // Use Ed25519-specific HMAC key + // https://github.com/trezor/trezor-crypto/blob/master/bip32.c#L56 + const hmacKey = safeBufferFrom(new TextEncoder().encode("ed25519 seed")); + const I = safeBufferFrom(bip32crypto.hmacSHA512(hmacKey, this.#seed)); + const IL = I.slice(0, 32); + const IR = I.slice(32, 64); + const out = await Ed25519Node.create(IL, IR); + this.addRevoker(() => out.revoke?.()); + return out; + } } diff --git a/packages/hdwallet-native/src/crypto/isolation/engines/default/bip39.ts b/packages/hdwallet-native/src/crypto/isolation/engines/default/bip39.ts index f563e66a7..4d783b480 100644 --- a/packages/hdwallet-native/src/crypto/isolation/engines/default/bip39.ts +++ b/packages/hdwallet-native/src/crypto/isolation/engines/default/bip39.ts @@ -5,6 +5,7 @@ import { TextEncoder } from "web-encoding"; import type { Seed as SeedType } from "../../core/bip32"; import type { Mnemonic as Bip39Mnemonic } from "../../core/bip39"; +import { Ed25519Node } from "../../core/ed25519"; import { Seed } from "./bip32"; import { Revocable, revocable } from "./revocable"; @@ -42,4 +43,8 @@ export class Mnemonic extends Revocable(class {}) implements Bip39Mnemonic { this.addRevoker(() => out.revoke?.()); return out; } + async toEd25519MasterKey(passphrase?: string): Promise { + const seed = await this.toSeed(passphrase); + return seed.toEd25519MasterKey(); + } } diff --git a/packages/hdwallet-native/src/crypto/isolation/engines/dummy/bip32.ts b/packages/hdwallet-native/src/crypto/isolation/engines/dummy/bip32.ts index 482bd1ed1..7a1285f4d 100644 --- a/packages/hdwallet-native/src/crypto/isolation/engines/dummy/bip32.ts +++ b/packages/hdwallet-native/src/crypto/isolation/engines/dummy/bip32.ts @@ -3,6 +3,7 @@ import { toArrayBuffer } from "@shapeshiftoss/hdwallet-core"; import * as bip32crypto from "bip32/src/crypto"; import { BIP32, Digest, SecP256K1 } from "../../core"; +import { Ed25519Node } from "../../core/ed25519"; import { ByteArray, checkType, safeBufferFrom, Uint32 } from "../../types"; import { DummyEngineError, ParsedXpubTree } from "./types"; @@ -115,4 +116,10 @@ export class Seed implements BIP32.Seed { return await Node.create(this.xpubTree); } + + toEd25519MasterKey(): Promise; + async toEd25519MasterKey(): Promise { + const edKey = await Ed25519Node.create(this.xpubTree.publicKey, this.xpubTree.chainCode); + return edKey; + } } diff --git a/packages/hdwallet-native/src/native.ts b/packages/hdwallet-native/src/native.ts index ce127db89..bb7c32b2e 100644 --- a/packages/hdwallet-native/src/native.ts +++ b/packages/hdwallet-native/src/native.ts @@ -9,12 +9,14 @@ import { MixinNativeBinanceWallet, MixinNativeBinanceWalletInfo } from "./binanc import { MixinNativeBTCWallet, MixinNativeBTCWalletInfo } from "./bitcoin"; import { MixinNativeCosmosWallet, MixinNativeCosmosWalletInfo } from "./cosmos"; import * as Isolation from "./crypto/isolation"; +import { Ed25519Node } from "./crypto/isolation/core/ed25519"; import { MixinNativeETHWallet, MixinNativeETHWalletInfo } from "./ethereum"; import { MixinNativeFioWallet, MixinNativeFioWalletInfo } from "./fio"; import { MixinNativeKavaWallet, MixinNativeKavaWalletInfo } from "./kava"; import { getNetwork } from "./networks"; import { MixinNativeOsmosisWallet, MixinNativeOsmosisWalletInfo } from "./osmosis"; import { MixinNativeSecretWallet, MixinNativeSecretWalletInfo } from "./secret"; +import { MixinNativeSolanaWallet, MixinNativeSolanaWalletInfo } from "./solana"; import { MixinNativeTerraWallet, MixinNativeTerraWalletInfo } from "./terra"; import { MixinNativeThorchainWallet, MixinNativeThorchainWalletInfo } from "./thorchain"; @@ -123,11 +125,13 @@ class NativeHDWalletInfo MixinNativeETHWalletInfo( MixinNativeCosmosWalletInfo( MixinNativeBinanceWalletInfo( - MixinNativeThorchainWalletInfo( - MixinNativeSecretWalletInfo( - MixinNativeTerraWalletInfo( - MixinNativeKavaWalletInfo( - MixinNativeArkeoWalletInfo(MixinNativeOsmosisWalletInfo(NativeHDWalletBase)) + MixinNativeSolanaWalletInfo( + MixinNativeThorchainWalletInfo( + MixinNativeSecretWalletInfo( + MixinNativeTerraWalletInfo( + MixinNativeKavaWalletInfo( + MixinNativeArkeoWalletInfo(MixinNativeOsmosisWalletInfo(NativeHDWalletBase)) + ) ) ) ) @@ -163,6 +167,8 @@ class NativeHDWalletInfo case "rune": case "thorchain": return core.thorchainDescribePath(msg.path); + case "solana": + return core.solanaDescribePath(msg.path); case "secret": case "scrt": case "tscrt": @@ -195,10 +201,12 @@ export class NativeHDWallet MixinNativeETHWallet( MixinNativeCosmosWallet( MixinNativeBinanceWallet( - MixinNativeThorchainWallet( - MixinNativeSecretWallet( - MixinNativeTerraWallet( - MixinNativeKavaWallet(MixinNativeOsmosisWallet(MixinNativeArkeoWallet(NativeHDWalletInfo))) + MixinNativeSolanaWallet( + MixinNativeThorchainWallet( + MixinNativeSecretWallet( + MixinNativeTerraWallet( + MixinNativeKavaWallet(MixinNativeOsmosisWallet(MixinNativeArkeoWallet(NativeHDWalletInfo))) + ) ) ) ) @@ -215,6 +223,7 @@ export class NativeHDWallet core.OsmosisWallet, core.FioWallet, core.ThorchainWallet, + core.SolanaWallet, core.SecretWallet, core.TerraWallet, core.KavaWallet, @@ -236,6 +245,7 @@ export class NativeHDWallet readonly _supportsBinance = true; readonly _supportsFio = true; readonly _supportsThorchain = true; + readonly _supportsSolana = true; readonly _supportsSecret = true; readonly _supportsTerra = true; readonly _supportsKava = true; @@ -245,6 +255,7 @@ export class NativeHDWallet #deviceId: string; #initialized = false; #masterKey: Promise | undefined = undefined; + #ed25519MasterKey: Promise | undefined = undefined; constructor({ mnemonic, deviceId, masterKey }: NativeAdapterArgs) { super(); @@ -257,6 +268,12 @@ export class NativeHDWallet const seed = await isolatedMnemonic.toSeed(); return await seed.toMasterKey(); })(); + this.#ed25519MasterKey = (async () => { + const isolatedMnemonic = + typeof mnemonic === "string" ? await Isolation.Engines.Default.BIP39.Mnemonic.create(mnemonic) : mnemonic; + const seed = await isolatedMnemonic.toSeed(); + return await seed.toEd25519MasterKey(); + })(); } this.#deviceId = deviceId; } @@ -317,9 +334,11 @@ export class NativeHDWallet async clearSession(): Promise {} async initialize(): Promise { - return this.needsMnemonic(!!this.#masterKey, async () => { + return this.needsMnemonic(!!this.#masterKey || !!this.#ed25519MasterKey, async () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const masterKey = await this.#masterKey!; + // Assume if we have a seckp256k1 masterKey, we have a ed25519 masterKey too + const ed25519MasterKey = await this.#ed25519MasterKey!; try { await Promise.all([ super.btcInitializeWallet(masterKey), @@ -329,6 +348,7 @@ export class NativeHDWallet super.binanceInitializeWallet(masterKey), super.fioInitializeWallet(masterKey), super.thorchainInitializeWallet(masterKey), + super.solanaInitializeWallet(ed25519MasterKey), super.secretInitializeWallet(masterKey), super.terraInitializeWallet(masterKey), super.kavaInitializeWallet(masterKey), @@ -370,6 +390,7 @@ export class NativeHDWallet this.#initialized = false; this.#masterKey = undefined; + super.solanaWipe(); super.btcWipe(); super.ethWipe(); super.cosmosWipe(); @@ -414,6 +435,28 @@ export class NativeHDWallet })(msg?.mnemonic, msg?.masterKey) ); + this.#ed25519MasterKey = Promise.resolve( + await (async (mnemonic, masterKey) => { + if (masterKey !== undefined) { + throw new Error("TODO?"); + } else if (mnemonic !== undefined) { + const isolatedMnemonic = await (async () => { + if (isMnemonicInterface(mnemonic)) return mnemonic; + if (typeof mnemonic === "string" && bip39.validateMnemonic(mnemonic)) { + return await Isolation.Engines.Default.BIP39.Mnemonic.create(mnemonic); + } + throw new Error("Required property [mnemonic] is invalid"); + })(); + const seed = await isolatedMnemonic.toSeed(); + seed.addRevoker?.(() => isolatedMnemonic.revoke?.()); + const out = await seed.toEd25519MasterKey(); + out.addRevoker?.(() => seed.revoke?.()); + return out; + } + throw new Error("Either [mnemonic] or [masterKey] is required"); + })(msg?.mnemonic, msg?.masterKey) + ); + if (typeof msg?.deviceId === "string") this.#deviceId = msg?.deviceId; this.#initialized = false; diff --git a/packages/hdwallet-native/src/solana.ts b/packages/hdwallet-native/src/solana.ts new file mode 100644 index 000000000..87da7a8f4 --- /dev/null +++ b/packages/hdwallet-native/src/solana.ts @@ -0,0 +1,70 @@ +import * as core from "@shapeshiftoss/hdwallet-core"; + +import BIP32Ed25519Adapter from "./crypto/isolation/adapters/bip32ed25519"; +import { SolanaDirectAdapter } from "./crypto/isolation/adapters/solana"; +import { Ed25519Node } from "./crypto/isolation/core/ed25519"; +import { NativeHDWalletBase } from "./native"; + +export function MixinNativeSolanaWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow + return class MixinNativeSolanaWalletInfo extends Base implements core.SolanaWalletInfo { + readonly _supportsSolanaInfo = true; + _chainId = 1; + + solanaGetAccountPaths(msg: core.ETHGetAccountPath): Array { + return core.solanaGetAccountPaths(msg); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + solanaNextAccountPath(msg: core.SolanaAccountPath): core.SolanaAccountPath | undefined { + throw new Error("Method not implemented"); + } + }; +} + +export function MixinNativeSolanaWallet>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow + return class MixinNativeSolanaWallet extends Base { + readonly _supportsSolana = true; + #solanaSigner: SolanaDirectAdapter | undefined; + + async solanaInitializeWallet(masterKey: Ed25519Node): Promise { + // Create an Ed25519 BIP32 adapter directly from our masterKey + const ed25519Adapter = await BIP32Ed25519Adapter.fromNode(masterKey); + + // Initialize the Solana adapter with the Ed25519 adapter + this.#solanaSigner = new SolanaDirectAdapter(ed25519Adapter); + } + + solanaWipe() { + this.#solanaSigner = undefined; + } + + async solanaGetAddress(msg: core.SolanaGetAddress): Promise { + return this.needsMnemonic(!!this.#solanaSigner, () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this.#solanaSigner!.getAddress(msg.addressNList); + }); + } + + async solanaSignTx(msg: core.SolanaSignTx): Promise { + return this.needsMnemonic(!!this.#solanaSigner, async () => { + const address = await this.solanaGetAddress({ + addressNList: msg.addressNList, + showDisplay: false, + }); + + if (!address) throw new Error("Failed to get Solana address"); + + const transaction = core.solanaBuildTransaction(msg, address); + const signedTx = await this.#solanaSigner!.signDirect(transaction, msg.addressNList); + const serializedData = signedTx.serialize(); + + return { + serialized: Buffer.from(serializedData).toString("base64"), + signatures: signedTx.signatures.map((signature) => Buffer.from(signature).toString("base64")), + }; + }); + } + }; +} diff --git a/yarn.lock b/yarn.lock index ba966b4bf..5946274d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3764,6 +3764,11 @@ resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.3.tgz#57e1677bf6885354b466c38e2b620c62f45a7123" integrity sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ== +"@noble/ed25519@^2.1.0": + version "2.1.0" + resolved "http://127.0.0.1:4873/@noble/ed25519/-/ed25519-2.1.0.tgz#4bf661de9ee0ad775d41fcacbfc9aeec491f459c" + integrity sha512-KM4qTyXPinyCgMzeYJH/UudpdL+paJXtY3CHtHYZQtBkS8MZoPr4rOikZllIutJe0d06QDQKisyn02gxZ8TcQA== + "@noble/hashes@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.0.0.tgz#d5e38bfbdaba174805a4e649f13be9a9ed3351ae" @@ -5481,7 +5486,7 @@ dependencies: ethereumjs-util "*" -"@types/eventsource@1.1.8", "@types/eventsource@^1.1.2": +"@types/eventsource@^1.1.2": version "1.1.8" resolved "https://registry.yarnpkg.com/@types/eventsource/-/eventsource-1.1.8.tgz#48ae1f3aaf9bb84c713038f354112cc7ceaad519" integrity sha512-fJQNt9LijJCZwYvM6O30uLzdpAK9zs52Uc9iUW9M2Zsg0HQM6DLf6QysjC/wuFX+0798B8AppVMvgdO6IftPKQ== @@ -15577,7 +15582,7 @@ p-pipe@^3.1.0: resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== -p-queue@^6.6.2, p-queue@^7.4.1: +p-queue@^6.6.2: version "6.6.2" resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== @@ -15585,6 +15590,14 @@ p-queue@^6.6.2, p-queue@^7.4.1: eventemitter3 "^4.0.4" p-timeout "^3.2.0" +p-queue@^7.4.1: + version "7.4.1" + resolved "http://127.0.0.1:4873/p-queue/-/p-queue-7.4.1.tgz#7f86f853048beca8272abdbb7cec1ed2afc0f265" + integrity sha512-vRpMXmIkYF2/1hLBKisKeVYJZ8S2tZ0zEAmIJgdVKP2nq0nh4qCdf8bgw+ZgKrkh71AOCaqzwbJJk1WtdcF3VA== + dependencies: + eventemitter3 "^5.0.1" + p-timeout "^5.0.2" + p-reduce@^2.0.0, p-reduce@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" @@ -15597,6 +15610,11 @@ p-timeout@^3.2.0: dependencies: p-finally "^1.0.0" +p-timeout@^5.0.2: + version "5.1.0" + resolved "http://127.0.0.1:4873/p-timeout/-/p-timeout-5.1.0.tgz#b3c691cf4415138ce2d9cfe071dba11f0fee085b" + integrity sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew== + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"