From 4e8cc3057424bcc8870ff2e4b306afe610462b17 Mon Sep 17 00:00:00 2001 From: Prithvish Baidya Date: Fri, 4 Oct 2024 05:48:11 +0530 Subject: [PATCH] feat: migrate aws kms flow to v5 (#626) * feat: migrate aws kms flow to v5 * chore: revert hardcoded test backend wallet address * Shared signature logic + move function defintions inside body * remove twAddress override * feat: GCP KMS + refactor - use multiple wallet types simultaneously - resourcePath or ARN is source of truth: treat config AWS/GCP details as "default" - credentials override * add unit tests for kms accounts with prool * fix .env file ordering for tests * bugfix: split awn arn correctly * ignore engines globally (temp fix) * fix yarnrc * Address CR Comments * tests for AWS ARN and GCP KMS * always store kms credentials in WalletDetails * modify healthCheck function to include HETEROGENEOUS_WALLET_TYPES feature * remove bad comment * refactor invalid check --------- Signed-off-by: Prithvish Baidya --- .env.test | 11 +- .yarnrc | 1 + package.json | 4 + src/db/configuration/getConfiguration.ts | 92 +- src/db/wallets/createWalletDetails.ts | 68 +- src/db/wallets/getWalletDetails.ts | 2 +- .../migration.sql | 5 + src/prisma/schema.prisma | 42 +- src/schema/config.ts | 48 +- src/server/routes/backend-wallet/create.ts | 52 +- src/server/routes/backend-wallet/import.ts | 264 ++-- .../routes/configuration/wallets/get.ts | 53 +- .../routes/configuration/wallets/update.ts | 127 +- src/server/routes/system/health.ts | 18 +- src/server/utils/wallets/awsKmsArn.ts | 33 + .../utils/wallets/createAwsKmsWallet.ts | 52 +- .../utils/wallets/createGcpKmsWallet.ts | 85 +- src/server/utils/wallets/createLocalWallet.ts | 7 - .../utils/wallets/fetchAwsKmsWalletParams.ts | 54 + .../utils/wallets/fetchGcpKmsWalletParams.ts | 80 ++ .../utils/wallets/gcpKmsResourcePath.ts | 31 + src/server/utils/wallets/getAwsKmsAccount.ts | 129 ++ src/server/utils/wallets/getAwsKmsWallet.ts | 23 - src/server/utils/wallets/getGcpKmsAccount.ts | 133 ++ src/server/utils/wallets/getGcpKmsWallet.ts | 30 - .../utils/wallets/importAwsKmsWallet.ts | 39 +- .../utils/wallets/importGcpKmsWallet.ts | 46 +- src/tests/aws-arn.test.ts | 62 + src/tests/config/aws-kms.ts | 22 + src/tests/config/gcp-kms.ts | 15 + src/tests/gcp-resource-path.test.ts | 84 ++ src/tests/shared/chain.ts | 13 + src/tests/shared/client.ts | 5 + src/tests/shared/typed-data.ts | 86 ++ src/tests/wallets/aws-kms.test.ts | 141 +++ src/tests/wallets/gcp-kms.test.ts | 138 +++ src/utils/account.ts | 171 ++- src/utils/cache/getSmartWalletV5.ts | 12 +- src/utils/cache/getWallet.ts | 88 +- vitest.config.ts | 11 + vitest.global-setup.ts | 21 + yarn.lock | 1090 ++++++++++++++++- 42 files changed, 2988 insertions(+), 500 deletions(-) create mode 100644 .yarnrc create mode 100644 src/prisma/migrations/20241003051028_wallet_details_credentials/migration.sql create mode 100644 src/server/utils/wallets/awsKmsArn.ts create mode 100644 src/server/utils/wallets/fetchAwsKmsWalletParams.ts create mode 100644 src/server/utils/wallets/fetchGcpKmsWalletParams.ts create mode 100644 src/server/utils/wallets/gcpKmsResourcePath.ts create mode 100644 src/server/utils/wallets/getAwsKmsAccount.ts delete mode 100644 src/server/utils/wallets/getAwsKmsWallet.ts create mode 100644 src/server/utils/wallets/getGcpKmsAccount.ts delete mode 100644 src/server/utils/wallets/getGcpKmsWallet.ts create mode 100644 src/tests/aws-arn.test.ts create mode 100644 src/tests/config/aws-kms.ts create mode 100644 src/tests/config/gcp-kms.ts create mode 100644 src/tests/gcp-resource-path.test.ts create mode 100644 src/tests/shared/chain.ts create mode 100644 src/tests/shared/client.ts create mode 100644 src/tests/shared/typed-data.ts create mode 100644 src/tests/wallets/aws-kms.test.ts create mode 100644 src/tests/wallets/gcp-kms.test.ts create mode 100644 vitest.config.ts create mode 100644 vitest.global-setup.ts diff --git a/.env.test b/.env.test index 3d3315055..d2acedfb2 100644 --- a/.env.test +++ b/.env.test @@ -5,4 +5,13 @@ ENCRYPTION_PASSWORD="test" ENABLE_KEYPAIR_AUTH="true" ENABLE_HTTPS="true" REDIS_URL="redis://127.0.0.1:6379/0" -THIRDWEB_API_SECRET_KEY="my-thirdweb-secret-key" \ No newline at end of file +THIRDWEB_API_SECRET_KEY="my-thirdweb-secret-key" + +TEST_AWS_KMS_KEY_ID="" +TEST_AWS_KMS_ACCESS_KEY_ID="" +TEST_AWS_KMS_SECRET_ACCESS_KEY="" +TEST_AWS_KMS_REGION="" + +TEST_GCP_KMS_RESOURCE_PATH="" +TEST_GCP_KMS_EMAIL="" +TEST_GCP_KMS_PK="" \ No newline at end of file diff --git a/.yarnrc b/.yarnrc new file mode 100644 index 000000000..f757a6ac5 --- /dev/null +++ b/.yarnrc @@ -0,0 +1 @@ +--ignore-engines true \ No newline at end of file diff --git a/package.json b/package.json index 58c43a5dd..fa5d128db 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,8 @@ "dependencies": { "@aws-sdk/client-kms": "^3.398.0", "@bull-board/fastify": "^5.21.1", + "@cloud-cryptographic-wallet/cloud-kms-signer": "^0.1.2", + "@cloud-cryptographic-wallet/signer": "^0.0.5", "@fastify/basic-auth": "^5.1.1", "@fastify/cookie": "^8.3.0", "@fastify/express": "^2.3.0", @@ -44,6 +46,7 @@ "@thirdweb-dev/sdk": "^4.0.89", "@thirdweb-dev/service-utils": "^0.4.28", "@types/base-64": "^1.0.2", + "aws-kms-signer": "^0.5.3", "base-64": "^1.0.0", "body-parser": "^1.20.2", "bullmq": "^5.11.0", @@ -69,6 +72,7 @@ "pg": "^8.11.3", "prisma": "^5.14.0", "prom-client": "^15.1.3", + "prool": "^0.0.16", "superjson": "^2.2.1", "thirdweb": "^5.58.4", "uuid": "^9.0.1", diff --git a/src/db/configuration/getConfiguration.ts b/src/db/configuration/getConfiguration.ts index 0203ff40f..67c47f9a6 100644 --- a/src/db/configuration/getConfiguration.ts +++ b/src/db/configuration/getConfiguration.ts @@ -1,12 +1,16 @@ -import { Configuration } from "@prisma/client"; -import { Static } from "@sinclair/typebox"; +import type { Configuration } from "@prisma/client"; +import type { Static } from "@sinclair/typebox"; import { LocalWallet } from "@thirdweb-dev/wallets"; import { ethers } from "ethers"; -import { Chain } from "thirdweb"; -import { ParsedConfig } from "../../schema/config"; +import type { Chain } from "thirdweb"; +import type { + AwsWalletConfiguration, + GcpWalletConfiguration, + ParsedConfig, +} from "../../schema/config"; import { WalletType } from "../../schema/wallet"; import { mandatoryAllowedCorsUrls } from "../../server/utils/cors-urls"; -import { networkResponseSchema } from "../../utils/cache/getSdk"; +import type { networkResponseSchema } from "../../utils/cache/getSdk"; import { decrypt } from "../../utils/crypto"; import { env } from "../../utils/env"; import { logger } from "../../utils/logger"; @@ -53,6 +57,18 @@ const toParsedConfig = async (config: Configuration): Promise => { } } + // LEGACY COMPATIBILITY + // legacy behaviour was to check for these in order: + // 1. AWS KMS Configuration - if found, wallet type is AWS KMS + // 2. GCP KMS Configuration - if found, wallet type is GCP KMS + // 3. If neither are found, wallet type is Local + // to maintain compatibility where users expect to call create new backend wallet endpoint without an explicit wallet type + // we need to preserve the wallet type in the configuration but only as the "default" wallet type + let legacyWalletType_removeInNextBreakingChange: WalletType = + WalletType.local; + + let awsWalletConfiguration: AwsWalletConfiguration | null = null; + // TODO: Remove backwards compatibility with next breaking change if (awsAccessKeyId && awsSecretAccessKey && awsRegion) { // First try to load the aws secret using the encryption password @@ -73,7 +89,8 @@ const toParsedConfig = async (config: Configuration): Promise => { logger({ service: "worker", level: "info", - message: `[Encryption] Updating awsSecretAccessKey to use ENCRYPTION_PASSWORD`, + message: + "[Encryption] Updating awsSecretAccessKey to use ENCRYPTION_PASSWORD", }); await updateConfiguration({ @@ -85,28 +102,18 @@ const toParsedConfig = async (config: Configuration): Promise => { // Renaming contractSubscriptionsRetryDelaySeconds // to contractSubscriptionsRequeryDelaySeconds to reflect its purpose // as we are requerying (& not retrying) with different delays - return { - ...restConfig, - contractSubscriptionsRequeryDelaySeconds: - contractSubscriptionsRetryDelaySeconds, - chainOverridesParsed, - walletConfiguration: { - type: WalletType.awsKms, - awsRegion, - awsAccessKeyId, - awsSecretAccessKey: decryptedSecretAccessKey, - }, + awsWalletConfiguration = { + awsAccessKeyId, + awsSecretAccessKey: decryptedSecretAccessKey, + defaultAwsRegion: awsRegion, }; + + legacyWalletType_removeInNextBreakingChange = WalletType.awsKms; } + let gcpWalletConfiguration: GcpWalletConfiguration | null = null; // TODO: Remove backwards compatibility with next breaking change - if ( - gcpApplicationProjectId && - gcpKmsLocationId && - gcpKmsKeyRingId && - gcpApplicationCredentialEmail && - gcpApplicationCredentialPrivateKey - ) { + if (gcpApplicationCredentialEmail && gcpApplicationCredentialPrivateKey) { // First try to load the gcp secret using the encryption password let decryptedGcpKey = decrypt( gcpApplicationCredentialPrivateKey, @@ -125,7 +132,8 @@ const toParsedConfig = async (config: Configuration): Promise => { logger({ service: "worker", level: "info", - message: `[Encryption] Updating gcpApplicationCredentialPrivateKey to use ENCRYPTION_PASSWORD`, + message: + "[Encryption] Updating gcpApplicationCredentialPrivateKey to use ENCRYPTION_PASSWORD", }); await updateConfiguration({ @@ -134,20 +142,24 @@ const toParsedConfig = async (config: Configuration): Promise => { } } - return { - ...restConfig, - contractSubscriptionsRequeryDelaySeconds: - contractSubscriptionsRetryDelaySeconds, - chainOverridesParsed, - walletConfiguration: { - type: WalletType.gcpKms, - gcpApplicationProjectId, - gcpKmsLocationId, - gcpKmsKeyRingId, - gcpApplicationCredentialEmail, - gcpApplicationCredentialPrivateKey: decryptedGcpKey, - }, + if (!gcpKmsLocationId || !gcpKmsKeyRingId || !gcpApplicationProjectId) { + throw new Error( + "GCP KMS location ID, project ID, and key ring ID are required configuration for this wallet type", + ); + } + + gcpWalletConfiguration = { + gcpApplicationCredentialEmail, + gcpApplicationCredentialPrivateKey: decryptedGcpKey, + + // TODO: Remove these with the next breaking change + // These are used because import endpoint does not yet support GCP KMS resource path + defaultGcpKmsLocationId: gcpKmsLocationId, + defaultGcpKmsKeyRingId: gcpKmsKeyRingId, + defaultGcpApplicationProjectId: gcpApplicationProjectId, }; + + legacyWalletType_removeInNextBreakingChange = WalletType.gcpKms; } return { @@ -156,7 +168,9 @@ const toParsedConfig = async (config: Configuration): Promise => { contractSubscriptionsRetryDelaySeconds, chainOverridesParsed, walletConfiguration: { - type: WalletType.local, + aws: awsWalletConfiguration, + gcp: gcpWalletConfiguration, + legacyWalletType_removeInNextBreakingChange, }, }; }; diff --git a/src/db/wallets/createWalletDetails.ts b/src/db/wallets/createWalletDetails.ts index af6cdee2d..617709c59 100644 --- a/src/db/wallets/createWalletDetails.ts +++ b/src/db/wallets/createWalletDetails.ts @@ -1,21 +1,33 @@ -import { PrismaTransaction } from "../../schema/prisma"; -import type { WalletType } from "../../schema/wallet"; +import type { PrismaTransaction } from "../../schema/prisma"; +import { encrypt } from "../../utils/crypto"; import { getPrismaWithPostgresTx } from "../client"; // TODO: Case on types by wallet type -interface CreateWalletDetailsParams { +type CreateWalletDetailsParams = { pgtx?: PrismaTransaction; address: string; - type: WalletType; label?: string; - awsKmsKeyId?: string; - awsKmsArn?: string; - gcpKmsKeyRingId?: string; - gcpKmsKeyId?: string; - gcpKmsKeyVersionId?: string; - gcpKmsLocationId?: string; - gcpKmsResourcePath?: string; -} +} & ( + | { + type: "aws-kms"; + awsKmsKeyId?: string; // depcrecated and unused, todo: remove with next breaking change + awsKmsArn: string; + + awsKmsSecretAccessKey?: string; // will be encrypted and stored, pass plaintext to this function + awsKmsAccessKeyId?: string; + } + | { + type: "gcp-kms"; + gcpKmsResourcePath: string; + gcpKmsKeyRingId?: string; // depcrecated and unused, todo: remove with next breaking change + gcpKmsKeyId?: string; // depcrecated and unused, todo: remove with next breaking change + gcpKmsKeyVersionId?: string; // depcrecated and unused, todo: remove with next breaking change + gcpKmsLocationId?: string; // depcrecated and unused, todo: remove with next breaking change + + gcpApplicationCredentialPrivateKey?: string; // encrypted + gcpApplicationCredentialEmail?: string; + } +); export const createWalletDetails = async ({ pgtx, @@ -35,10 +47,30 @@ export const createWalletDetails = async ({ ); } - return prisma.walletDetails.create({ - data: { - ...walletDetails, - address: walletDetails.address.toLowerCase(), - }, - }); + if (walletDetails.type === "aws-kms") { + return prisma.walletDetails.create({ + data: { + ...walletDetails, + address: walletDetails.address.toLowerCase(), + + awsKmsSecretAccessKey: walletDetails.awsKmsSecretAccessKey + ? encrypt(walletDetails.awsKmsSecretAccessKey) + : undefined, + }, + }); + } + + if (walletDetails.type === "gcp-kms") { + return prisma.walletDetails.create({ + data: { + ...walletDetails, + address: walletDetails.address.toLowerCase(), + + gcpApplicationCredentialPrivateKey: + walletDetails.gcpApplicationCredentialPrivateKey + ? encrypt(walletDetails.gcpApplicationCredentialPrivateKey) + : undefined, + }, + }); + } }; diff --git a/src/db/wallets/getWalletDetails.ts b/src/db/wallets/getWalletDetails.ts index c37709064..310079410 100644 --- a/src/db/wallets/getWalletDetails.ts +++ b/src/db/wallets/getWalletDetails.ts @@ -1,4 +1,4 @@ -import { PrismaTransaction } from "../../schema/prisma"; +import type { PrismaTransaction } from "../../schema/prisma"; import { getPrismaWithPostgresTx } from "../client"; interface GetWalletDetailsParams { diff --git a/src/prisma/migrations/20241003051028_wallet_details_credentials/migration.sql b/src/prisma/migrations/20241003051028_wallet_details_credentials/migration.sql new file mode 100644 index 000000000..ee4b3b78b --- /dev/null +++ b/src/prisma/migrations/20241003051028_wallet_details_credentials/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "wallet_details" ADD COLUMN "awsKmsAccessKeyId" TEXT, +ADD COLUMN "awsKmsSecretAccessKey" TEXT, +ADD COLUMN "gcpApplicationCredentialEmail" TEXT, +ADD COLUMN "gcpApplicationCredentialPrivateKey" TEXT; diff --git a/src/prisma/schema.prisma b/src/prisma/schema.prisma index 4ba629b58..6ed00bdd8 100644 --- a/src/prisma/schema.prisma +++ b/src/prisma/schema.prisma @@ -30,15 +30,15 @@ model Configuration { contractSubscriptionsRetryDelaySeconds String @default("10") @map("contractSubscriptionsRetryDelaySeconds") // AWS - awsAccessKeyId String? @map("awsAccessKeyId") - awsSecretAccessKey String? @map("awsSecretAccessKey") - awsRegion String? @map("awsRegion") + awsAccessKeyId String? @map("awsAccessKeyId") /// global config, precedence goes to WalletDetails + awsSecretAccessKey String? @map("awsSecretAccessKey") /// global config, precedence goes to WalletDetails + awsRegion String? @map("awsRegion") /// global config, treat as "default", store in WalletDetails.awsKmsArn // GCP - gcpApplicationProjectId String? @map("gcpApplicationProjectId") - gcpKmsLocationId String? @map("gcpKmsLocationId") - gcpKmsKeyRingId String? @map("gcpKmsKeyRingId") - gcpApplicationCredentialEmail String? @map("gcpApplicationCredentialEmail") - gcpApplicationCredentialPrivateKey String? @map("gcpApplicationCredentialPrivateKey") + gcpApplicationProjectId String? @map("gcpApplicationProjectId") /// global config, treat as "default", store in WalletDetails.gcpKmsResourcePath + gcpKmsLocationId String? @map("gcpKmsLocationId") /// global config, treat as "default", store in WalletDetails.gcpKmsResourcePath + gcpKmsKeyRingId String? @map("gcpKmsKeyRingId") /// global config, treat as "default", store in WalletDetails.gcpKmsResourcePath + gcpApplicationCredentialEmail String? @map("gcpApplicationCredentialEmail") /// global config, precedence goes to WalletDetails + gcpApplicationCredentialPrivateKey String? @map("gcpApplicationCredentialPrivateKey") /// global config, precedence goes to WalletDetails // Auth authDomain String @default("") @map("authDomain") // TODO: Remove defaults on major authWalletEncryptedJson String @default("") @map("authWalletEncryptedJson") // TODO: Remove defaults on major @@ -76,20 +76,24 @@ model Tokens { } model WalletDetails { - address String @id @map("address") - type String @map("type") - label String? @map("label") + address String @id @map("address") + type String @map("type") + label String? @map("label") // Local - encryptedJson String? @map("encryptedJson") + encryptedJson String? @map("encryptedJson") // KMS - awsKmsKeyId String? @map("awsKmsKeyId") - awsKmsArn String? @map("awsKmsArn") + awsKmsKeyId String? @map("awsKmsKeyId") /// deprecated and unused, todo: remove with next breaking change. Use awsKmsArn + awsKmsArn String? @map("awsKmsArn") + awsKmsSecretAccessKey String? @map("awsKmsSecretAccessKey") /// if not available, default to: Configuration.awsSecretAccessKey + awsKmsAccessKeyId String? @map("awsKmsAccessKeyId") /// if not available, default to: Configuration.awsAccessKeyId // GCP - gcpKmsKeyRingId String? @map("gcpKmsKeyRingId") @db.VarChar(50) - gcpKmsKeyId String? @map("gcpKmsKeyId") @db.VarChar(50) - gcpKmsKeyVersionId String? @map("gcpKmsKeyVersionId") @db.VarChar(20) - gcpKmsLocationId String? @map("gcpKmsLocationId") @db.VarChar(20) - gcpKmsResourcePath String? @map("gcpKmsResourcePath") @db.Text + gcpKmsKeyRingId String? @map("gcpKmsKeyRingId") @db.VarChar(50) /// deprecated and unused. Use gcpKmsResourcePath instead, todo: remove with next breaking change + gcpKmsKeyId String? @map("gcpKmsKeyId") @db.VarChar(50) /// deprecated and unused. Use gcpKmsResourcePath instead, todo: remove with next breaking change + gcpKmsKeyVersionId String? @map("gcpKmsKeyVersionId") @db.VarChar(20) /// deprecated and unused. Use gcpKmsResourcePath instead, todo: remove with next breaking change + gcpKmsLocationId String? @map("gcpKmsLocationId") @db.VarChar(20) /// deprecated and unused. Use gcpKmsResourcePath instead, todo: remove with next breaking change + gcpKmsResourcePath String? @map("gcpKmsResourcePath") @db.Text + gcpApplicationCredentialEmail String? @map("gcpApplicationCredentialEmail") /// if not available, default to: Configuration.gcpApplicationCredentialEmail + gcpApplicationCredentialPrivateKey String? @map("gcpApplicationCredentialPrivateKey") /// if not available, default to: Configuration.gcpApplicationCredentialPrivateKey @@map("wallet_details") } diff --git a/src/schema/config.ts b/src/schema/config.ts index 8c202b976..fc3ce2d62 100644 --- a/src/schema/config.ts +++ b/src/schema/config.ts @@ -1,6 +1,25 @@ -import { Configuration } from "@prisma/client"; -import { Chain } from "thirdweb"; -import { WalletType } from "./wallet"; +import type { Configuration } from "@prisma/client"; +import type { Chain } from "thirdweb"; +import type { WalletType } from "./wallet"; + +export type AwsWalletConfiguration = { + awsAccessKeyId: string; + awsSecretAccessKey: string; + + defaultAwsRegion: string; +}; + +export type GcpWalletConfiguration = { + gcpApplicationCredentialEmail: string; + gcpApplicationCredentialPrivateKey: string; + + // these values are used as default so users don't need to specify them every time to the create wallet endpoint + // for fetching a wallet, always trust the resource path in the wallet details + // only use these values for creating a new wallet, when the resource path is not known + defaultGcpKmsLocationId: string; + defaultGcpKmsKeyRingId: string; + defaultGcpApplicationProjectId: string; +}; export interface ParsedConfig extends Omit< @@ -15,24 +34,11 @@ export interface ParsedConfig | "gcpApplicationCredentialPrivateKey" | "contractSubscriptionsRetryDelaySeconds" > { - walletConfiguration: - | { - type: WalletType.local; - } - | { - type: WalletType.awsKms; - awsAccessKeyId: string; - awsSecretAccessKey: string; - awsRegion: string; - } - | { - type: WalletType.gcpKms; - gcpApplicationProjectId: string; - gcpKmsLocationId: string; - gcpKmsKeyRingId: string; - gcpApplicationCredentialEmail: string; - gcpApplicationCredentialPrivateKey: string; - }; + walletConfiguration: { + aws: AwsWalletConfiguration | null; + gcp: GcpWalletConfiguration | null; + legacyWalletType_removeInNextBreakingChange: WalletType; + }; contractSubscriptionsRequeryDelaySeconds: string; chainOverridesParsed: Chain[]; } diff --git a/src/server/routes/backend-wallet/create.ts b/src/server/routes/backend-wallet/create.ts index 1c360825c..c70a24605 100644 --- a/src/server/routes/backend-wallet/create.ts +++ b/src/server/routes/backend-wallet/create.ts @@ -3,14 +3,27 @@ import type { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { WalletType } from "../../../schema/wallet"; import { getConfig } from "../../../utils/cache/getConfig"; +import { createCustomError } from "../../middleware/error"; import { AddressSchema } from "../../schemas/address"; import { standardResponseSchema } from "../../schemas/sharedApiSchemas"; -import { createAwsKmsWallet } from "../../utils/wallets/createAwsKmsWallet"; -import { createGcpKmsWallet } from "../../utils/wallets/createGcpKmsWallet"; +import { + CreateAwsKmsWalletError, + createAwsKmsWallet, +} from "../../utils/wallets/createAwsKmsWallet"; +import { + CreateGcpKmsWalletError, + createGcpKmsWallet, +} from "../../utils/wallets/createGcpKmsWallet"; import { createLocalWallet } from "../../utils/wallets/createLocalWallet"; const requestBodySchema = Type.Object({ label: Type.Optional(Type.String()), + type: Type.Optional( + Type.Enum(WalletType, { + description: + "Optional wallet type. If not provided, the default wallet type will be used.", + }), + ), }); const responseSchema = Type.Object({ @@ -28,7 +41,7 @@ responseSchema.example = { }; export const createBackendWallet = async (fastify: FastifyInstance) => { - fastify.route<{ + fastify.withTypeProvider().route<{ Body: Static; Reply: Static; }>({ @@ -50,15 +63,42 @@ export const createBackendWallet = async (fastify: FastifyInstance) => { let walletAddress: string; const config = await getConfig(); - switch (config.walletConfiguration.type) { + + const walletType = + req.body.type ?? + config.walletConfiguration.legacyWalletType_removeInNextBreakingChange; + + switch (walletType) { case WalletType.local: walletAddress = await createLocalWallet({ label }); break; case WalletType.awsKms: - walletAddress = await createAwsKmsWallet({ label }); + try { + walletAddress = await createAwsKmsWallet({ label }); + } catch (e) { + if (e instanceof CreateAwsKmsWalletError) { + throw createCustomError( + e.message, + StatusCodes.BAD_REQUEST, + "CREATE_AWS_KMS_WALLET_ERROR", + ); + } + throw e; + } break; case WalletType.gcpKms: - walletAddress = await createGcpKmsWallet({ label }); + try { + walletAddress = await createGcpKmsWallet({ label }); + } catch (e) { + if (e instanceof CreateGcpKmsWalletError) { + throw createCustomError( + e.message, + StatusCodes.BAD_REQUEST, + "CREATE_GCP_KMS_WALLET_ERROR", + ); + } + throw e; + } break; } diff --git a/src/server/routes/backend-wallet/import.ts b/src/server/routes/backend-wallet/import.ts index 897fcbdad..510059a14 100644 --- a/src/server/routes/backend-wallet/import.ts +++ b/src/server/routes/backend-wallet/import.ts @@ -1,39 +1,71 @@ -import { Static, Type } from "@sinclair/typebox"; -import { FastifyInstance } from "fastify"; +import { Type, type Static } from "@sinclair/typebox"; +import type { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; -import { WalletType } from "../../../schema/wallet"; import { getConfig } from "../../../utils/cache/getConfig"; import { createCustomError } from "../../middleware/error"; import { AddressSchema } from "../../schemas/address"; import { standardResponseSchema } from "../../schemas/sharedApiSchemas"; +import { getGcpKmsResourcePath } from "../../utils/wallets/gcpKmsResourcePath"; import { importAwsKmsWallet } from "../../utils/wallets/importAwsKmsWallet"; import { importGcpKmsWallet } from "../../utils/wallets/importGcpKmsWallet"; import { importLocalWallet } from "../../utils/wallets/importLocalWallet"; -const RequestBodySchema = Type.Union([ +const RequestBodySchema = Type.Intersect([ Type.Object({ - awsKmsKeyId: Type.String({ - description: "AWS KMS key ID", - examples: ["12345678-1234-1234-1234-123456789012"], - }), - awsKmsArn: Type.String({ - description: "AWS KMS key ARN", - examples: [ - "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012", - ], - }), + label: Type.Optional( + Type.String({ + description: "Optional label for the imported wallet", + }), + ), }), - Type.Object({ - gcpKmsKeyId: Type.String({ - description: "GCP KMS key ID", - examples: ["12345678-1234-1234-1234-123456789012"], + Type.Union([ + Type.Object({ + awsKmsArn: Type.String({ + description: "AWS KMS key ARN", + examples: [ + "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012", + ], + }), + credentials: Type.Optional( + Type.Object( + { + awsAccessKeyId: Type.String({ description: "AWS Access Key ID" }), + awsSecretAccessKey: Type.String({ + description: "AWS Secret Access Key", + }), + }, + { + description: + "Optional AWS credentials to use for importing the wallet, if not provided, the default AWS credentials will be used (if available).", + }, + ), + ), }), - gcpKmsKeyVersionId: Type.String({ - description: "GCP KMS key version ID", - examples: ["1"], + // TODO: with next breaking change, only require GCP KMS resource path + Type.Object({ + gcpKmsKeyId: Type.String({ + description: "GCP KMS key ID", + examples: ["12345678-1234-1234-1234-123456789012"], + }), + gcpKmsKeyVersionId: Type.String({ + description: "GCP KMS key version ID", + examples: ["1"], + }), + credentials: Type.Optional( + Type.Object( + { + email: Type.String({ description: "GCP service account email" }), + privateKey: Type.String({ + description: "GCP service account private key", + }), + }, + { + description: + "Optional GCP credentials to use for importing the wallet, if not provided, the default GCP credentials will be used (if available).", + }, + ), + ), }), - }), - Type.Union([ Type.Object({ privateKey: Type.String({ description: "The private key of the wallet to import", @@ -69,7 +101,6 @@ RequestBodySchema.examples = [ password: "password123", }, { - awsKmsKeyId: "12345678-1234-1234-1234-123456789012", awsKmsArn: "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012", }, @@ -112,68 +143,131 @@ export const importBackendWallet = async (fastify: FastifyInstance) => { }, }, handler: async (request, reply) => { - let walletAddress: string; + let walletAddress: string | undefined; + const body = request.body; const config = await getConfig(); - switch (config.walletConfiguration.type) { - case WalletType.local: - // TODO: This is why where zod would be great - const { privateKey, mnemonic, encryptedJson, password } = - request.body as any; - - if (privateKey) { - walletAddress = await importLocalWallet({ - method: "privateKey", - privateKey, - }); - } else if (mnemonic) { - walletAddress = await importLocalWallet({ - method: "mnemonic", - mnemonic, - }); - } else if (encryptedJson && password) { - walletAddress = await importLocalWallet({ - method: "encryptedJson", - encryptedJson, - password, - }); - } else { - throw createCustomError( - `Please provide either 'privateKey', 'mnemonic', or 'encryptedJson' & 'password' to import a wallet.`, - StatusCodes.BAD_REQUEST, - "MISSING_PARAMETERS", - ); - } - break; - case WalletType.awsKms: - const { awsKmsArn, awsKmsKeyId } = request.body as any; - if (!(awsKmsArn && awsKmsKeyId)) { - throw createCustomError( - `Please provide 'awsKmsArn' and 'awsKmsKeyId' to import a wallet.`, - StatusCodes.BAD_REQUEST, - "MISSING_PARAMETERS", - ); - } - - walletAddress = await importAwsKmsWallet({ - awsKmsArn, - awsKmsKeyId, - }); - break; - case WalletType.gcpKms: - const { gcpKmsKeyId, gcpKmsKeyVersionId } = request.body as any; - if (!(gcpKmsKeyId && gcpKmsKeyVersionId)) { - throw createCustomError( - `Please provide 'gcpKmsKeyId' and 'gcpKmsKeyVersionId' to import a wallet.`, - StatusCodes.BAD_REQUEST, - "MISSING_PARAMETERS", - ); - } - - walletAddress = await importGcpKmsWallet({ - gcpKmsKeyId, - gcpKmsKeyVersionId, - }); - break; + + // AWS KMS + if ("awsKmsArn" in body) { + const { awsKmsArn, label, credentials } = body; + + const secretAccessKey = + credentials?.awsSecretAccessKey ?? + config.walletConfiguration.aws?.awsSecretAccessKey; + const accessKeyId = + credentials?.awsAccessKeyId ?? + config.walletConfiguration.aws?.awsAccessKeyId; + + if (!(accessKeyId && secretAccessKey)) { + throw createCustomError( + `Please provide 'awsAccessKeyId' and 'awsSecretAccessKey' to import a wallet. Can be provided as configuration or as credential with the request.`, + StatusCodes.BAD_REQUEST, + "MISSING_PARAMETERS", + ); + } + + walletAddress = await importAwsKmsWallet({ + awsKmsArn, + crendentials: { + accessKeyId, + secretAccessKey, + }, + label, + }); + } + + if ("gcpKmsKeyId" in body && !walletAddress) { + const { gcpKmsKeyId, gcpKmsKeyVersionId, credentials, label } = body; + + const email = + credentials?.email ?? + config.walletConfiguration.gcp?.gcpApplicationCredentialEmail; + const privateKey = + credentials?.privateKey ?? + config.walletConfiguration.gcp?.gcpApplicationCredentialPrivateKey; + + if (!(email && privateKey)) { + throw createCustomError( + `Please provide 'email' and 'privateKey' to import a wallet. Can be provided as configuration or as credential with the request.`, + StatusCodes.BAD_REQUEST, + "MISSING_PARAMETERS", + ); + } + + // TODO: with next breaking change, only require GCP KMS resource path + // import endoint does not currently have resource path in the request body + // so we rely on the global configuration for these values + if ( + !( + config.walletConfiguration.gcp?.defaultGcpKmsKeyRingId && + config.walletConfiguration.gcp?.defaultGcpApplicationProjectId && + config.walletConfiguration.gcp?.defaultGcpKmsLocationId + ) + ) { + throw createCustomError( + "GCP KMS location ID, project ID, and key ring ID are required configuration for this wallet type", + StatusCodes.BAD_REQUEST, + "MISSING_PARAMETERS", + ); + } + + const gcpKmsResourcePath = getGcpKmsResourcePath({ + locationId: config.walletConfiguration.gcp.defaultGcpKmsLocationId, + keyRingId: config.walletConfiguration.gcp.defaultGcpKmsKeyRingId, + cryptoKeyId: gcpKmsKeyId, + projectId: + config.walletConfiguration.gcp.defaultGcpApplicationProjectId, + versionId: gcpKmsKeyVersionId, + }); + + const walletAddress = await importGcpKmsWallet({ + gcpKmsResourcePath, + credentials: { + email, + privateKey, + }, + label, + }); + + return reply.status(StatusCodes.OK).send({ + result: { + walletAddress, + status: "success", + }, + }); + } + + if ("privateKey" in body && !walletAddress) { + walletAddress = await importLocalWallet({ + method: "privateKey", + privateKey: body.privateKey, + label: body.label, + }); + } + + if ("mnemonic" in body && !walletAddress) { + walletAddress = await importLocalWallet({ + method: "mnemonic", + mnemonic: body.mnemonic, + label: body.label, + }); + } + + if ("encryptedJson" in body && !walletAddress) { + walletAddress = await importLocalWallet({ + method: "encryptedJson", + encryptedJson: body.encryptedJson, + password: body.password, + label: body.label, + }); + } + + if (!walletAddress) { + throw createCustomError( + "Invalid request body, please provide a valid wallet import method", + StatusCodes.BAD_REQUEST, + "INVALID_REQUEST", + ); } reply.status(StatusCodes.OK).send({ diff --git a/src/server/routes/configuration/wallets/get.ts b/src/server/routes/configuration/wallets/get.ts index d77e97bd0..d170cbea5 100644 --- a/src/server/routes/configuration/wallets/get.ts +++ b/src/server/routes/configuration/wallets/get.ts @@ -1,30 +1,24 @@ -import { Static, Type } from "@sinclair/typebox"; -import { FastifyInstance } from "fastify"; +import { Type, type Static } from "@sinclair/typebox"; +import type { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { WalletType } from "../../../../schema/wallet"; import { getConfig } from "../../../../utils/cache/getConfig"; import { standardResponseSchema } from "../../../schemas/sharedApiSchemas"; export const responseBodySchema = Type.Object({ - result: Type.Union([ - Type.Object({ - type: Type.Literal(WalletType.local), - }), - Type.Object({ - type: Type.Literal(WalletType.awsKms), - awsAccessKeyId: Type.String(), - awsRegion: Type.String(), - // Omit awsSecretAccessKey - }), - Type.Object({ - type: Type.Literal(WalletType.gcpKms), - gcpApplicationProjectId: Type.String(), - gcpKmsLocationId: Type.String(), - gcpKmsKeyRingId: Type.String(), - gcpApplicationCredentialEmail: Type.String(), - // Omit gcpApplicationCredentialPrivateKey - }), - ]), + result: Type.Object({ + type: Type.Enum(WalletType), + + awsAccessKeyId: Type.Union([Type.String(), Type.Null()]), + awsRegion: Type.Union([Type.String(), Type.Null()]), + + // Omit awsSecretAccessKey + gcpApplicationProjectId: Type.Union([Type.String(), Type.Null()]), + gcpKmsLocationId: Type.Union([Type.String(), Type.Null()]), + gcpKmsKeyRingId: Type.Union([Type.String(), Type.Null()]), + gcpApplicationCredentialEmail: Type.Union([Type.String(), Type.Null()]), + // Omit gcpApplicationCredentialPrivateKey + }), }); export async function getWalletsConfiguration(fastify: FastifyInstance) { @@ -43,10 +37,23 @@ export async function getWalletsConfiguration(fastify: FastifyInstance) { [StatusCodes.OK]: responseBodySchema, }, }, - handler: async (req, res) => { + handler: async (_req, res) => { const config = await getConfig(); + + const { legacyWalletType_removeInNextBreakingChange, aws, gcp } = + config.walletConfiguration; + res.status(StatusCodes.OK).send({ - result: config.walletConfiguration, + result: { + type: legacyWalletType_removeInNextBreakingChange, + awsAccessKeyId: aws?.awsAccessKeyId ?? null, + awsRegion: aws?.defaultAwsRegion ?? null, + gcpApplicationProjectId: gcp?.defaultGcpApplicationProjectId ?? null, + gcpKmsLocationId: gcp?.defaultGcpKmsLocationId ?? null, + gcpKmsKeyRingId: gcp?.defaultGcpKmsKeyRingId ?? null, + gcpApplicationCredentialEmail: + gcp?.gcpApplicationCredentialEmail ?? null, + }, }); }, }); diff --git a/src/server/routes/configuration/wallets/update.ts b/src/server/routes/configuration/wallets/update.ts index 07566a17f..d21182227 100644 --- a/src/server/routes/configuration/wallets/update.ts +++ b/src/server/routes/configuration/wallets/update.ts @@ -1,5 +1,5 @@ -import { Static, Type } from "@sinclair/typebox"; -import { FastifyInstance } from "fastify"; +import { Type, type Static } from "@sinclair/typebox"; +import type { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { updateConfiguration } from "../../../../db/configuration/updateConfiguration"; import { WalletType } from "../../../../schema/wallet"; @@ -10,16 +10,11 @@ import { responseBodySchema } from "./get"; const requestBodySchema = Type.Union([ Type.Object({ - type: Type.Literal(WalletType.local), - }), - Type.Object({ - type: Type.Literal(WalletType.awsKms), awsAccessKeyId: Type.String(), awsSecretAccessKey: Type.String(), awsRegion: Type.String(), }), Type.Object({ - type: Type.Literal(WalletType.gcpKms), gcpApplicationProjectId: Type.String(), gcpKmsLocationId: Type.String(), gcpKmsKeyRingId: Type.String(), @@ -68,76 +63,66 @@ export async function updateWalletsConfiguration(fastify: FastifyInstance) { }, }, handler: async (req, res) => { - switch (req.body.type) { - case WalletType.local: - await updateConfiguration({ - awsAccessKeyId: null, - awsSecretAccessKey: null, - awsRegion: null, - gcpApplicationProjectId: null, - gcpKmsLocationId: null, - gcpKmsKeyRingId: null, - gcpApplicationCredentialEmail: null, - gcpApplicationCredentialPrivateKey: null, - }); - break; - case WalletType.awsKms: - if ( - !req.body.awsAccessKeyId || - !req.body.awsSecretAccessKey || - !req.body.awsRegion - ) { - throw createCustomError( - "Please specify all AWS KMS configuration.", - StatusCodes.BAD_REQUEST, - "BAD_REQUEST", - ); - } + if ("awsAccessKeyId" in req.body) { + if ( + !req.body.awsAccessKeyId || + !req.body.awsSecretAccessKey || + !req.body.awsRegion + ) { + throw createCustomError( + "Please specify all AWS KMS configuration.", + StatusCodes.BAD_REQUEST, + "BAD_REQUEST", + ); + } + await updateConfiguration({ + awsAccessKeyId: req.body.awsAccessKeyId, + awsSecretAccessKey: req.body.awsSecretAccessKey, + awsRegion: req.body.awsRegion, + }); + } - await updateConfiguration({ - awsAccessKeyId: req.body.awsAccessKeyId, - awsSecretAccessKey: req.body.awsSecretAccessKey, - awsRegion: req.body.awsRegion, - gcpApplicationProjectId: null, - gcpKmsLocationId: null, - gcpKmsKeyRingId: null, - gcpApplicationCredentialEmail: null, - gcpApplicationCredentialPrivateKey: null, - }); - break; - case WalletType.gcpKms: - if ( - !req.body.gcpApplicationProjectId || - !req.body.gcpKmsLocationId || - !req.body.gcpKmsKeyRingId || - !req.body.gcpApplicationCredentialEmail || - !req.body.gcpApplicationCredentialPrivateKey - ) { - throw createCustomError( - "Please specify all GCP KMS configuration.", - StatusCodes.BAD_REQUEST, - "BAD_REQUEST", - ); - } + if ("gcpApplicationProjectId" in req.body) { + if ( + !req.body.gcpApplicationProjectId || + !req.body.gcpKmsLocationId || + !req.body.gcpKmsKeyRingId || + !req.body.gcpApplicationCredentialEmail || + !req.body.gcpApplicationCredentialPrivateKey + ) { + throw createCustomError( + "Please specify all GCP KMS configuration.", + StatusCodes.BAD_REQUEST, + "BAD_REQUEST", + ); + } - await updateConfiguration({ - awsAccessKeyId: null, - awsSecretAccessKey: null, - awsRegion: null, - gcpApplicationProjectId: req.body.gcpApplicationProjectId, - gcpKmsLocationId: req.body.gcpKmsLocationId, - gcpKmsKeyRingId: req.body.gcpKmsKeyRingId, - gcpApplicationCredentialEmail: - req.body.gcpApplicationCredentialEmail, - gcpApplicationCredentialPrivateKey: - req.body.gcpApplicationCredentialPrivateKey, - }); - break; + await updateConfiguration({ + gcpApplicationProjectId: req.body.gcpApplicationProjectId, + gcpKmsLocationId: req.body.gcpKmsLocationId, + gcpKmsKeyRingId: req.body.gcpKmsKeyRingId, + gcpApplicationCredentialEmail: req.body.gcpApplicationCredentialEmail, + gcpApplicationCredentialPrivateKey: + req.body.gcpApplicationCredentialPrivateKey, + }); } const config = await getConfig(false); + + const { legacyWalletType_removeInNextBreakingChange, aws, gcp } = + config.walletConfiguration; + res.status(StatusCodes.OK).send({ - result: config.walletConfiguration, + result: { + type: legacyWalletType_removeInNextBreakingChange, + awsAccessKeyId: aws?.awsAccessKeyId ?? null, + awsRegion: aws?.defaultAwsRegion ?? null, + gcpApplicationProjectId: gcp?.defaultGcpApplicationProjectId ?? null, + gcpKmsLocationId: gcp?.defaultGcpKmsLocationId ?? null, + gcpKmsKeyRingId: gcp?.defaultGcpKmsKeyRingId ?? null, + gcpApplicationCredentialEmail: + gcp?.gcpApplicationCredentialEmail ?? null, + }, }); }, }); diff --git a/src/server/routes/system/health.ts b/src/server/routes/system/health.ts index 8eb857437..70a95db0f 100644 --- a/src/server/routes/system/health.ts +++ b/src/server/routes/system/health.ts @@ -6,7 +6,11 @@ import { env } from "../../../utils/env"; import { isRedisReachable } from "../../../utils/redis/redis"; import { createCustomError } from "../../middleware/error"; -type EngineFeature = "KEYPAIR_AUTH" | "CONTRACT_SUBSCRIPTIONS" | "IP_ALLOWLIST"; +type EngineFeature = + | "KEYPAIR_AUTH" + | "CONTRACT_SUBSCRIPTIONS" + | "IP_ALLOWLIST" + | "HETEROGENEOUS_WALLET_TYPES"; const ReplySchemaOk = Type.Object({ status: Type.String(), @@ -17,6 +21,7 @@ const ReplySchemaOk = Type.Object({ Type.Literal("KEYPAIR_AUTH"), Type.Literal("CONTRACT_SUBSCRIPTIONS"), Type.Literal("IP_ALLOWLIST"), + Type.Literal("HETEROGENEOUS_WALLET_TYPES"), ]), ), }); @@ -44,7 +49,7 @@ export async function healthCheck(fastify: FastifyInstance) { [StatusCodes.SERVICE_UNAVAILABLE]: ReplySchemaError, }, }, - handler: async (req, res) => { + handler: async (_, res) => { if (!(await isDatabaseReachable())) { throw createCustomError( "The database is unreachable.", @@ -72,8 +77,13 @@ export async function healthCheck(fastify: FastifyInstance) { } const getFeatures = (): EngineFeature[] => { - // IP Allowlist is always available as a feature, but added as a feature for backwards compatibility. - const features: EngineFeature[] = ["CONTRACT_SUBSCRIPTIONS", "IP_ALLOWLIST"]; + // Default list is populated with features that are always enabled. + // Added here to make dashboard UI backwards compatible. + const features: EngineFeature[] = [ + "IP_ALLOWLIST", + "HETEROGENEOUS_WALLET_TYPES", + "CONTRACT_SUBSCRIPTIONS" + ]; if (env.ENABLE_KEYPAIR_AUTH) features.push("KEYPAIR_AUTH"); diff --git a/src/server/utils/wallets/awsKmsArn.ts b/src/server/utils/wallets/awsKmsArn.ts new file mode 100644 index 000000000..29f4b32da --- /dev/null +++ b/src/server/utils/wallets/awsKmsArn.ts @@ -0,0 +1,33 @@ +/** + * Split an AWS KMS ARN into its parts. + */ +export function splitAwsKmsArn(arn: string) { + // arn:aws:kms:::key/ + const parts = arn.split(":"); + if (parts.length < 6) { + throw new Error("Invalid AWS KMS ARN"); + } + + const keyId = parts[5].split("/")[1]; + if (!keyId) { + throw new Error("Invalid AWS KMS ARN"); + } + const keyIdExtension = parts.slice(6).join(":"); + + return { + region: parts[3], + accountId: parts[4], + keyId: `${keyId}${keyIdExtension ? `:${keyIdExtension}` : ""}`, + }; +} + +/** + * Get an AWS KMS ARN from its parts. + */ +export function getAwsKmsArn(options: { + region: string; + accountId: string; + keyId: string; +}) { + return `arn:aws:kms:${options.region}:${options.accountId}:key/${options.keyId}`; +} diff --git a/src/server/utils/wallets/createAwsKmsWallet.ts b/src/server/utils/wallets/createAwsKmsWallet.ts index fc5fe047b..c4cdd03c5 100644 --- a/src/server/utils/wallets/createAwsKmsWallet.ts +++ b/src/server/utils/wallets/createAwsKmsWallet.ts @@ -1,25 +1,42 @@ import { CreateKeyCommand, KMSClient } from "@aws-sdk/client-kms"; -import { WalletType } from "../../../schema/wallet"; -import { getConfig } from "../../../utils/cache/getConfig"; +import { + FetchAwsKmsWalletParamsError, + fetchAwsKmsWalletParams, + type AwsKmsWalletParams, +} from "./fetchAwsKmsWalletParams"; import { importAwsKmsWallet } from "./importAwsKmsWallet"; -interface CreateAwsKmsWalletParams { +type CreateAwsKmsWalletParams = { label?: string; -} +} & Partial; +export class CreateAwsKmsWalletError extends Error {} + +/** + * Create an AWS KMS wallet, and store it into the database + * All optional parameters are overrides for the configuration in the database + * If any required parameter cannot be resolved from either the configuration or the overrides, an error is thrown. + * If credentials (awsAccessKeyId and awsSecretAccessKey) are explicitly provided, they will be stored separately from the global configuration + */ export const createAwsKmsWallet = async ({ label, + ...overrides }: CreateAwsKmsWalletParams): Promise => { - const config = await getConfig(); - if (config.walletConfiguration.type !== WalletType.awsKms) { - throw new Error(`Server was not configured for AWS KMS wallet creation`); + let kmsWalletParams: AwsKmsWalletParams; + try { + kmsWalletParams = await fetchAwsKmsWalletParams(overrides); + } catch (e) { + if (e instanceof FetchAwsKmsWalletParamsError) { + throw new CreateAwsKmsWalletError(e.message); + } + throw e; } const client = new KMSClient({ - region: config.walletConfiguration.awsRegion, + region: kmsWalletParams.awsRegion, credentials: { - accessKeyId: config.walletConfiguration.awsAccessKeyId, - secretAccessKey: config.walletConfiguration.awsSecretAccessKey, + accessKeyId: kmsWalletParams.awsAccessKeyId, + secretAccessKey: kmsWalletParams.awsSecretAccessKey, }, }); @@ -32,8 +49,17 @@ export const createAwsKmsWallet = async ({ }), ); - const awsKmsArn = res.KeyMetadata!.Arn!; - const awsKmsKeyId = res.KeyMetadata!.KeyId!; + if (!res.KeyMetadata?.Arn) { + throw new Error("Failed to create AWS KMS key"); + } - return importAwsKmsWallet({ awsKmsArn, awsKmsKeyId, label }); + const awsKmsArn = res.KeyMetadata.Arn; + return importAwsKmsWallet({ + awsKmsArn, + label, + crendentials: { + accessKeyId: kmsWalletParams.awsAccessKeyId, + secretAccessKey: kmsWalletParams.awsSecretAccessKey, + }, + }); }; diff --git a/src/server/utils/wallets/createGcpKmsWallet.ts b/src/server/utils/wallets/createGcpKmsWallet.ts index 464fa335f..47f5e9523 100644 --- a/src/server/utils/wallets/createGcpKmsWallet.ts +++ b/src/server/utils/wallets/createGcpKmsWallet.ts @@ -1,38 +1,58 @@ import { KeyManagementServiceClient } from "@google-cloud/kms"; import { createWalletDetails } from "../../../db/wallets/createWalletDetails"; import { WalletType } from "../../../schema/wallet"; -import { getConfig } from "../../../utils/cache/getConfig"; -import { getGcpKmsWallet } from "./getGcpKmsWallet"; +import { thirdwebClient } from "../../../utils/sdk"; +import { + FetchGcpKmsWalletParamsError, + fetchGcpKmsWalletParams, + type GcpKmsWalletParams, +} from "./fetchGcpKmsWalletParams"; +import { getGcpKmsResourcePath } from "./gcpKmsResourcePath"; +import { getGcpKmsAccount } from "./getGcpKmsAccount"; -interface CreateGcpKmsWallet { +type CreateGcpKmsWallet = { label?: string; -} +} & Partial; +export class CreateGcpKmsWalletError extends Error {} + +/** + * Create an GCP KMS wallet, and store it into the database + * All optional parameters are overrides for the configuration in the database + * If any required parameter cannot be resolved from either the configuration or the overrides, an error is thrown. + * If credentials (gcpApplicationCredentialEmail and gcpApplicationCredentialPrivateKey) are explicitly provided, they will be stored separately from the global configuration + */ export const createGcpKmsWallet = async ({ label, + ...overrides }: CreateGcpKmsWallet): Promise => { - const config = await getConfig(); - if (config.walletConfiguration.type !== WalletType.gcpKms) { - throw new Error(`Server was not configured for GCP KMS wallet creation`); + let params: GcpKmsWalletParams; + try { + params = await fetchGcpKmsWalletParams(overrides); + } catch (e) { + if (e instanceof FetchGcpKmsWalletParamsError) { + throw new CreateGcpKmsWalletError(e.message); + } + throw e; } const client = new KeyManagementServiceClient({ credentials: { - client_email: config.walletConfiguration.gcpApplicationCredentialEmail, - private_key: config.walletConfiguration.gcpApplicationCredentialPrivateKey + client_email: params.gcpApplicationCredentialEmail, + private_key: params.gcpApplicationCredentialPrivateKey .split(String.raw`\n`) .join("\n"), }, - projectId: config.walletConfiguration.gcpApplicationProjectId, + projectId: params.gcpApplicationProjectId, }); // TODO: What should we set this to? const cryptoKeyId = `ec-web3api-${new Date().getTime()}`; const [key] = await client.createCryptoKey({ parent: client.keyRingPath( - config.walletConfiguration.gcpApplicationProjectId, - config.walletConfiguration.gcpKmsLocationId, - config.walletConfiguration.gcpKmsKeyRingId, + params.gcpApplicationProjectId, + params.gcpKmsLocationId, + params.gcpKmsKeyRingId, ), cryptoKeyId, cryptoKey: { @@ -46,21 +66,42 @@ export const createGcpKmsWallet = async ({ await client.close(); - const wallet = await getGcpKmsWallet({ - gcpKmsKeyId: cryptoKeyId, - gcpKmsKeyVersionId: "1", + const resourcePath = getGcpKmsResourcePath({ + projectId: params.gcpApplicationProjectId, + locationId: params.gcpKmsLocationId, + keyRingId: params.gcpKmsKeyRingId, + cryptoKeyId: cryptoKeyId, + versionId: "1", }); - const walletAddress = await wallet.getAddress(); + if (`${key.name}/cryptoKeyVersions/1` !== resourcePath) { + throw new CreateGcpKmsWalletError( + `Expected created key resource path to be ${resourcePath}, but got ${key.name}`, + ); + } + + const account = await getGcpKmsAccount({ + client: thirdwebClient, + name: resourcePath, + clientOptions: { + credentials: { + client_email: params.gcpApplicationCredentialEmail, + private_key: params.gcpApplicationCredentialPrivateKey, + }, + }, + }); + + const walletAddress = account.address; + await createWalletDetails({ type: WalletType.gcpKms, address: walletAddress, label, - gcpKmsKeyId: cryptoKeyId, - gcpKmsKeyRingId: config.walletConfiguration.gcpKmsKeyRingId, - gcpKmsLocationId: config.walletConfiguration.gcpKmsLocationId, - gcpKmsKeyVersionId: "1", - gcpKmsResourcePath: `${key.name!}/cryptoKeysVersion/1`, + gcpKmsResourcePath: resourcePath, + + gcpApplicationCredentialEmail: params.gcpApplicationCredentialEmail, + gcpApplicationCredentialPrivateKey: + params.gcpApplicationCredentialPrivateKey, }); return walletAddress; diff --git a/src/server/utils/wallets/createLocalWallet.ts b/src/server/utils/wallets/createLocalWallet.ts index b6ffd5cd3..d05c77c42 100644 --- a/src/server/utils/wallets/createLocalWallet.ts +++ b/src/server/utils/wallets/createLocalWallet.ts @@ -1,6 +1,4 @@ import { LocalWallet } from "@thirdweb-dev/wallets"; -import { WalletType } from "../../../schema/wallet"; -import { getConfig } from "../../../utils/cache/getConfig"; import { env } from "../../../utils/env"; import { LocalFileStorage } from "../storage/localStorage"; @@ -11,11 +9,6 @@ interface CreateLocalWallet { export const createLocalWallet = async ({ label, }: CreateLocalWallet): Promise => { - const config = await getConfig(); - if (config.walletConfiguration.type !== WalletType.local) { - throw new Error(`Server was not configured for local wallet creation.`); - } - const wallet = new LocalWallet(); const walletAddress = await wallet.generate(); diff --git a/src/server/utils/wallets/fetchAwsKmsWalletParams.ts b/src/server/utils/wallets/fetchAwsKmsWalletParams.ts new file mode 100644 index 000000000..f94cd0f1f --- /dev/null +++ b/src/server/utils/wallets/fetchAwsKmsWalletParams.ts @@ -0,0 +1,54 @@ +import { getConfig } from "../../../utils/cache/getConfig"; + +export type AwsKmsWalletParams = { + awsAccessKeyId: string; + awsSecretAccessKey: string; + + awsRegion: string; +}; + +export class FetchAwsKmsWalletParamsError extends Error {} + +/** + * Fetches the AWS KMS wallet creation parameters from the configuration or overrides. + * If any required parameter cannot be resolved from either the configuration or the overrides, an error is thrown. + */ +export async function fetchAwsKmsWalletParams( + overrides: Partial, +): Promise { + const config = await getConfig(); + + const awsAccessKeyId = + overrides.awsAccessKeyId ?? config.walletConfiguration.aws?.awsAccessKeyId; + + if (!awsAccessKeyId) { + throw new FetchAwsKmsWalletParamsError( + "AWS access key ID is required for this wallet type. Could not find in configuration or params.", + ); + } + + const awsSecretAccessKey = + overrides.awsSecretAccessKey ?? + config.walletConfiguration.aws?.awsSecretAccessKey; + + if (!awsSecretAccessKey) { + throw new FetchAwsKmsWalletParamsError( + "AWS secretAccessKey is required for this wallet type. Could not find in configuration or params.", + ); + } + + const awsRegion = + overrides.awsRegion ?? config.walletConfiguration.aws?.defaultAwsRegion; + + if (!awsRegion) { + throw new FetchAwsKmsWalletParamsError( + "AWS region is required for this wallet type. Could not find in configuration or params.", + ); + } + + return { + awsAccessKeyId, + awsSecretAccessKey, + awsRegion, + }; +} diff --git a/src/server/utils/wallets/fetchGcpKmsWalletParams.ts b/src/server/utils/wallets/fetchGcpKmsWalletParams.ts new file mode 100644 index 000000000..b0c468ebc --- /dev/null +++ b/src/server/utils/wallets/fetchGcpKmsWalletParams.ts @@ -0,0 +1,80 @@ +import { getConfig } from "../../../utils/cache/getConfig"; + +export type GcpKmsWalletParams = { + gcpApplicationCredentialEmail: string; + gcpApplicationCredentialPrivateKey: string; + + gcpApplicationProjectId: string; + gcpKmsKeyRingId: string; + gcpKmsLocationId: string; +}; + +export class FetchGcpKmsWalletParamsError extends Error {} + +/** + * Fetches the GCP KMS wallet creation parameters from the configuration or overrides. + * If any required parameter cannot be resolved from either the configuration or the overrides, an error is thrown. + */ +export async function fetchGcpKmsWalletParams( + overrides: Partial, +) { + const config = await getConfig(); + + const gcpApplicationCredentialEmail = + overrides.gcpApplicationCredentialEmail ?? + config.walletConfiguration.gcp?.gcpApplicationCredentialEmail; + + if (!gcpApplicationCredentialEmail) { + throw new FetchGcpKmsWalletParamsError( + "GCP application credential email is required for this wallet type. Could not find in configuration or params.", + ); + } + + const gcpApplicationCredentialPrivateKey = + overrides.gcpApplicationCredentialPrivateKey ?? + config.walletConfiguration.gcp?.gcpApplicationCredentialPrivateKey; + + if (!gcpApplicationCredentialPrivateKey) { + throw new FetchGcpKmsWalletParamsError( + "GCP application credential private key is required for this wallet type. Could not find in configuration or params.", + ); + } + + const gcpApplicationProjectId = + overrides.gcpApplicationProjectId ?? + config.walletConfiguration.gcp?.defaultGcpApplicationProjectId; + + if (!gcpApplicationProjectId) { + throw new FetchGcpKmsWalletParamsError( + "GCP application project ID is required for this wallet type. Could not find in configuration or params.", + ); + } + + const gcpKmsKeyRingId = + overrides.gcpKmsKeyRingId ?? + config.walletConfiguration.gcp?.defaultGcpKmsKeyRingId; + + if (!gcpKmsKeyRingId) { + throw new FetchGcpKmsWalletParamsError( + "GCP KMS key ring ID is required for this wallet type. Could not find in configuration or params.", + ); + } + + const gcpKmsLocationId = + overrides.gcpKmsLocationId ?? + config.walletConfiguration.gcp?.defaultGcpKmsLocationId; + + if (!gcpKmsLocationId) { + throw new FetchGcpKmsWalletParamsError( + "GCP KMS location ID is required for this wallet type. Could not find in configuration or params.", + ); + } + + return { + gcpApplicationCredentialEmail, + gcpApplicationCredentialPrivateKey, + gcpApplicationProjectId, + gcpKmsKeyRingId, + gcpKmsLocationId, + }; +} diff --git a/src/server/utils/wallets/gcpKmsResourcePath.ts b/src/server/utils/wallets/gcpKmsResourcePath.ts new file mode 100644 index 000000000..160fb1640 --- /dev/null +++ b/src/server/utils/wallets/gcpKmsResourcePath.ts @@ -0,0 +1,31 @@ +/** + * Split a GCP KMS resource path into its parts. + */ +export function splitGcpKmsResourcePath(resourcePath: string) { + const parts = resourcePath.split("/"); + + if (parts.length < 9) { + throw new Error("Invalid GCP KMS resource path"); + } + + return { + projectId: parts[1], + locationId: parts[3], + keyRingId: parts[5], + cryptoKeyId: parts[7], + versionId: parts[9], + }; +} + +/** + * Get a GCP KMS resource path from its parts. + */ +export function getGcpKmsResourcePath(options: { + locationId: string; + keyRingId: string; + cryptoKeyId: string; + versionId: string; + projectId: string; +}) { + return `projects/${options.projectId}/locations/${options.locationId}/keyRings/${options.keyRingId}/cryptoKeys/${options.cryptoKeyId}/cryptoKeyVersions/${options.versionId}`; +} diff --git a/src/server/utils/wallets/getAwsKmsAccount.ts b/src/server/utils/wallets/getAwsKmsAccount.ts new file mode 100644 index 000000000..575cc1962 --- /dev/null +++ b/src/server/utils/wallets/getAwsKmsAccount.ts @@ -0,0 +1,129 @@ +import type { KMSClientConfig } from "@aws-sdk/client-kms"; +import { KmsSigner } from "aws-kms-signer"; +import type { Hex, ThirdwebClient } from "thirdweb"; +import { + eth_sendRawTransaction, + getRpcClient, + keccak256, + type Address, +} from "thirdweb"; +import { serializeTransaction } from "thirdweb/transaction"; +import { toBytes } from "thirdweb/utils"; +import type { Account } from "thirdweb/wallets"; +import type { + SignableMessage, + TransactionSerializable, + TypedData, + TypedDataDefinition, +} from "viem"; +import { hashTypedData } from "viem"; +import { getChain } from "../../../utils/chain"; + +type SendTransactionResult = { + transactionHash: Hex; +}; + +type SendTransactionOption = TransactionSerializable & { + chainId: number; +}; + +type AwsKmsAccountOptions = { + keyId: string; + config?: KMSClientConfig; + client: ThirdwebClient; +}; + +type AwsKmsAccount = Account; + +export async function getAwsKmsAccount( + options: AwsKmsAccountOptions, +): Promise { + const { keyId, config, client } = options; + const signer = new KmsSigner(keyId, config); + + // Populate address immediately + const addressUnprefixed = await signer.getAddress(); + const address = `0x${addressUnprefixed}` as Address; + + async function signTransaction(tx: TransactionSerializable): Promise { + const serializedTx = serializeTransaction({ transaction: tx }); + const txHash = keccak256(serializedTx); + const signature = await signer.sign(Buffer.from(txHash.slice(2), "hex")); + + const r = `0x${signature.r.toString("hex")}` as Hex; + const s = `0x${signature.s.toString("hex")}` as Hex; + const v = signature.v; + + const yParity = v % 2 === 0 ? 1 : (0 as 0 | 1); + + const signedTx = serializeTransaction({ + transaction: tx, + signature: { + r, + s, + yParity, + }, + }); + + return signedTx; + } + + /** + * Sign a message with the account's private key. + * If the message is a string, it will be prefixed with the Ethereum message prefix. + * If the message is an object with a `raw` property, it will be signed as-is. + */ + async function signMessage({ + message, + }: { + message: SignableMessage; + }): Promise { + let messageHash: Hex; + if (typeof message === "string") { + const prefixedMessage = `\x19Ethereum Signed Message:\n${message.length}${message}`; + messageHash = keccak256(toBytes(prefixedMessage)); + } else if ("raw" in message) { + messageHash = keccak256(message.raw); + } else { + throw new Error("Invalid message format"); + } + + const signature = await signer.sign( + Buffer.from(messageHash.slice(2), "hex"), + ); + return `0x${signature.toString()}`; + } + + async function signTypedData< + const typedData extends TypedData | Record, + primaryType extends keyof typedData | "EIP712Domain" = keyof typedData, + >(_typedData: TypedDataDefinition): Promise { + const typedDataHash = hashTypedData(_typedData); + const signature = await signer.sign( + Buffer.from(typedDataHash.slice(2), "hex"), + ); + return `0x${signature.toString()}`; + } + + async function sendTransaction( + tx: SendTransactionOption, + ): Promise { + const rpcRequest = getRpcClient({ + client: client, + chain: await getChain(tx.chainId), + }); + + const signedTx = await signTransaction(tx); + + const transactionHash = await eth_sendRawTransaction(rpcRequest, signedTx); + return { transactionHash }; + } + + return { + address, + sendTransaction, + signMessage, + signTypedData, + signTransaction, + } satisfies Account; +} diff --git a/src/server/utils/wallets/getAwsKmsWallet.ts b/src/server/utils/wallets/getAwsKmsWallet.ts deleted file mode 100644 index ae4863b15..000000000 --- a/src/server/utils/wallets/getAwsKmsWallet.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { AwsKmsWallet } from "@thirdweb-dev/wallets/evm/wallets/aws-kms"; -import { WalletType } from "../../../schema/wallet"; -import { getConfig } from "../../../utils/cache/getConfig"; - -interface GetAwsKmsWalletParams { - awsKmsKeyId: string; -} - -export const getAwsKmsWallet = async ({ - awsKmsKeyId, -}: GetAwsKmsWalletParams) => { - const config = await getConfig(); - if (config.walletConfiguration.type !== WalletType.awsKms) { - throw new Error(`Server was not configured for AWS KMS wallets.`); - } - - return new AwsKmsWallet({ - region: config.walletConfiguration.awsRegion, - accessKeyId: config.walletConfiguration.awsAccessKeyId, - secretAccessKey: config.walletConfiguration.awsSecretAccessKey, - keyId: awsKmsKeyId, - }); -}; diff --git a/src/server/utils/wallets/getGcpKmsAccount.ts b/src/server/utils/wallets/getGcpKmsAccount.ts new file mode 100644 index 000000000..7160415b9 --- /dev/null +++ b/src/server/utils/wallets/getGcpKmsAccount.ts @@ -0,0 +1,133 @@ +import { CloudKmsSigner } from "@cloud-cryptographic-wallet/cloud-kms-signer"; +import { Bytes } from "@cloud-cryptographic-wallet/signer"; +import type { Hex, ThirdwebClient } from "thirdweb"; +import { + eth_sendRawTransaction, + getAddress, + getRpcClient, + keccak256, +} from "thirdweb"; +import { serializeTransaction } from "thirdweb/transaction"; +import { toBytes } from "thirdweb/utils"; +import type { Account } from "thirdweb/wallets"; +import type { + SignableMessage, + TransactionSerializable, + TypedData, + TypedDataDefinition, +} from "viem"; +import { hashTypedData } from "viem"; +import { getChain } from "../../../utils/chain"; // Adjust import path as needed + +type SendTransactionResult = { + transactionHash: Hex; +}; + +type SendTransactionOption = TransactionSerializable & { + chainId: number; +}; + +type GcpKmsAccountOptions = { + name: string; // GCP KMS key name + clientOptions?: ConstructorParameters[1]; + client: ThirdwebClient; +}; + +type GcpKmsAccount = Account; + +export async function getGcpKmsAccount( + options: GcpKmsAccountOptions, +): Promise { + const { name: unprocessedName, clientOptions, client } = options; + + // we had a bug previously where we previously called it "cryptoKeyVersion" instead of "cryptoKeyVersions" + // if we detect that, we'll fix it here + // TODO: remove this as a breaking change + const name = unprocessedName.includes("cryptoKeyVersions") + ? unprocessedName + : unprocessedName.replace("cryptoKeyVersion", "cryptoKeyVersions"); + + const signer = new CloudKmsSigner(name, clientOptions); + + // Populate address immediately + const publicKey = await signer.getPublicKey(); + const address = getAddress(publicKey.toAddress().toString()); + + async function signTransaction(tx: TransactionSerializable): Promise { + const serializedTx = serializeTransaction({ transaction: tx }); + const txHash = keccak256(serializedTx); + const signature = await signer.sign(Bytes.fromString(txHash.slice(2))); + + const r = signature.r.toString() as Hex; + const s = signature.s.toString() as Hex; + const v = signature.v; + + const yParity = v % 2 === 0 ? 1 : (0 as 0 | 1); + + const signedTx = serializeTransaction({ + transaction: tx, + signature: { + r, + s, + yParity, + }, + }); + + return signedTx; + } + + /** + * Sign a message with the account's private key. + * If the message is a string, it will be prefixed with the Ethereum message prefix. + * If the message is an object with a `raw` property, it will be signed as-is. + */ + async function signMessage({ + message, + }: { + message: SignableMessage; + }): Promise { + let messageHash: Hex; + if (typeof message === "string") { + const prefixedMessage = `\x19Ethereum Signed Message:\n${message.length}${message}`; + messageHash = keccak256(toBytes(prefixedMessage)); + } else if ("raw" in message) { + messageHash = keccak256(message.raw); + } else { + throw new Error("Invalid message format"); + } + + const signature = await signer.sign(Bytes.fromString(messageHash)); + return signature.bytes.toString() as Hex; + } + + async function signTypedData< + const typedData extends TypedData | Record, + primaryType extends keyof typedData | "EIP712Domain" = keyof typedData, + >(_typedData: TypedDataDefinition): Promise { + const typedDataHash = hashTypedData(_typedData); + const signature = await signer.sign(Bytes.fromString(typedDataHash)); + return signature.bytes.toString() as Hex; + } + + async function sendTransaction( + tx: SendTransactionOption, + ): Promise { + const rpcRequest = getRpcClient({ + client: client, + chain: await getChain(tx.chainId), + }); + + const signedTx = await signTransaction(tx); + + const transactionHash = await eth_sendRawTransaction(rpcRequest, signedTx); + return { transactionHash }; + } + + return { + address, + sendTransaction, + signMessage, + signTypedData, + signTransaction, + } satisfies Account; +} diff --git a/src/server/utils/wallets/getGcpKmsWallet.ts b/src/server/utils/wallets/getGcpKmsWallet.ts deleted file mode 100644 index 78cf3e34c..000000000 --- a/src/server/utils/wallets/getGcpKmsWallet.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { GcpKmsWallet } from "@thirdweb-dev/wallets/evm/wallets/gcp-kms"; -import { WalletType } from "../../../schema/wallet"; -import { getConfig } from "../../../utils/cache/getConfig"; - -interface GetGcpKmsWalletParams { - gcpKmsKeyId: string; - gcpKmsKeyVersionId: string; -} - -export const getGcpKmsWallet = async ({ - gcpKmsKeyId, - gcpKmsKeyVersionId, -}: GetGcpKmsWalletParams) => { - const config = await getConfig(); - if (config.walletConfiguration.type !== WalletType.gcpKms) { - throw new Error(`Server was not configured for GCP KMS.`); - } - - return new GcpKmsWallet({ - projectId: config.walletConfiguration.gcpApplicationProjectId, - locationId: config.walletConfiguration.gcpKmsLocationId, - keyRingId: config.walletConfiguration.gcpKmsKeyRingId, - keyId: gcpKmsKeyId, - keyVersion: gcpKmsKeyVersionId, - applicationCredentialEmail: - config.walletConfiguration.gcpApplicationCredentialEmail, - applicationCredentialPrivateKey: - config.walletConfiguration.gcpApplicationCredentialPrivateKey, - }); -}; diff --git a/src/server/utils/wallets/importAwsKmsWallet.ts b/src/server/utils/wallets/importAwsKmsWallet.ts index 104ba98a5..196d2c6b6 100644 --- a/src/server/utils/wallets/importAwsKmsWallet.ts +++ b/src/server/utils/wallets/importAwsKmsWallet.ts @@ -1,33 +1,50 @@ import { createWalletDetails } from "../../../db/wallets/createWalletDetails"; import { WalletType } from "../../../schema/wallet"; -import { getConfig } from "../../../utils/cache/getConfig"; -import { getAwsKmsWallet } from "./getAwsKmsWallet"; +import { thirdwebClient } from "../../../utils/sdk"; +import { splitAwsKmsArn } from "./awsKmsArn"; +import { getAwsKmsAccount } from "./getAwsKmsAccount"; interface ImportAwsKmsWalletParams { - awsKmsKeyId: string; awsKmsArn: string; + crendentials: { + accessKeyId: string; + secretAccessKey: string; + }; label?: string; } +/** + * Import an AWS KMS wallet, and store it into the database + * + */ export const importAwsKmsWallet = async ({ - awsKmsKeyId, + crendentials, awsKmsArn, label, }: ImportAwsKmsWalletParams) => { - const config = await getConfig(); - if (config.walletConfiguration.type !== WalletType.awsKms) { - throw new Error(`Server was not configured for AWS KMS wallet creation`); - } + const { keyId, region } = splitAwsKmsArn(awsKmsArn); + const account = await getAwsKmsAccount({ + client: thirdwebClient, + keyId, + config: { + region, + credentials: { + accessKeyId: crendentials.accessKeyId, + secretAccessKey: crendentials.secretAccessKey, + }, + }, + }); - const wallet = await getAwsKmsWallet({ awsKmsKeyId }); + const walletAddress = account.address; - const walletAddress = await wallet.getAddress(); await createWalletDetails({ type: WalletType.awsKms, address: walletAddress, awsKmsArn, - awsKmsKeyId, label, + + awsKmsAccessKeyId: crendentials.accessKeyId, + awsKmsSecretAccessKey: crendentials.secretAccessKey, }); return walletAddress; diff --git a/src/server/utils/wallets/importGcpKmsWallet.ts b/src/server/utils/wallets/importGcpKmsWallet.ts index 9c94f04ef..5ac149b55 100644 --- a/src/server/utils/wallets/importGcpKmsWallet.ts +++ b/src/server/utils/wallets/importGcpKmsWallet.ts @@ -1,37 +1,49 @@ import { createWalletDetails } from "../../../db/wallets/createWalletDetails"; import { WalletType } from "../../../schema/wallet"; -import { getConfig } from "../../../utils/cache/getConfig"; -import { getGcpKmsWallet } from "./getGcpKmsWallet"; +import { thirdwebClient } from "../../../utils/sdk"; +import { getGcpKmsAccount } from "./getGcpKmsAccount"; interface ImportGcpKmsWalletParams { - gcpKmsKeyId: string; - gcpKmsKeyVersionId: string; + gcpKmsResourcePath: string; label?: string; + credentials: { + email: string; + privateKey: string; + }; } +/** + * Import a GCP KMS wallet, and store it into the database + * + * If credentials.shouldStore is true, the GCP application credential email and private key will be stored + * along with the wallet details, separately from the global configuration + */ export const importGcpKmsWallet = async ({ - gcpKmsKeyId, - gcpKmsKeyVersionId, label, + gcpKmsResourcePath, + credentials, }: ImportGcpKmsWalletParams) => { - const config = await getConfig(); - if (config.walletConfiguration.type !== WalletType.gcpKms) { - throw new Error(`Server was not configured for GCP KMS wallet creation`); - } + const account = await getGcpKmsAccount({ + client: thirdwebClient, + name: gcpKmsResourcePath, + clientOptions: { + credentials: { + client_email: credentials.email, + private_key: credentials.privateKey, + }, + }, + }); - const gcpKmsResourcePath = `projects/${config.walletConfiguration.gcpApplicationProjectId}/locations/${config.walletConfiguration.gcpKmsLocationId}/keyRings/${config.walletConfiguration.gcpKmsKeyRingId}/cryptoKeys/${gcpKmsKeyId}/cryptoKeysVersion/${gcpKmsKeyVersionId}`; - const wallet = await getGcpKmsWallet({ gcpKmsKeyId, gcpKmsKeyVersionId }); + const walletAddress = account.address; - const walletAddress = await wallet.getAddress(); await createWalletDetails({ type: WalletType.gcpKms, address: walletAddress, label, - gcpKmsKeyId: gcpKmsKeyId, - gcpKmsKeyRingId: config.walletConfiguration.gcpKmsKeyRingId, - gcpKmsLocationId: config.walletConfiguration.gcpKmsLocationId, - gcpKmsKeyVersionId: gcpKmsKeyVersionId, gcpKmsResourcePath, + + gcpApplicationCredentialEmail: credentials.email, + gcpApplicationCredentialPrivateKey: credentials.privateKey, }); return walletAddress; diff --git a/src/tests/aws-arn.test.ts b/src/tests/aws-arn.test.ts new file mode 100644 index 000000000..cc885a4a9 --- /dev/null +++ b/src/tests/aws-arn.test.ts @@ -0,0 +1,62 @@ +import { describe, expect, it } from "vitest"; +import { + getAwsKmsArn, + splitAwsKmsArn, +} from "../server/utils/wallets/awsKmsArn"; + +describe("splitAwsKmsArn", () => { + it("should correctly split a valid AWS KMS ARN", () => { + const arn = + "arn:aws:kms:us-west-2:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab"; + const result = splitAwsKmsArn(arn); + expect(result).toEqual({ + region: "us-west-2", + accountId: "123456789012", + keyId: "1234abcd-12ab-34cd-56ef-1234567890ab", + }); + }); + + it("should throw an error for an ARN without a key ID", () => { + const arn = "arn:aws:kms:us-west-2:123456789012:key/"; + expect(() => splitAwsKmsArn(arn)).toThrow("Invalid AWS KMS ARN"); + }); + + it("should throw an error for an ARN with insufficient parts", () => { + const arn = "arn:aws:kms:us-west-2:123456789012"; + expect(() => splitAwsKmsArn(arn)).toThrow("Invalid AWS KMS ARN"); + }); + + it("should handle ARNs with additional parts", () => { + const arn = "arn:aws:kms:us-west-2:123456789012:key/abcdef:extra:parts"; + const result = splitAwsKmsArn(arn); + expect(result).toEqual({ + region: "us-west-2", + accountId: "123456789012", + keyId: "abcdef:extra:parts", + }); + }); +}); + +describe("getAwsKmsArn", () => { + it("should correctly construct an AWS KMS ARN", () => { + const options = { + region: "eu-central-1", + accountId: "987654321098", + keyId: "9876fedc-ba98-7654-3210-fedcba9876543", + }; + const result = getAwsKmsArn(options); + expect(result).toBe( + "arn:aws:kms:eu-central-1:987654321098:key/9876fedc-ba98-7654-3210-fedcba9876543", + ); + }); + + it("should handle numeric account IDs", () => { + const options = { + region: "us-east-1", + accountId: "123456789012", + keyId: "abcdef", + }; + const result = getAwsKmsArn(options); + expect(result).toBe("arn:aws:kms:us-east-1:123456789012:key/abcdef"); + }); +}); diff --git a/src/tests/config/aws-kms.ts b/src/tests/config/aws-kms.ts new file mode 100644 index 000000000..3dd664a45 --- /dev/null +++ b/src/tests/config/aws-kms.ts @@ -0,0 +1,22 @@ +import { assert } from "vitest"; + +const TEST_AWS_KMS_ACCESS_KEY_ID = process.env.TEST_AWS_KMS_ACCESS_KEY_ID; +const TEST_AWS_KMS_SECRET_ACCESS_KEY = + process.env.TEST_AWS_KMS_SECRET_ACCESS_KEY; +const TEST_AWS_KMS_KEY_ID = process.env.TEST_AWS_KMS_KEY_ID; +const TEST_AWS_KMS_REGION = process.env.TEST_AWS_KMS_REGION; + +assert(TEST_AWS_KMS_ACCESS_KEY_ID, "TEST_AWS_KMS_ACCESS_KEY_ID is required"); +assert( + TEST_AWS_KMS_SECRET_ACCESS_KEY, + "TEST_AWS_KMS_SECRET_ACCESS_KEY is required", +); +assert(TEST_AWS_KMS_KEY_ID, "TEST_AWS_KMS_KEY_ID is required"); +assert(TEST_AWS_KMS_REGION, "TEST_AWS_KMS_REGION is required"); + +export const TEST_AWS_KMS_CONFIG = { + accessKeyId: TEST_AWS_KMS_ACCESS_KEY_ID, + secretAccessKey: TEST_AWS_KMS_SECRET_ACCESS_KEY, + region: TEST_AWS_KMS_REGION, + keyId: TEST_AWS_KMS_KEY_ID, +}; diff --git a/src/tests/config/gcp-kms.ts b/src/tests/config/gcp-kms.ts new file mode 100644 index 000000000..a846632a3 --- /dev/null +++ b/src/tests/config/gcp-kms.ts @@ -0,0 +1,15 @@ +import { assert } from "vitest"; + +const TEST_GCP_KMS_RESOURCE_PATH = process.env.TEST_GCP_KMS_RESOURCE_PATH; +const TEST_GCP_KMS_EMAIL = process.env.TEST_GCP_KMS_EMAIL; +const TEST_GCP_KMS_PK = process.env.TEST_GCP_KMS_PK; + +assert(TEST_GCP_KMS_RESOURCE_PATH, "TEST_GCP_KMS_RESOURCE_PATH is required"); +assert(TEST_GCP_KMS_EMAIL, "TEST_GCP_KMS_EMAIL is required"); +assert(TEST_GCP_KMS_PK, "TEST_GCP_KMS_PK is required"); + +export const TEST_GCP_KMS_CONFIG = { + resourcePath: TEST_GCP_KMS_RESOURCE_PATH, + email: TEST_GCP_KMS_EMAIL, + pk: TEST_GCP_KMS_PK, +}; diff --git a/src/tests/gcp-resource-path.test.ts b/src/tests/gcp-resource-path.test.ts new file mode 100644 index 000000000..ae6da618f --- /dev/null +++ b/src/tests/gcp-resource-path.test.ts @@ -0,0 +1,84 @@ +import { describe, expect, it } from "vitest"; +import { + getGcpKmsResourcePath, + splitGcpKmsResourcePath, +} from "../server/utils/wallets/gcpKmsResourcePath"; + +describe("splitGcpKmsResourcePath", () => { + it("should correctly split a valid GCP KMS resource path", () => { + const resourcePath = + "projects/my-project/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key/cryptoKeyVersions/1"; + const result = splitGcpKmsResourcePath(resourcePath); + expect(result).toEqual({ + projectId: "my-project", + locationId: "us-central1", + keyRingId: "my-keyring", + cryptoKeyId: "my-key", + versionId: "1", + }); + }); + + it("should throw an error for a resource path with insufficient parts", () => { + const resourcePath = + "projects/my-project/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key"; + expect(() => splitGcpKmsResourcePath(resourcePath)).toThrow( + "Invalid GCP KMS resource path", + ); + }); + + it("should handle resource paths with additional parts", () => { + const resourcePath = + "projects/my-project/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key/cryptoKeyVersions/1/extra/parts"; + const result = splitGcpKmsResourcePath(resourcePath); + expect(result).toEqual({ + projectId: "my-project", + locationId: "us-central1", + keyRingId: "my-keyring", + cryptoKeyId: "my-key", + versionId: "1", + }); + }); + + it("should handle resource paths with hyphens and numbers in IDs", () => { + const resourcePath = + "projects/my-project-123/locations/us-east1-b/keyRings/key-ring-2/cryptoKeys/crypto-key-3/cryptoKeyVersions/2"; + const result = splitGcpKmsResourcePath(resourcePath); + expect(result).toEqual({ + projectId: "my-project-123", + locationId: "us-east1-b", + keyRingId: "key-ring-2", + cryptoKeyId: "crypto-key-3", + versionId: "2", + }); + }); +}); + +describe("getGcpKmsResourcePath", () => { + it("should correctly construct a GCP KMS resource path", () => { + const options = { + projectId: "my-project", + locationId: "us-central1", + keyRingId: "my-keyring", + cryptoKeyId: "my-key", + versionId: "1", + }; + const result = getGcpKmsResourcePath(options); + expect(result).toBe( + "projects/my-project/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key/cryptoKeyVersions/1", + ); + }); + + it("should handle IDs with hyphens and numbers", () => { + const options = { + projectId: "my-project-123", + locationId: "us-east1-b", + keyRingId: "key-ring-2", + cryptoKeyId: "crypto-key-3", + versionId: "2", + }; + const result = getGcpKmsResourcePath(options); + expect(result).toBe( + "projects/my-project-123/locations/us-east1-b/keyRings/key-ring-2/cryptoKeys/crypto-key-3/cryptoKeyVersions/2", + ); + }); +}); diff --git a/src/tests/shared/chain.ts b/src/tests/shared/chain.ts new file mode 100644 index 000000000..aed41dbdc --- /dev/null +++ b/src/tests/shared/chain.ts @@ -0,0 +1,13 @@ +import { defineChain } from "thirdweb"; +import { anvil } from "thirdweb/chains"; +import { createTestClient, http } from "viem"; + +export const ANVIL_CHAIN = defineChain({ + ...anvil, + rpc: "http://127.0.0.1:8645/1", +}); + +export const anvilTestClient = createTestClient({ + mode: "anvil", + transport: http(ANVIL_CHAIN.rpc), +}); diff --git a/src/tests/shared/client.ts b/src/tests/shared/client.ts new file mode 100644 index 000000000..72af98599 --- /dev/null +++ b/src/tests/shared/client.ts @@ -0,0 +1,5 @@ +import { createThirdwebClient } from "thirdweb"; + +const secretKey = process.env.TW_SECRET_KEY; +if (!secretKey) throw new Error("TW_SECRET_KEY is required"); +export const TEST_CLIENT = createThirdwebClient({ secretKey }); diff --git a/src/tests/shared/typed-data.ts b/src/tests/shared/typed-data.ts new file mode 100644 index 000000000..487a2d2d3 --- /dev/null +++ b/src/tests/shared/typed-data.ts @@ -0,0 +1,86 @@ +export const typedData = { + basic: { + domain: { + name: "Ether Mail", + version: "1", + chainId: 1, + verifyingContract: "0x0000000000000000000000000000000000000000", + }, + types: { + Person: [ + { name: "name", type: "string" }, + { name: "wallet", type: "address" }, + ], + Mail: [ + { name: "from", type: "Person" }, + { name: "to", type: "Person" }, + { name: "contents", type: "string" }, + ], + }, + message: { + from: { + name: "Cow", + wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + to: { + name: "Bob", + wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + contents: "Hello, Bob!", + }, + primaryType: "Mail", + }, + complex: { + domain: { + name: "Ether Mail 🥵", + version: "1.1.1", + chainId: 1, + verifyingContract: "0x0000000000000000000000000000000000000000", + }, + types: { + Name: [ + { name: "first", type: "string" }, + { name: "last", type: "string" }, + ], + Person: [ + { name: "name", type: "Name" }, + { name: "wallet", type: "address" }, + { name: "favoriteColors", type: "string[3]" }, + { name: "foo", type: "uint256" }, + { name: "age", type: "uint8" }, + { name: "isCool", type: "bool" }, + ], + Mail: [ + { name: "timestamp", type: "uint256" }, + { name: "from", type: "Person" }, + { name: "to", type: "Person" }, + { name: "contents", type: "string" }, + { name: "hash", type: "bytes" }, + ], + }, + message: { + timestamp: 1234567890n, + contents: "Hello, Bob! 🖤", + hash: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + from: { + name: { + first: "Cow", + last: "Burns", + }, + wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + age: 69, + foo: 123123123123123123n, + favoriteColors: ["red", "green", "blue"], + isCool: false, + }, + to: { + name: { first: "Bob", last: "Builder" }, + wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + age: 70, + foo: 123123123123123123n, + favoriteColors: ["orange", "yellow", "green"], + isCool: true, + }, + }, + }, +} as const; diff --git a/src/tests/wallets/aws-kms.test.ts b/src/tests/wallets/aws-kms.test.ts new file mode 100644 index 000000000..4d0b7a129 --- /dev/null +++ b/src/tests/wallets/aws-kms.test.ts @@ -0,0 +1,141 @@ +import { beforeAll, expect, test, vi } from "vitest"; + +import { ANVIL_CHAIN, anvilTestClient } from "../shared/chain.ts"; + +import { TEST_AWS_KMS_CONFIG } from "../config/aws-kms.ts"; + +import { typedData } from "../shared/typed-data.ts"; + +import { verifyTypedData } from "thirdweb"; +import { verifyEOASignature } from "thirdweb/auth"; +import { + prepareTransaction, + sendAndConfirmTransaction, +} from "thirdweb/transaction"; +import { toUnits, toWei } from "thirdweb/utils"; +import { getWalletBalance } from "thirdweb/wallets"; +import { getAwsKmsAccount } from "../../server/utils/wallets/getAwsKmsAccount.js"; +import { TEST_CLIENT } from "../shared/client.ts"; + +let account: Awaited>; + +vi.mock("../../utils/chain", () => ({ + getChain: async () => ANVIL_CHAIN, +})); + +beforeAll(async () => { + account = await getAwsKmsAccount({ + keyId: TEST_AWS_KMS_CONFIG.keyId, + client: TEST_CLIENT, + config: { + credentials: { + accessKeyId: TEST_AWS_KMS_CONFIG.accessKeyId, + secretAccessKey: TEST_AWS_KMS_CONFIG.secretAccessKey, + }, + region: TEST_AWS_KMS_CONFIG.region, + }, + }); +}); + +test("account address is valid", () => { + expect(account.address).toMatch(/^0x[a-fA-F0-9]{40}$/); +}); + +test("sign message", async () => { + const message = "hello world"; + const signature = await account.signMessage({ message }); + + expect(signature).toMatch(/^0x[a-fA-F0-9]{130}$/); + + const isValid = await verifyEOASignature({ + address: account.address, + message, + signature, + }); + expect(isValid).toBe(true); +}); + +test("sign transaction", async () => { + const tx = { + chainId: ANVIL_CHAIN.id, + maxFeePerGas: toUnits("20", 9), + gas: 21000n, + to: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + value: toUnits("1", 18), + }; + + expect(account.signTransaction).toBeDefined(); + + const signedTx = await account.signTransaction?.(tx); + expect(signedTx).toMatch(/^0x[a-fA-F0-9]+$/); +}); + +test("sign typed data", async () => { + const signature = await account.signTypedData({ + ...typedData.basic, + primaryType: "Mail", + }); + + expect(signature).toMatch(/^0x[a-fA-F0-9]{130}$/); + + const isValid = await verifyTypedData({ + address: account.address, + ...typedData.basic, + primaryType: "Mail", + signature, + client: TEST_CLIENT, + chain: ANVIL_CHAIN, + }); + expect(isValid).toBe(true); +}); + +test("send transaction", async () => { + const recipient = "0x70997970c51812dc3a010c7d01b50e0d17dc79c8"; + + await anvilTestClient.setBalance({ + address: account.address, + value: toWei("10"), + }); + + const startingBalance = await getWalletBalance({ + address: account.address, + chain: ANVIL_CHAIN, + client: TEST_CLIENT, + }); + + const startingBalanceRecipient = await getWalletBalance({ + address: recipient, + chain: ANVIL_CHAIN, + client: TEST_CLIENT, + }); + + const tx = prepareTransaction({ + client: TEST_CLIENT, + chain: ANVIL_CHAIN, + to: recipient, + value: toUnits("1", 18), + }); + + const result = await sendAndConfirmTransaction({ + account, + transaction: tx, + }); + + expect(result.transactionHash).toMatch(/^0x[a-fA-F0-9]{64}$/); + + const endingBalance = await getWalletBalance({ + address: account.address, + client: TEST_CLIENT, + chain: ANVIL_CHAIN, + }); + const endingBalanceRecipient = await getWalletBalance({ + address: recipient, + client: TEST_CLIENT, + chain: ANVIL_CHAIN, + }); + + expect(endingBalance.value).toBeLessThan(startingBalance.value); + expect(endingBalanceRecipient.value).toBeGreaterThan( + startingBalanceRecipient.value, + ); +}); diff --git a/src/tests/wallets/gcp-kms.test.ts b/src/tests/wallets/gcp-kms.test.ts new file mode 100644 index 000000000..afc4feffb --- /dev/null +++ b/src/tests/wallets/gcp-kms.test.ts @@ -0,0 +1,138 @@ +import { beforeAll, expect, test, vi } from "vitest"; + +import { ANVIL_CHAIN, anvilTestClient } from "../shared/chain.ts"; + +import { typedData } from "../shared/typed-data.ts"; + +import { verifyTypedData } from "thirdweb"; +import { verifyEOASignature } from "thirdweb/auth"; +import { + prepareTransaction, + sendAndConfirmTransaction, +} from "thirdweb/transaction"; +import { toUnits, toWei } from "thirdweb/utils"; +import { getWalletBalance } from "thirdweb/wallets"; +import { getGcpKmsAccount } from "../../server/utils/wallets/getGcpKmsAccount.ts"; +import { TEST_GCP_KMS_CONFIG } from "../config/gcp-kms.ts"; +import { TEST_CLIENT } from "../shared/client.ts"; + +let account: Awaited>; + +vi.mock("../../utils/chain", () => ({ + getChain: async () => ANVIL_CHAIN, +})); + +beforeAll(async () => { + account = await getGcpKmsAccount({ + client: TEST_CLIENT, + name: TEST_GCP_KMS_CONFIG.resourcePath, + clientOptions: { + credentials: { + client_email: TEST_GCP_KMS_CONFIG.email, + private_key: TEST_GCP_KMS_CONFIG.pk, + }, + }, + }); +}); + +test("account address is valid", () => { + expect(account.address).toMatch(/^0x[a-fA-F0-9]{40}$/); +}); + +test("sign message", async () => { + const message = "hello world"; + const signature = await account.signMessage({ message }); + + expect(signature).toMatch(/^0x[a-fA-F0-9]{130}$/); + + const isValid = await verifyEOASignature({ + address: account.address, + message, + signature, + }); + expect(isValid).toBe(true); +}); + +test("sign transaction", async () => { + const tx = { + chainId: ANVIL_CHAIN.id, + maxFeePerGas: toUnits("20", 9), + gas: 21000n, + to: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + value: toUnits("1", 18), + }; + + expect(account.signTransaction).toBeDefined(); + + const signedTx = await account.signTransaction?.(tx); + expect(signedTx).toMatch(/^0x[a-fA-F0-9]+$/); +}); + +test("sign typed data", async () => { + const signature = await account.signTypedData({ + ...typedData.basic, + primaryType: "Mail", + }); + expect(signature).toMatch(/^0x[a-fA-F0-9]{130}$/); + + const isValid = await verifyTypedData({ + address: account.address, + ...typedData.basic, + primaryType: "Mail", + signature, + client: TEST_CLIENT, + chain: ANVIL_CHAIN, + }); + expect(isValid).toBe(true); +}); + +test("send transaction", async () => { + const recipient = "0x70997970c51812dc3a010c7d01b50e0d17dc79c8"; + + await anvilTestClient.setBalance({ + address: account.address, + value: toWei("10"), + }); + + const startingBalance = await getWalletBalance({ + address: account.address, + chain: ANVIL_CHAIN, + client: TEST_CLIENT, + }); + + const startingBalanceRecipient = await getWalletBalance({ + address: recipient, + chain: ANVIL_CHAIN, + client: TEST_CLIENT, + }); + + const tx = prepareTransaction({ + client: TEST_CLIENT, + chain: ANVIL_CHAIN, + to: recipient, + value: toUnits("1", 18), + }); + + const result = await sendAndConfirmTransaction({ + account, + transaction: tx, + }); + + expect(result.transactionHash).toMatch(/^0x[a-fA-F0-9]{64}$/); + + const endingBalance = await getWalletBalance({ + address: account.address, + client: TEST_CLIENT, + chain: ANVIL_CHAIN, + }); + const endingBalanceRecipient = await getWalletBalance({ + address: recipient, + client: TEST_CLIENT, + chain: ANVIL_CHAIN, + }); + + expect(endingBalance.value).toBeLessThan(startingBalance.value); + expect(endingBalanceRecipient.value).toBeGreaterThan( + startingBalanceRecipient.value, + ); +}); diff --git a/src/utils/account.ts b/src/utils/account.ts index 3e0747e71..8880a553d 100644 --- a/src/utils/account.ts +++ b/src/utils/account.ts @@ -1,21 +1,19 @@ -import { EVMWallet } from "@thirdweb-dev/wallets"; -import { Signer, providers } from "ethers"; - import { StatusCodes } from "http-status-codes"; -import { Address } from "thirdweb"; -import { ethers5Adapter } from "thirdweb/adapters/ethers5"; -import { Account } from "thirdweb/wallets"; +import type { Address } from "thirdweb"; +import type { Account } from "thirdweb/wallets"; import { getWalletDetails } from "../db/wallets/getWalletDetails"; import { WalletType } from "../schema/wallet"; import { createCustomError } from "../server/middleware/error"; -import { getAwsKmsWallet } from "../server/utils/wallets/getAwsKmsWallet"; -import { getGcpKmsWallet } from "../server/utils/wallets/getGcpKmsWallet"; -import { - getLocalWallet, - getLocalWalletAccount, -} from "../server/utils/wallets/getLocalWallet"; -import { getSmartWallet } from "../server/utils/wallets/getSmartWallet"; +import { splitAwsKmsArn } from "../server/utils/wallets/awsKmsArn"; +import { getAwsKmsAccount } from "../server/utils/wallets/getAwsKmsAccount"; +import { getGcpKmsAccount } from "../server/utils/wallets/getGcpKmsAccount"; +import { getLocalWalletAccount } from "../server/utils/wallets/getLocalWallet"; +import { getConfig } from "./cache/getConfig"; +import { getSmartWalletV5 } from "./cache/getSmartWalletV5"; import { getChain } from "./chain"; +import { decrypt } from "./crypto"; +import { env } from "./env"; +import { thirdwebClient } from "./sdk"; export const _accountsCache = new Map(); @@ -36,6 +34,7 @@ export const getAccount = async (args: { const walletDetails = await getWalletDetails({ address: from, }); + if (!walletDetails) { throw createCustomError( `No configured wallet found with address ${from}`, @@ -43,64 +42,114 @@ export const getAccount = async (args: { "BAD_REQUEST", ); } + const config = await getConfig(); - let wallet: EVMWallet; switch (walletDetails.type) { - case WalletType.awsKms: - wallet = await getAwsKmsWallet({ - awsKmsKeyId: walletDetails.awsKmsKeyId!, + case WalletType.awsKms: { + const arn = walletDetails.awsKmsArn; + + if (!arn) { + throw new Error("AWS KMS ARN is missing for the wallet"); + } + + const { keyId, region } = splitAwsKmsArn(arn); + + // try to decrypt the secret access key in walletDetails (if found) + // otherwise fallback to global config + const secretAccessKey = walletDetails.awsKmsSecretAccessKey + ? decrypt(walletDetails.awsKmsSecretAccessKey, env.ENCRYPTION_PASSWORD) + : config.walletConfiguration.aws?.awsSecretAccessKey; + + if (!secretAccessKey) { + throw new Error("AWS KMS secret access key is missing for the wallet"); + } + + // try to get the access key id from walletDetails (if found) + // otherwise fallback to global config + const accessKeyId = + walletDetails.awsKmsAccessKeyId ?? + config.walletConfiguration.aws?.awsAccessKeyId; + + if (!accessKeyId) { + throw new Error("AWS KMS access key id is missing for the wallet"); + } + + return await getAwsKmsAccount({ + client: thirdwebClient, + keyId, + config: { + region, + credentials: { + accessKeyId, + secretAccessKey, + }, + }, }); - break; - case WalletType.gcpKms: - wallet = await getGcpKmsWallet({ - gcpKmsKeyId: walletDetails.gcpKmsKeyId!, - gcpKmsKeyVersionId: walletDetails.gcpKmsKeyVersionId!, + } + case WalletType.gcpKms: { + const resourcePath = walletDetails.gcpKmsResourcePath; + if (!resourcePath) { + throw new Error("GCP KMS resource path is missing for the wallet"); + } + + // try to decrypt the application credential private key in walletDetails (if found) + // otherwise fallback to global config + const gcpApplicationCredentialPrivateKey = + walletDetails.gcpApplicationCredentialPrivateKey + ? decrypt( + walletDetails.gcpApplicationCredentialPrivateKey, + env.ENCRYPTION_PASSWORD, + ) + : config.walletConfiguration.gcp?.gcpApplicationCredentialPrivateKey; + + if (!gcpApplicationCredentialPrivateKey) { + throw new Error( + "GCP application credential private key is missing for the wallet", + ); + } + + // try to get the application credential email from walletDetails (if found) + // otherwise fallback to global config + const gcpApplicationCredentialEmail = + walletDetails.gcpApplicationCredentialEmail ?? + config.walletConfiguration.gcp?.gcpApplicationCredentialEmail; + + if (!gcpApplicationCredentialEmail) { + throw new Error( + "GCP application credential email is missing for the wallet", + ); + } + + return await getGcpKmsAccount({ + client: thirdwebClient, + name: resourcePath, + clientOptions: { + credentials: { + client_email: gcpApplicationCredentialEmail, + private_key: gcpApplicationCredentialPrivateKey, + }, + }, }); - break; - case WalletType.local: - // For non-AA - // @TODO: Update all wallets to use v5 sdk and avoid ethers. - if (!accountAddress) { - const account = await getLocalWalletAccount(from); + } + case WalletType.local: { + if (accountAddress) { + const chain = await getChain(chainId); + const account = await getSmartWalletV5({ + chain, + accountAddress, + from, + }); _accountsCache.set(cacheKey, account); return account; } - wallet = await getLocalWallet({ chainId, walletAddress: from }); - break; + + const account = await getLocalWalletAccount(from); + _accountsCache.set(cacheKey, account); + return account; + } default: throw new Error(`Wallet type not supported: ${walletDetails.type}`); } - - // Get smart wallet if `accountAddress` is provided. - let signer: Signer; - - if (accountAddress) { - const smartWallet = await getSmartWallet({ - chainId, - backendWallet: wallet, - accountAddress, - }); - signer = await smartWallet.getSigner(); - } else { - signer = await wallet.getSigner(); - } - - if (walletDetails.type !== WalletType.local) { - // Get chain rpc provider. - const chain = await getChain(chainId); - const provider = new providers.JsonRpcProvider(chain.rpc); - - signer = signer.connect(provider); - } - - // @TODO: Move all wallets to use v5 SDK and avoid ethers adapter. - const account = await ethers5Adapter.signer.fromEthers({ - signer, - }); - - // Set cache. - _accountsCache.set(cacheKey, account); - return account; }; const getAccountCacheKey = (args: { diff --git a/src/utils/cache/getSmartWalletV5.ts b/src/utils/cache/getSmartWalletV5.ts index 68ba675b0..5361c0d46 100644 --- a/src/utils/cache/getSmartWalletV5.ts +++ b/src/utils/cache/getSmartWalletV5.ts @@ -1,13 +1,13 @@ -import { Account, smartWallet } from "thirdweb/wallets"; +import { smartWallet, type Account } from "thirdweb/wallets"; import { getAccount } from "../account"; import { thirdwebClient } from "../sdk"; -import { Address, getContract, readContract } from "thirdweb"; +import { getContract, readContract, type Address, type Chain } from "thirdweb"; export const smartWalletsCache = new Map(); interface SmartWalletParams { - chain: any; + chain: Chain; accountAddress: Address; from: Address; accountFactoryAddress?: Address; @@ -20,8 +20,10 @@ export const getSmartWalletV5 = async ({ accountFactoryAddress, }: SmartWalletParams) => { const cacheKey = `${chain.id}-${accountAddress}-${from}`; - if (smartWalletsCache.has(cacheKey)) { - return smartWalletsCache.get(cacheKey)!; + const cachedWallet = smartWalletsCache.get(cacheKey); + + if (cachedWallet) { + return cachedWallet; } // Resolve Smart-Account Contract diff --git a/src/utils/cache/getWallet.ts b/src/utils/cache/getWallet.ts index 778a8aa24..c3e7d9d63 100644 --- a/src/utils/cache/getWallet.ts +++ b/src/utils/cache/getWallet.ts @@ -1,11 +1,14 @@ -import { EVMWallet } from "@thirdweb-dev/wallets"; +import type { EVMWallet } from "@thirdweb-dev/wallets"; +import { AwsKmsWallet } from "@thirdweb-dev/wallets/evm/wallets/aws-kms"; +import { GcpKmsWallet } from "@thirdweb-dev/wallets/evm/wallets/gcp-kms"; import { getWalletDetails } from "../../db/wallets/getWalletDetails"; -import { PrismaTransaction } from "../../schema/prisma"; +import type { PrismaTransaction } from "../../schema/prisma"; import { WalletType } from "../../schema/wallet"; -import { getAwsKmsWallet } from "../../server/utils/wallets/getAwsKmsWallet"; -import { getGcpKmsWallet } from "../../server/utils/wallets/getGcpKmsWallet"; +import { splitAwsKmsArn } from "../../server/utils/wallets/awsKmsArn"; +import { splitGcpKmsResourcePath } from "../../server/utils/wallets/gcpKmsResourcePath"; import { getLocalWallet } from "../../server/utils/wallets/getLocalWallet"; import { getSmartWallet } from "../../server/utils/wallets/getSmartWallet"; +import { getConfig } from "./getConfig"; export const walletsCache = new Map(); @@ -25,8 +28,10 @@ export const getWallet = async ({ const cacheKey = accountAddress ? `${chainId}-${walletAddress}-${accountAddress}` : `${chainId}-${walletAddress}`; - if (walletsCache.has(cacheKey)) { - return walletsCache.get(cacheKey)! as TWallet; + + const cachedWallet = walletsCache.get(cacheKey); + if (cachedWallet) { + return cachedWallet as TWallet; } const walletDetails = await getWalletDetails({ @@ -38,22 +43,79 @@ export const getWallet = async ({ throw new Error(`No configured wallet found with address ${walletAddress}`); } + const config = await getConfig(); + let wallet: EVMWallet; switch (walletDetails.type) { - case WalletType.awsKms: - wallet = await getAwsKmsWallet({ - awsKmsKeyId: walletDetails.awsKmsKeyId!, + case WalletType.awsKms: { + if (!walletDetails.awsKmsArn) { + throw new Error("AWS KMS ARN is missing for the wallet"); + } + + const splitArn = splitAwsKmsArn(walletDetails.awsKmsArn); + + const accessKeyId = + walletDetails.awsKmsAccessKeyId ?? + config.walletConfiguration.aws?.awsAccessKeyId; + + const secretAccessKey = + walletDetails.awsKmsSecretAccessKey ?? + config.walletConfiguration.aws?.awsSecretAccessKey; + + if (!(accessKeyId && secretAccessKey)) { + throw new Error( + "AWS KMS access key id and secret access key are missing for the wallet", + ); + } + + wallet = new AwsKmsWallet({ + keyId: splitArn.keyId, + region: splitArn.region, + accessKeyId, + secretAccessKey, }); + break; - case WalletType.gcpKms: - wallet = await getGcpKmsWallet({ - gcpKmsKeyId: walletDetails.gcpKmsKeyId!, - gcpKmsKeyVersionId: walletDetails.gcpKmsKeyVersionId!, + } + + case WalletType.gcpKms: { + if (!walletDetails.gcpKmsResourcePath) { + throw new Error("GCP KMS resource path is missing for the wallet"); + } + const splitResourcePath = splitGcpKmsResourcePath( + walletDetails.gcpKmsResourcePath, + ); + + const email = + walletDetails.gcpApplicationCredentialEmail ?? + config.walletConfiguration.gcp?.gcpApplicationCredentialEmail; + const privateKey = + walletDetails.gcpApplicationCredentialPrivateKey ?? + config.walletConfiguration.gcp?.gcpApplicationCredentialPrivateKey; + + if (!(email && privateKey)) { + throw new Error( + "GCP KMS email and private key are missing for the wallet", + ); + } + + wallet = new GcpKmsWallet({ + keyId: splitResourcePath.cryptoKeyId, + keyRingId: splitResourcePath.keyRingId, + keyVersion: splitResourcePath.versionId, + locationId: splitResourcePath.locationId, + projectId: splitResourcePath.projectId, + + applicationCredentialEmail: email, + applicationCredentialPrivateKey: privateKey, }); break; + } + case WalletType.local: wallet = await getLocalWallet({ chainId, walletAddress }); break; + default: throw new Error( `Wallet with address ${walletAddress} was configured with unknown wallet type ${walletDetails.type}`, diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 000000000..cfcdaf467 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], + exclude: ["tests/e2e/**/*"], + // setupFiles: ["./vitest.setup.ts"], + globalSetup: ["./vitest.global-setup.ts"], + mockReset: true, + }, +}); diff --git a/vitest.global-setup.ts b/vitest.global-setup.ts new file mode 100644 index 000000000..53f215c91 --- /dev/null +++ b/vitest.global-setup.ts @@ -0,0 +1,21 @@ +import { config } from "dotenv"; +import path from "node:path"; + +config({ + path: [path.resolve(".env.test.local"), path.resolve(".env.test")], +}); + +import { createServer } from "prool"; +import { anvil } from "prool/instances"; + +export async function setup() { + const server = createServer({ + instance: anvil(), + port: 8645, // Choose an appropriate port + }); + await server.start(); + // Return a teardown function that will be called after all tests are complete + return async () => { + await server.stop(); + }; +} diff --git a/yarn.lock b/yarn.lock index a102c4ee0..7c39a5f51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -51,6 +51,19 @@ "@aws-sdk/util-utf8-browser" "^3.0.0" tslib "^1.11.1" +"@aws-crypto/sha256-browser@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz#153895ef1dba6f9fce38af550e0ef58988eb649e" + integrity sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw== + dependencies: + "@aws-crypto/sha256-js" "^5.2.0" + "@aws-crypto/supports-web-crypto" "^5.2.0" + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + "@aws-crypto/sha256-js@3.0.0", "@aws-crypto/sha256-js@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz#f06b84d550d25521e60d2a0e2a90139341e007c2" @@ -60,6 +73,15 @@ "@aws-sdk/types" "^3.222.0" tslib "^1.11.1" +"@aws-crypto/sha256-js@5.2.0", "@aws-crypto/sha256-js@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz#c4fdb773fdbed9a664fc1a95724e206cf3860042" + integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + "@aws-crypto/supports-web-crypto@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz#5d1bf825afa8072af2717c3e455f35cda0103ec2" @@ -67,6 +89,13 @@ dependencies: tslib "^1.11.1" +"@aws-crypto/supports-web-crypto@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz#a1e399af29269be08e695109aa15da0a07b5b5fb" + integrity sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg== + dependencies: + tslib "^2.6.2" + "@aws-crypto/util@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-3.0.0.tgz#1c7ca90c29293f0883468ad48117937f0fe5bfb0" @@ -76,6 +105,62 @@ "@aws-sdk/util-utf8-browser" "^3.0.0" tslib "^1.11.1" +"@aws-crypto/util@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da" + integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== + dependencies: + "@aws-sdk/types" "^3.222.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-kms@^3.28.0": + version "3.637.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-kms/-/client-kms-3.637.0.tgz#4e3836971b14b348e845a1f63c8548aa5633fae4" + integrity sha512-bqppLpmIPl6eZkZx/9axnr4CBbhtrRKe3LffW8320DlwCqP3zU+c500vXMjEgYdrAqkqOFyDY/FYMAgZhtHVCQ== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/client-sso-oidc" "3.637.0" + "@aws-sdk/client-sts" "3.637.0" + "@aws-sdk/core" "3.635.0" + "@aws-sdk/credential-provider-node" "3.637.0" + "@aws-sdk/middleware-host-header" "3.620.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.620.0" + "@aws-sdk/middleware-user-agent" "3.637.0" + "@aws-sdk/region-config-resolver" "3.614.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.637.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.614.0" + "@smithy/config-resolver" "^3.0.5" + "@smithy/core" "^2.4.0" + "@smithy/fetch-http-handler" "^3.2.4" + "@smithy/hash-node" "^3.0.3" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.5" + "@smithy/middleware-endpoint" "^3.1.0" + "@smithy/middleware-retry" "^3.0.15" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/node-http-handler" "^3.1.4" + "@smithy/protocol-http" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.15" + "@smithy/util-defaults-mode-node" "^3.0.15" + "@smithy/util-endpoints" "^2.0.5" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + "@aws-sdk/client-kms@^3.398.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-kms/-/client-kms-3.587.0.tgz#b1a51dce4d73c7f1b3358457967b50c4ade9b418" @@ -169,6 +254,51 @@ "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/client-sso-oidc@3.637.0": + version "3.637.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.637.0.tgz#d7e22ce6627c3285bf311e6c9e64c22b99bbd76a" + integrity sha512-27bHALN6Qb6m6KZmPvRieJ/QRlj1lyac/GT2Rn5kJpre8Mpp+yxrtvp3h9PjNBty4lCeFEENfY4dGNSozBuBcw== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.635.0" + "@aws-sdk/credential-provider-node" "3.637.0" + "@aws-sdk/middleware-host-header" "3.620.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.620.0" + "@aws-sdk/middleware-user-agent" "3.637.0" + "@aws-sdk/region-config-resolver" "3.614.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.637.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.614.0" + "@smithy/config-resolver" "^3.0.5" + "@smithy/core" "^2.4.0" + "@smithy/fetch-http-handler" "^3.2.4" + "@smithy/hash-node" "^3.0.3" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.5" + "@smithy/middleware-endpoint" "^3.1.0" + "@smithy/middleware-retry" "^3.0.15" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/node-http-handler" "^3.1.4" + "@smithy/protocol-http" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.15" + "@smithy/util-defaults-mode-node" "^3.0.15" + "@smithy/util-endpoints" "^2.0.5" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + "@aws-sdk/client-sso@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.587.0.tgz#b4de7772496add0c4196de8b15b3ee622ce0adb5" @@ -213,6 +343,50 @@ "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/client-sso@3.637.0": + version "3.637.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.637.0.tgz#ae152759a5e1e576e1df6b8f4edaf59796e1758e" + integrity sha512-+KjLvgX5yJYROWo3TQuwBJlHCY0zz9PsLuEolmXQn0BVK1L/m9GteZHtd+rEdAoDGBpE0Xqjy1oz5+SmtsaRUw== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.635.0" + "@aws-sdk/middleware-host-header" "3.620.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.620.0" + "@aws-sdk/middleware-user-agent" "3.637.0" + "@aws-sdk/region-config-resolver" "3.614.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.637.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.614.0" + "@smithy/config-resolver" "^3.0.5" + "@smithy/core" "^2.4.0" + "@smithy/fetch-http-handler" "^3.2.4" + "@smithy/hash-node" "^3.0.3" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.5" + "@smithy/middleware-endpoint" "^3.1.0" + "@smithy/middleware-retry" "^3.0.15" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/node-http-handler" "^3.1.4" + "@smithy/protocol-http" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.15" + "@smithy/util-defaults-mode-node" "^3.0.15" + "@smithy/util-endpoints" "^2.0.5" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + "@aws-sdk/client-sts@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.587.0.tgz#4b5908647c9d919185f181d631864262666c6501" @@ -259,6 +433,52 @@ "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/client-sts@3.637.0": + version "3.637.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.637.0.tgz#6dcde6640d8a5e60dd4a2df8557284a0226d182c" + integrity sha512-xUi7x4qDubtA8QREtlblPuAcn91GS/09YVEY/RwU7xCY0aqGuFwgszAANlha4OUIqva8oVj2WO4gJuG+iaSnhw== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/client-sso-oidc" "3.637.0" + "@aws-sdk/core" "3.635.0" + "@aws-sdk/credential-provider-node" "3.637.0" + "@aws-sdk/middleware-host-header" "3.620.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.620.0" + "@aws-sdk/middleware-user-agent" "3.637.0" + "@aws-sdk/region-config-resolver" "3.614.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.637.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.614.0" + "@smithy/config-resolver" "^3.0.5" + "@smithy/core" "^2.4.0" + "@smithy/fetch-http-handler" "^3.2.4" + "@smithy/hash-node" "^3.0.3" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.5" + "@smithy/middleware-endpoint" "^3.1.0" + "@smithy/middleware-retry" "^3.0.15" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/node-http-handler" "^3.1.4" + "@smithy/protocol-http" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.15" + "@smithy/util-defaults-mode-node" "^3.0.15" + "@smithy/util-endpoints" "^2.0.5" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + "@aws-sdk/core@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.587.0.tgz#5e854b4330f92102120d1400ed19769ae8cee8c6" @@ -272,6 +492,22 @@ fast-xml-parser "4.2.5" tslib "^2.6.2" +"@aws-sdk/core@3.635.0": + version "3.635.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.635.0.tgz#74b7d0d7fa3aa39f87ea5cf4e6c97d4d84f4ef14" + integrity sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg== + dependencies: + "@smithy/core" "^2.4.0" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/property-provider" "^3.1.3" + "@smithy/protocol-http" "^4.1.0" + "@smithy/signature-v4" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/util-middleware" "^3.0.3" + fast-xml-parser "4.4.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-env@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.587.0.tgz#40435be331773e4b1b665a1f4963518d4647505c" @@ -282,6 +518,16 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-env@3.620.1": + version "3.620.1" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz#d4692c49a65ebc11dae3f7f8b053fee9268a953c" + integrity sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@aws-sdk/credential-provider-http@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.587.0.tgz#dc23c6d6708bc67baea54bfab0f256c5fe4df023" @@ -297,6 +543,21 @@ "@smithy/util-stream" "^3.0.1" tslib "^2.6.2" +"@aws-sdk/credential-provider-http@3.635.0": + version "3.635.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz#083439af1336693049958e4b61695e4712b30fd4" + integrity sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/fetch-http-handler" "^3.2.4" + "@smithy/node-http-handler" "^3.1.4" + "@smithy/property-provider" "^3.1.3" + "@smithy/protocol-http" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/util-stream" "^3.1.3" + tslib "^2.6.2" + "@aws-sdk/credential-provider-ini@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.587.0.tgz#2c4d2deb7a2085217149ddb61bdebbe7b7b3c952" @@ -314,6 +575,23 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-ini@3.637.0": + version "3.637.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.637.0.tgz#dae0d8b05c8b9480da5a92beb4dd244985ecbd70" + integrity sha512-h+PFCWfZ0Q3Dx84SppET/TFpcQHmxFW8/oV9ArEvMilw4EBN+IlxgbL0CnHwjHW64szcmrM0mbebjEfHf4FXmw== + dependencies: + "@aws-sdk/credential-provider-env" "3.620.1" + "@aws-sdk/credential-provider-http" "3.635.0" + "@aws-sdk/credential-provider-process" "3.620.1" + "@aws-sdk/credential-provider-sso" "3.637.0" + "@aws-sdk/credential-provider-web-identity" "3.621.0" + "@aws-sdk/types" "3.609.0" + "@smithy/credential-provider-imds" "^3.2.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@aws-sdk/credential-provider-node@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.587.0.tgz#f76685685dae4f6e3a4e0764ac05edd5e3406b1e" @@ -332,6 +610,24 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-node@3.637.0": + version "3.637.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.637.0.tgz#0ac6678ab31783adf5b1cf03add5d1da101ea946" + integrity sha512-yoEhoxJJfs7sPVQ6Is939BDQJZpZCoUgKr/ySse4YKOZ24t4VqgHA6+wV7rYh+7IW24Rd91UTvEzSuHYTlxlNA== + dependencies: + "@aws-sdk/credential-provider-env" "3.620.1" + "@aws-sdk/credential-provider-http" "3.635.0" + "@aws-sdk/credential-provider-ini" "3.637.0" + "@aws-sdk/credential-provider-process" "3.620.1" + "@aws-sdk/credential-provider-sso" "3.637.0" + "@aws-sdk/credential-provider-web-identity" "3.621.0" + "@aws-sdk/types" "3.609.0" + "@smithy/credential-provider-imds" "^3.2.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@aws-sdk/credential-provider-process@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.587.0.tgz#1e5cc562a68438a77f464adc0493b02e04dd3ea1" @@ -343,6 +639,17 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-process@3.620.1": + version "3.620.1" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz#10387cf85400420bb4bbda9cc56937dcc6d6d0ee" + integrity sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@aws-sdk/credential-provider-sso@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.587.0.tgz#9d113caba7fe69e566313ef2a511946a6b170d6f" @@ -356,6 +663,19 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-sso@3.637.0": + version "3.637.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.637.0.tgz#13acf77579df026e89ced33501489defd06a0518" + integrity sha512-Mvz+h+e62/tl+dVikLafhv+qkZJ9RUb8l2YN/LeKMWkxQylPT83CPk9aimVhCV89zth1zpREArl97+3xsfgQvA== + dependencies: + "@aws-sdk/client-sso" "3.637.0" + "@aws-sdk/token-providers" "3.614.0" + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@aws-sdk/credential-provider-web-identity@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.587.0.tgz#daa41e3cc9309594327056e431b8065145c5297a" @@ -366,6 +686,16 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-web-identity@3.621.0": + version "3.621.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz#b25878c0a05dad60cd5f91e7e5a31a145c2f14be" + integrity sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@aws-sdk/middleware-host-header@3.577.0": version "3.577.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.577.0.tgz#a3fc626d409ec850296740478c64ef5806d8b878" @@ -376,6 +706,16 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/middleware-host-header@3.620.0": + version "3.620.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz#b561d419a08a984ba364c193376b482ff5224d74" + integrity sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@aws-sdk/middleware-logger@3.577.0": version "3.577.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.577.0.tgz#6da3b13ae284fb3930961f0fc8e20b1f6cf8be30" @@ -385,6 +725,15 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/middleware-logger@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz#ed44d201f091b8bac908cbf14724c7a4d492553f" + integrity sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@aws-sdk/middleware-recursion-detection@3.577.0": version "3.577.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.577.0.tgz#fff76abc6d4521636f9e654ce5bf2c4c79249417" @@ -395,6 +744,16 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/middleware-recursion-detection@3.620.0": + version "3.620.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz#f8270dfff843fd756be971e5673f89c6a24c6513" + integrity sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@aws-sdk/middleware-user-agent@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.587.0.tgz#2a68900cfb29afbae2952d901de4fcb91850bd3d" @@ -406,6 +765,17 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/middleware-user-agent@3.637.0": + version "3.637.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.637.0.tgz#2b00de72b00953a477bcc02a68d8cbb5e9670c44" + integrity sha512-EYo0NE9/da/OY8STDsK2LvM4kNa79DBsf4YVtaG4P5pZ615IeFsD8xOHZeuJmUrSMlVQ8ywPRX7WMucUybsKug== + dependencies: + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.637.0" + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@aws-sdk/region-config-resolver@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.587.0.tgz#ad1c15494f44dfc4c7a7bce389f8b128dace923f" @@ -418,6 +788,18 @@ "@smithy/util-middleware" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/region-config-resolver@3.614.0": + version "3.614.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz#9cebb31a5bcfea2a41891fff7f28d0164cde179a" + integrity sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/types" "^3.3.0" + "@smithy/util-config-provider" "^3.0.0" + "@smithy/util-middleware" "^3.0.3" + tslib "^2.6.2" + "@aws-sdk/token-providers@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.587.0.tgz#f9fd2ddfc554c1370f8d0f467c76a4c8cb904ae6" @@ -429,6 +811,17 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/token-providers@3.614.0": + version "3.614.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz#88da04f6d4ce916b0b0f6e045676d04201fb47fd" + integrity sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@aws-sdk/types@3.577.0", "@aws-sdk/types@^3.222.0": version "3.577.0" resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.577.0.tgz#7700784d368ce386745f8c340d9d68cea4716f90" @@ -437,6 +830,14 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/types@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.609.0.tgz#06b39d799c9f197a7b43670243e8e78a3bf7d6a5" + integrity sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@aws-sdk/util-endpoints@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.587.0.tgz#781e0822a95dba15f7ac8f22a6f6d7f0c8819818" @@ -447,6 +848,16 @@ "@smithy/util-endpoints" "^2.0.1" tslib "^2.6.2" +"@aws-sdk/util-endpoints@3.637.0": + version "3.637.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.637.0.tgz#e20bcb69028039fdbc06e98a3028c7f8d8e8adaa" + integrity sha512-pAqOKUHeVWHEXXDIp/qoMk/6jyxIb6GGjnK1/f8dKHtKIEs4tKsnnL563gceEvdad53OPXIt86uoevCcCzmBnw== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/types" "^3.3.0" + "@smithy/util-endpoints" "^2.0.5" + tslib "^2.6.2" + "@aws-sdk/util-locate-window@^3.0.0": version "3.568.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz#2acc4b2236af0d7494f7e517401ba6b3c4af11ff" @@ -464,6 +875,16 @@ bowser "^2.11.0" tslib "^2.6.2" +"@aws-sdk/util-user-agent-browser@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz#aa15421b2e32ae8bc589dac2bd6e8969832ce588" + integrity sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/types" "^3.3.0" + bowser "^2.11.0" + tslib "^2.6.2" + "@aws-sdk/util-user-agent-node@3.587.0": version "3.587.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.587.0.tgz#a6bf422f307a68e16a6c19ee5d731fcc32696fb9" @@ -474,6 +895,16 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/util-user-agent-node@3.614.0": + version "3.614.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz#1e3f49a80f841a3f21647baed2adce01aac5beb5" + integrity sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@aws-sdk/util-utf8-browser@^3.0.0": version "3.259.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz#3275a6f5eb334f96ca76635b961d3c50259fd9ff" @@ -691,6 +1122,32 @@ dependencies: "@bull-board/api" "5.21.1" +"@cloud-cryptographic-wallet/asn1-parser@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@cloud-cryptographic-wallet/asn1-parser/-/asn1-parser-0.0.4.tgz#4494a8f46d2b3974731d6cc2f3f34cb8afeb0c78" + integrity sha512-2Mwc2TSJsBmZluvw4mPqtt5UdymDvGRA3DqNAMU6xifh8Ytms0UtAm0YHNpuD2O2LnQr19q2d7eDg0eaS5qIPg== + dependencies: + asn1js "^3.0.5" + +"@cloud-cryptographic-wallet/cloud-kms-signer@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@cloud-cryptographic-wallet/cloud-kms-signer/-/cloud-kms-signer-0.1.2.tgz#9419d45a4a3f9693c8690bb5b6a49cf4e923559b" + integrity sha512-A+d2nb9RfL2Tfkwoodga+arX5Yg2SX8UNE1dFK6QkQKDgr/fMHGuc994y2QHG+u6VAJsF+977T44FAR96Tx4CQ== + dependencies: + "@cloud-cryptographic-wallet/asn1-parser" "^0.0.4" + "@cloud-cryptographic-wallet/signer" "^0.0.5" + "@google-cloud/kms" "^3.0.1" + "@node-lightning/checksum" "^0.27.0" + +"@cloud-cryptographic-wallet/signer@^0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@cloud-cryptographic-wallet/signer/-/signer-0.0.5.tgz#28dc9dd6d222c6e1f06a3e8c52342c2141085cbc" + integrity sha512-CfD3o1PWN3JF1F7bsfgcIET+RpNHeb953TrlMhQ+4yqFzIKGbqTLk3TQT+IoTKdxCWGqo/bzXpLO7aqqzIpPbg== + dependencies: + bn.js "^5.2.0" + keccak "^3.0.2" + secp256k1 "^4.0.3" + "@coinbase/wallet-sdk@4.0.3": version "4.0.3" resolved "https://registry.yarnpkg.com/@coinbase/wallet-sdk/-/wallet-sdk-4.0.3.tgz#fd52dd4c168c35979c7b3294018a6f78d163a593" @@ -2048,6 +2505,13 @@ dependencies: google-gax "^3.0.1" +"@google-cloud/kms@^3.0.1": + version "3.8.0" + resolved "https://registry.yarnpkg.com/@google-cloud/kms/-/kms-3.8.0.tgz#165774725152fd73a2940667dfc7179843fab014" + integrity sha512-hE9np8VuMLe7npw16wCchcnkW0TDraP3zjD4+vzrxwDUKSTId8sWjlU/Pb69viUBQLyoLFE3mkH6nPhd8TWU3A== + dependencies: + google-gax "^3.5.8" + "@google-cloud/kms@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@google-cloud/kms/-/kms-4.4.0.tgz#8f3586e0d24bfa44378b9b1f26a14283c7e2c468" @@ -2130,6 +2594,13 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" +"@isaacs/fs-minipass@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32" + integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== + dependencies: + minipass "^7.0.4" + "@istanbuljs/schema@^0.1.2": version "0.1.3" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" @@ -2505,6 +2976,11 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.5.0.tgz#abadc5ca20332db2b1b2aa3e496e9af1213570b0" integrity sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA== +"@node-lightning/checksum@^0.27.0": + version "0.27.4" + resolved "https://registry.yarnpkg.com/@node-lightning/checksum/-/checksum-0.27.4.tgz#493004e76aa76cdbab46f01bf445e4010c30a179" + integrity sha512-33DuXWqVVvHPnO7O38L2wtz9cSjCXeqi3+xUHZpPhZHpez4atw0JUcc1Fa1SJ4aEjDX81t6rLloMW083z9ZHYQ== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -3391,11 +3867,21 @@ "@noble/hashes" "~1.5.0" "@scure/base" "~1.1.8" +"@sec-ant/readable-stream@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz#60de891bb126abfdc5410fdc6166aca065f10a0c" + integrity sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg== + "@sinclair/typebox@^0.31.28": version "0.31.28" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.31.28.tgz#b68831e7bc7d09daac26968ea32f42bedc968ede" integrity sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ== +"@sindresorhus/merge-streams@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz#abb11d99aeb6d27f1b563c38147a72d50058e339" + integrity sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ== + "@smithy/abort-controller@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-3.0.0.tgz#5815f5d4618e14bf8d031bb98a99adabbb831168" @@ -3404,6 +3890,14 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/abort-controller@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-3.1.1.tgz#291210611ff6afecfc198d0ca72d5771d8461d16" + integrity sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/config-resolver@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-3.0.1.tgz#4e0917e5a02139ef978a1ed470543ab41dd3626b" @@ -3415,6 +3909,17 @@ "@smithy/util-middleware" "^3.0.0" tslib "^2.6.2" +"@smithy/config-resolver@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-3.0.5.tgz#727978bba7ace754c741c259486a19d3083431fd" + integrity sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA== + dependencies: + "@smithy/node-config-provider" "^3.1.4" + "@smithy/types" "^3.3.0" + "@smithy/util-config-provider" "^3.0.0" + "@smithy/util-middleware" "^3.0.3" + tslib "^2.6.2" + "@smithy/core@^2.1.1": version "2.1.1" resolved "https://registry.yarnpkg.com/@smithy/core/-/core-2.1.1.tgz#c5e50785f9f61cad85fbefafa402b1a87eff095a" @@ -3429,6 +3934,22 @@ "@smithy/util-middleware" "^3.0.0" tslib "^2.6.2" +"@smithy/core@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-2.4.0.tgz#56e917b6ab2dffeba681a05395c40a757d681147" + integrity sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w== + dependencies: + "@smithy/middleware-endpoint" "^3.1.0" + "@smithy/middleware-retry" "^3.0.15" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/protocol-http" "^4.1.0" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + "@smithy/credential-provider-imds@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-3.1.0.tgz#7e58b78aa8de13dd04e94829241cd1cbde59b6d3" @@ -3440,6 +3961,17 @@ "@smithy/url-parser" "^3.0.0" tslib "^2.6.2" +"@smithy/credential-provider-imds@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz#0e0e7ddaff1a8633cb927aee1056c0ab506b7ecf" + integrity sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA== + dependencies: + "@smithy/node-config-provider" "^3.1.4" + "@smithy/property-provider" "^3.1.3" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + tslib "^2.6.2" + "@smithy/fetch-http-handler@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-3.0.1.tgz#dacfdf6e70d639fac4a0f57c42ce13f0ed14ff22" @@ -3451,6 +3983,17 @@ "@smithy/util-base64" "^3.0.0" tslib "^2.6.2" +"@smithy/fetch-http-handler@^3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz#c754de7e0ff2541b73ac9ba7cc955940114b3d62" + integrity sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg== + dependencies: + "@smithy/protocol-http" "^4.1.0" + "@smithy/querystring-builder" "^3.0.3" + "@smithy/types" "^3.3.0" + "@smithy/util-base64" "^3.0.0" + tslib "^2.6.2" + "@smithy/hash-node@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-3.0.0.tgz#f44b5fff193e241c1cdcc957b296b60f186f0e59" @@ -3461,6 +4004,16 @@ "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" +"@smithy/hash-node@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-3.0.3.tgz#82c5cb7b0f1a29ee7319081853d2d158c07dff24" + integrity sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw== + dependencies: + "@smithy/types" "^3.3.0" + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + "@smithy/invalid-dependency@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-3.0.0.tgz#21cb6b5203ee15321bfcc751f21f7a19536d4ae8" @@ -3469,6 +4022,21 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/invalid-dependency@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz#8d9fd70e3a94b565a4eba4ffbdc95238e1930528" + integrity sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/is-array-buffer@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" + integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== + dependencies: + tslib "^2.6.2" + "@smithy/is-array-buffer@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz#9a95c2d46b8768946a9eec7f935feaddcffa5e7a" @@ -3485,6 +4053,15 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/middleware-content-length@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz#1680aa4fb2a1c0505756103c9a5c2916307d9035" + integrity sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw== + dependencies: + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/middleware-endpoint@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-3.0.1.tgz#49e8defb8e892e70417bd05f1faaf207070f32c7" @@ -3498,6 +4075,34 @@ "@smithy/util-middleware" "^3.0.0" tslib "^2.6.2" +"@smithy/middleware-endpoint@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz#9b8a496d87a68ec43f3f1a0139868d6765a88119" + integrity sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw== + dependencies: + "@smithy/middleware-serde" "^3.0.3" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-middleware" "^3.0.3" + tslib "^2.6.2" + +"@smithy/middleware-retry@^3.0.15": + version "3.0.15" + resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-3.0.15.tgz#9b96900cde70d8aafd267e13f4e79241be90e0c7" + integrity sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ== + dependencies: + "@smithy/node-config-provider" "^3.1.4" + "@smithy/protocol-http" "^4.1.0" + "@smithy/service-error-classification" "^3.0.3" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + tslib "^2.6.2" + uuid "^9.0.1" + "@smithy/middleware-retry@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-3.0.3.tgz#8e9af1c9db4bc8904d73126225211b42b562f961" @@ -3521,6 +4126,14 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/middleware-serde@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz#74d974460f74d99f38c861e6862984543a880a66" + integrity sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/middleware-stack@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-3.0.0.tgz#00f112bae7af5fc3bd37d4fab95ebce0f17a7774" @@ -3529,6 +4142,14 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/middleware-stack@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz#91845c7e61e6f137fa912b623b6def719a4f6ce7" + integrity sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/node-config-provider@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-3.1.0.tgz#e962987c4e2e2b8b50397de5f4745eb21ee7bdbb" @@ -3539,6 +4160,16 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/node-config-provider@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz#05647bed666aa8036a1ad72323c1942e5d421be1" + integrity sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ== + dependencies: + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/node-http-handler@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-3.0.0.tgz#e771ea95d03e259f04b7b37e8aece8a4fffc8cdc" @@ -3550,6 +4181,17 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/node-http-handler@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz#be4195e45639e690d522cd5f11513ea822ff9d5f" + integrity sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg== + dependencies: + "@smithy/abort-controller" "^3.1.1" + "@smithy/protocol-http" "^4.1.0" + "@smithy/querystring-builder" "^3.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/property-provider@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-3.1.0.tgz#b78d4964a1016b90331cc0c770b472160361fde7" @@ -3558,6 +4200,14 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/property-provider@^3.1.3": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-3.1.3.tgz#afd57ea82a3f6c79fbda95e3cb85c0ee0a79f39a" + integrity sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/protocol-http@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-4.0.0.tgz#04df3b5674b540323f678e7c4113e8abd8b26432" @@ -3566,6 +4216,14 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/protocol-http@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-4.1.0.tgz#23519d8f45bf4f33960ea5415847bc2b620a010b" + integrity sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/querystring-builder@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-3.0.0.tgz#48a9aa7b700e8409368c21bc0adf7564e001daea" @@ -3575,6 +4233,15 @@ "@smithy/util-uri-escape" "^3.0.0" tslib "^2.6.2" +"@smithy/querystring-builder@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz#6b0e566f885bb84938d077c69e8f8555f686af13" + integrity sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw== + dependencies: + "@smithy/types" "^3.3.0" + "@smithy/util-uri-escape" "^3.0.0" + tslib "^2.6.2" + "@smithy/querystring-parser@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-3.0.0.tgz#fa1ed0cee408cd4d622070fa874bc50ac1a379b7" @@ -3583,6 +4250,14 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/querystring-parser@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz#272a6b83f88dfcbbec8283d72a6bde850cc00091" + integrity sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/service-error-classification@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-3.0.0.tgz#06a45cb91b15b8b0d5f3b1df2b3743d2ca42f5c4" @@ -3590,6 +4265,13 @@ dependencies: "@smithy/types" "^3.0.0" +"@smithy/service-error-classification@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz#73484255060a094aa9372f6cd972dcaf97e3ce80" + integrity sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ== + dependencies: + "@smithy/types" "^3.3.0" + "@smithy/shared-ini-file-loader@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.0.tgz#a4cb9304c3be1c232ec661132ca88d177ac7a5b1" @@ -3598,6 +4280,14 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/shared-ini-file-loader@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz#7dceaf5a5307a2ee347ace8aba17312a1a3ede15" + integrity sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/signature-v4@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-3.0.0.tgz#f536d0abebfeeca8e9aab846a4042658ca07d3b7" @@ -3611,6 +4301,20 @@ "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" +"@smithy/signature-v4@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-4.1.0.tgz#251ff43dc1f4ad66776122732fea9e56efc56443" + integrity sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag== + dependencies: + "@smithy/is-array-buffer" "^3.0.0" + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + "@smithy/util-hex-encoding" "^3.0.0" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-uri-escape" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + "@smithy/smithy-client@^3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-3.1.1.tgz#9aa770edd9b6277dc4124c924c617a436cdb670e" @@ -3623,6 +4327,18 @@ "@smithy/util-stream" "^3.0.1" tslib "^2.6.2" +"@smithy/smithy-client@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-3.2.0.tgz#6db94024e4bdaefa079ac68dbea23dafbea230c8" + integrity sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw== + dependencies: + "@smithy/middleware-endpoint" "^3.1.0" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/protocol-http" "^4.1.0" + "@smithy/types" "^3.3.0" + "@smithy/util-stream" "^3.1.3" + tslib "^2.6.2" + "@smithy/types@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/types/-/types-3.0.0.tgz#00231052945159c64ffd8b91e8909d8d3006cb7e" @@ -3630,6 +4346,13 @@ dependencies: tslib "^2.6.2" +"@smithy/types@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-3.3.0.tgz#fae037c733d09bc758946a01a3de0ef6e210b16b" + integrity sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA== + dependencies: + tslib "^2.6.2" + "@smithy/url-parser@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-3.0.0.tgz#5fdc77cd22051c1aac6531be0315bfcba0fa705d" @@ -3639,6 +4362,15 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/url-parser@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-3.0.3.tgz#e8a060d9810b24b1870385fc2b02485b8a6c5955" + integrity sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A== + dependencies: + "@smithy/querystring-parser" "^3.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/util-base64@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-3.0.0.tgz#f7a9a82adf34e27a72d0719395713edf0e493017" @@ -3662,6 +4394,14 @@ dependencies: tslib "^2.6.2" +"@smithy/util-buffer-from@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b" + integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA== + dependencies: + "@smithy/is-array-buffer" "^2.2.0" + tslib "^2.6.2" + "@smithy/util-buffer-from@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz#559fc1c86138a89b2edaefc1e6677780c24594e3" @@ -3677,6 +4417,17 @@ dependencies: tslib "^2.6.2" +"@smithy/util-defaults-mode-browser@^3.0.15": + version "3.0.15" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.15.tgz#df73b9ae3dddc9126e0bb93ebc720b09d7163858" + integrity sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg== + dependencies: + "@smithy/property-provider" "^3.1.3" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + bowser "^2.11.0" + tslib "^2.6.2" + "@smithy/util-defaults-mode-browser@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.3.tgz#6fff11a6c407ca1d5a1dc009768bd09271b199c2" @@ -3688,6 +4439,19 @@ bowser "^2.11.0" tslib "^2.6.2" +"@smithy/util-defaults-mode-node@^3.0.15": + version "3.0.15" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.15.tgz#d52476e1f2e66525d918b51f8d5a9b0972bf518e" + integrity sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A== + dependencies: + "@smithy/config-resolver" "^3.0.5" + "@smithy/credential-provider-imds" "^3.2.0" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/property-provider" "^3.1.3" + "@smithy/smithy-client" "^3.2.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/util-defaults-mode-node@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.3.tgz#0b52ba9cb1138ee9076feba9a733462b2e2e6093" @@ -3710,6 +4474,15 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/util-endpoints@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz#e3a7a4d1c41250bfd2b2d890d591273a7d8934be" + integrity sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg== + dependencies: + "@smithy/node-config-provider" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/util-hex-encoding@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz#32938b33d5bf2a15796cd3f178a55b4155c535e6" @@ -3725,6 +4498,14 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/util-middleware@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-3.0.3.tgz#07bf9602682f5a6c55bc2f0384303f85fc68c87e" + integrity sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/util-retry@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-3.0.0.tgz#8a0c47496aab74e1dfde4905d462ad636a8824bb" @@ -3734,6 +4515,15 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@smithy/util-retry@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-3.0.3.tgz#9b2ac0dbb1c81f69812a8affa4d772bebfc0e049" + integrity sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w== + dependencies: + "@smithy/service-error-classification" "^3.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@smithy/util-stream@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-3.0.1.tgz#3cf527bcd3fec82c231c38d47dd75f3364747edb" @@ -3748,6 +4538,20 @@ "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" +"@smithy/util-stream@^3.1.3": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-3.1.3.tgz#699ee2397cc1d474e46d2034039d5263812dca64" + integrity sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw== + dependencies: + "@smithy/fetch-http-handler" "^3.2.4" + "@smithy/node-http-handler" "^3.1.4" + "@smithy/types" "^3.3.0" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-hex-encoding" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + "@smithy/util-uri-escape@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz#e43358a78bf45d50bb736770077f0f09195b6f54" @@ -3755,6 +4559,14 @@ dependencies: tslib "^2.6.2" +"@smithy/util-utf8@^2.0.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5" + integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== + dependencies: + "@smithy/util-buffer-from" "^2.2.0" + tslib "^2.6.2" + "@smithy/util-utf8@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-3.0.0.tgz#1a6a823d47cbec1fd6933e5fc87df975286d9d6a" @@ -5206,6 +6018,15 @@ asn1.js@^4.10.1: inherits "^2.0.1" minimalistic-assert "^1.0.0" +asn1js@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" + integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== + dependencies: + pvtsutils "^1.3.2" + pvutils "^1.1.3" + tslib "^2.4.0" + assert@^1.1.1: version "1.5.1" resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.1.tgz#038ab248e4ff078e7bc2485ba6e6388466c78f76" @@ -5261,6 +6082,17 @@ avvio@^8.3.0: "@fastify/error" "^3.3.0" fastq "^1.17.1" +aws-kms-signer@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/aws-kms-signer/-/aws-kms-signer-0.5.3.tgz#02f9c3122ba79c6bf28e43f2cf3fad1db06afcf9" + integrity sha512-NUtftorrEcO+ODBfL4S/IIxNJMljaM/l233TRCFK0L0sOY/nOjbm2BLqLQOK/EUZ5i0pP1HK/32Lp6APdY9nCg== + dependencies: + "@aws-sdk/client-kms" "^3.28.0" + asn1js "^3.0.5" + bn.js "^5.2.0" + keccak "^3.0.2" + secp256k1 "4.0" + aws-sdk@^2.922.0: version "2.1632.0" resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1632.0.tgz#24bd0bd000f500300f7d52ac4c6e316a0311e25d" @@ -5668,6 +6500,11 @@ chalk@^4.0.0, chalk@^4.0.2: ansi-styles "^4.1.0" supports-color "^7.1.0" +change-case@5.4.4: + version "5.4.4" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-5.4.4.tgz#0d52b507d8fb8f204343432381d1a6d7bff97a02" + integrity sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w== + check-error@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" @@ -5695,6 +6532,11 @@ chokidar@^3.6.0: optionalDependencies: fsevents "~2.3.2" +chownr@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4" + integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== + cid-tool@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cid-tool/-/cid-tool-3.0.0.tgz#557540c5896d204503ef0ece848b88bbb350b90a" @@ -6995,7 +7837,7 @@ eventemitter3@4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== -eventemitter3@^4.0.4: +eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== @@ -7038,6 +7880,24 @@ execa@^8.0.1: signal-exit "^4.1.0" strip-final-newline "^3.0.0" +execa@^9.1.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-9.4.0.tgz#071ff6516c46eb82af9a559dba3c891637a10f3f" + integrity sha512-yKHlle2YGxZE842MERVIplWwNH5VYmqqcPFgtnlU//K8gxuFFXu0pwd/CrfXTumFpeEiufsP7+opT/bPJa1yVw== + dependencies: + "@sindresorhus/merge-streams" "^4.0.0" + cross-spawn "^7.0.3" + figures "^6.1.0" + get-stream "^9.0.0" + human-signals "^8.0.0" + is-plain-obj "^4.1.0" + is-stream "^4.0.1" + npm-run-path "^6.0.0" + pretty-ms "^9.0.0" + signal-exit "^4.1.0" + strip-final-newline "^4.0.0" + yoctocolors "^2.0.0" + explain-error@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/explain-error/-/explain-error-1.0.4.tgz#a793d3ac0cad4c6ab571e9968fbbab6cb2532929" @@ -7175,6 +8035,13 @@ fast-xml-parser@4.2.5: dependencies: strnum "^1.0.5" +fast-xml-parser@4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz#86dbf3f18edf8739326447bcaac31b4ae7f6514f" + integrity sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw== + dependencies: + strnum "^1.0.5" + fastify-plugin@^4.0.0, fastify-plugin@^4.5.0: version "4.5.1" resolved "https://registry.yarnpkg.com/fastify-plugin/-/fastify-plugin-4.5.1.tgz#44dc6a3cc2cce0988bc09e13f160120bbd91dbee" @@ -7221,6 +8088,13 @@ fecha@^4.2.0: resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== +figures@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-6.1.0.tgz#935479f51865fa7479f6fa94fc6fc7ac14e62c4a" + integrity sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg== + dependencies: + is-unicode-supported "^2.0.0" + file-entry-cache@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" @@ -7308,6 +8182,11 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== +follow-redirects@^1.0.0: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + follow-redirects@^1.15.6: version "1.15.6" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" @@ -7458,11 +8337,24 @@ get-port-please@^3.1.2: resolved "https://registry.yarnpkg.com/get-port-please/-/get-port-please-3.1.2.tgz#502795e56217128e4183025c89a48c71652f4e49" integrity sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ== +get-port@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-7.1.0.tgz#d5a500ebfc7aa705294ec2b83cc38c5d0e364fec" + integrity sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw== + get-stream@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== +get-stream@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-9.0.1.tgz#95157d21df8eb90d1647102b63039b1df60ebd27" + integrity sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA== + dependencies: + "@sec-ant/readable-stream" "^0.4.1" + is-stream "^4.0.1" + getopts@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4" @@ -7482,7 +8374,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^10.4.1: +glob@^10.3.7, glob@^10.4.1: version "10.4.5" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== @@ -7574,7 +8466,7 @@ google-auth-library@^9.3.0: gtoken "^7.0.0" jws "^4.0.0" -google-gax@^3.0.1: +google-gax@^3.0.1, google-gax@^3.5.8: version "3.6.1" resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-3.6.1.tgz#02c78fc496f5adf86f2ca9145545f4b6575f6118" integrity sha512-g/lcUjGcB6DSw2HxgEmCDOrI/CByOwqRvsuUvNalHUK2iPPPlmAIpbMbl62u0YufGMr8zgE3JL7th6dCb1Ry+w== @@ -7804,6 +8696,15 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + http-shutdown@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/http-shutdown/-/http-shutdown-1.2.2.tgz#41bc78fc767637c4c95179bc492f312c0ae64c5f" @@ -7840,6 +8741,11 @@ human-signals@^5.0.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== +human-signals@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-8.0.0.tgz#2d3d63481c7c2319f0373428b01ffe30da6df852" + integrity sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -8053,6 +8959,11 @@ is-path-inside@^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@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + is-stream-ended@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-stream-ended/-/is-stream-ended-0.1.4.tgz#f50224e95e06bce0e356d440a4827cd35b267eda" @@ -8068,6 +8979,11 @@ is-stream@^3.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== +is-stream@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-4.0.1.tgz#375cf891e16d2e4baec250b85926cffc14720d9b" + integrity sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A== + 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" @@ -8080,6 +8996,11 @@ is-typedarray@^1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== +is-unicode-supported@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz#09f0ab0de6d3744d48d265ebb98f65d11f2a9b3a" + integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ== + is-what@^4.1.8: version "4.1.16" resolved "https://registry.yarnpkg.com/is-what/-/is-what-4.1.16.tgz#1ad860a19da8b4895ad5495da3182ce2acdd7a6f" @@ -8386,7 +9307,7 @@ jws@^4.0.0: jwa "^2.0.0" safe-buffer "^5.0.1" -keccak@^3.0.0, keccak@^3.0.3: +keccak@^3.0.0, keccak@^3.0.2, keccak@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== @@ -8904,11 +9825,19 @@ minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4, minipass@^7.1.0, minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== +minizlib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-3.0.1.tgz#46d5329d1eb3c83924eff1d3b858ca0a31581012" + integrity sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg== + dependencies: + minipass "^7.0.4" + rimraf "^5.0.5" + mipd@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mipd/-/mipd-0.0.7.tgz#bb5559e21fa18dc3d9fe1c08902ef14b7ce32fd9" @@ -8919,6 +9848,11 @@ mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mkdirp@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" + integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== + mlly@^1.6.1, mlly@^1.7.1: version "1.7.1" resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.7.1.tgz#e0336429bb0731b6a8e887b438cbdae522c8f32f" @@ -9176,6 +10110,14 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" +npm-run-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-6.0.0.tgz#25cfdc4eae04976f3349c0b1afc089052c362537" + integrity sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA== + dependencies: + path-key "^4.0.0" + unicorn-magic "^0.3.0" + number-to-bn@1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" @@ -9410,6 +10352,11 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-ms@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-4.0.0.tgz#c0c058edd47c2a590151a718990533fd62803df4" + integrity sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw== + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -9760,6 +10707,13 @@ prettier@^2.8.7: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +pretty-ms@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-9.1.0.tgz#0ad44de6086454f48a168e5abb3c26f8db1b3253" + integrity sha512-o1piW0n3tgKIKCwk2vpM/vOV13zjJzvP37Ioze54YlTHE06m4tjEbzg9WsKkvTuyYln2DHjo5pY4qrZGI0otpw== + dependencies: + parse-ms "^4.0.0" + prisma@^5.14.0: version "5.17.0" resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.17.0.tgz#267b43921ab94805b010537cffa5ccaf530fa066" @@ -9795,6 +10749,18 @@ prom-client@^15.1.3: "@opentelemetry/api" "^1.4.0" tdigest "^0.1.1" +prool@^0.0.16: + version "0.0.16" + resolved "https://registry.yarnpkg.com/prool/-/prool-0.0.16.tgz#b18c76fd102485ce4c706bb6031bd85a49859a45" + integrity sha512-s+i66jsINIJQd8w5iGunT8hCeM6RSXjL8qgXDTvcIuaAOjVRVMh0/PgbJ90KR3JAprXWXpa5XQnDEc4fLnvy1Q== + dependencies: + change-case "5.4.4" + eventemitter3 "^5.0.1" + execa "^9.1.0" + get-port "^7.1.0" + http-proxy "^1.18.1" + tar "7.2.0" + proto3-json-serializer@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/proto3-json-serializer/-/proto3-json-serializer-1.1.1.tgz#1b5703152b6ce811c5cdcc6468032caf53521331" @@ -9825,7 +10791,25 @@ protobufjs-cli@1.1.1: tmp "^0.2.1" uglify-js "^3.7.7" -protobufjs@7.2.4, protobufjs@7.3.0, protobufjs@>=7.2.5, protobufjs@^7.0.0, protobufjs@^7.2.5: +protobufjs@7.2.4: + version "7.2.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.4.tgz#3fc1ec0cdc89dd91aef9ba6037ba07408485c3ae" + integrity sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + +protobufjs@7.3.0, protobufjs@>=7.2.5, protobufjs@^7.0.0, protobufjs@^7.2.5: version "7.3.0" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.3.0.tgz#a32ec0422c039798c41a0700306a6e305b9cb32c" integrity sha512-YWD03n3shzV9ImZRX3ccbjqLxj7NokGN0V/ESiBV5xWqrommYHYiihuIyavq03pWSGqlyvYUFmfoMKd+1rPA/g== @@ -9893,6 +10877,18 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +pvtsutils@^1.3.2: + version "1.3.5" + resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.5.tgz#b8705b437b7b134cd7fd858f025a23456f1ce910" + integrity sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA== + dependencies: + tslib "^2.6.1" + +pvutils@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" + integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== + qrcode@1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.3.tgz#03afa80912c0dccf12bc93f615a535aad1066170" @@ -10141,6 +11137,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== + requizzle@^0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.4.tgz#319eb658b28c370f0c20f968fa8ceab98c13d27c" @@ -10204,6 +11205,13 @@ rfdc@^1.1.4, rfdc@^1.2.0, rfdc@^1.3.0: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.1.tgz#2b6d4df52dffe8bb346992a10ea9451f24373a8f" integrity sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg== +rimraf@^5.0.5: + version "5.0.10" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.10.tgz#23b9843d3dc92db71f96e1a2ce92e39fd2a8221c" + integrity sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ== + dependencies: + glob "^10.3.7" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -10298,7 +11306,7 @@ scrypt-js@3.0.1, scrypt-js@^3.0.0: resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== -secp256k1@^4.0.1: +secp256k1@4.0, secp256k1@^4.0.1, secp256k1@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== @@ -10571,7 +11579,16 @@ strict-uri-encode@^2.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + 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.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: 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== @@ -10608,7 +11625,14 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm: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.1" + +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== @@ -10627,6 +11651,11 @@ strip-final-newline@^3.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== +strip-final-newline@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-4.0.0.tgz#35a369ec2ac43df356e3edd5dcebb6429aa1fa5c" + integrity sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw== + strip-hex-prefix@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" @@ -10697,6 +11726,18 @@ system-architecture@^0.1.0: resolved "https://registry.yarnpkg.com/system-architecture/-/system-architecture-0.1.0.tgz#71012b3ac141427d97c67c56bc7921af6bff122d" integrity sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA== +tar@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.2.0.tgz#f03ae6ecd2e2bab880f2ef33450f502e761d7548" + integrity sha512-hctwP0Nb4AB60bj8WQgRYaMOuJYRAPMGiQUAotms5igN8ppfQM+IvjQ5HcKu1MaZh2Wy2KWVTe563Yj8dfc14w== + dependencies: + "@isaacs/fs-minipass" "^4.0.0" + chownr "^3.0.0" + minipass "^7.1.0" + minizlib "^3.0.1" + mkdirp "^3.0.1" + yallist "^5.0.0" + tarn@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693" @@ -10923,6 +11964,11 @@ tslib@^2.0.0, tslib@^2.1.0, tslib@^2.3.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== +tslib@^2.4.0, tslib@^2.6.1: + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== + tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" @@ -11061,6 +12107,11 @@ unfetch@^4.2.0: resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== +unicorn-magic@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz#4efd45c85a69e0dd576d25532fbfa22aa5c8a104" + integrity sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA== + universalify@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" @@ -11637,7 +12688,7 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -11655,6 +12706,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -11784,6 +12844,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yallist@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533" + integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== + yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" @@ -11865,6 +12930,11 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +yoctocolors@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/yoctocolors/-/yoctocolors-2.1.1.tgz#e0167474e9fbb9e8b3ecca738deaa61dd12e56fc" + integrity sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ== + zksync-web3@^0.14.3: version "0.14.4" resolved "https://registry.yarnpkg.com/zksync-web3/-/zksync-web3-0.14.4.tgz#0b70a7e1a9d45cc57c0971736079185746d46b1f"