diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml new file mode 100644 index 000000000..4d31c0e71 --- /dev/null +++ b/.github/workflows/beta.yml @@ -0,0 +1,43 @@ +name: Build on beta branch + +on: + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + ref: beta # checkout the main branch to build nightly + + - name: Check Disk Space Before Build + run: df -h + + - name: Docker Prune + run: docker system prune -af + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and Push Docker Image + uses: docker/build-push-action@v2 + with: + context: . + target: prod + platforms: linux/amd64,linux/arm64 + push: true + tags: thirdweb/engine:beta-nightly + build-args: | + ENGINE_VERSION=beta-nightly + + - name: Check Disk Space After Build + run: df -h diff --git a/.vscode/settings.json b/.vscode/settings.json index 6fe69998f..2f2181835 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,6 @@ "editor.formatOnSave": true, "eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }], "typescript.tsdk": "node_modules/typescript/lib", - "typescript.enablePromptUseWorkspaceTsdk": true + "typescript.enablePromptUseWorkspaceTsdk": true, + "cSpell.words": ["idempotency"] } diff --git a/docker-compose-infra.yml b/docker-compose-infra.yml index 9f381e4bf..de4ff4f68 100644 --- a/docker-compose-infra.yml +++ b/docker-compose-infra.yml @@ -20,5 +20,29 @@ services: cpus: "2" memory: 2G + redis: + container_name: redis + image: redis:latest + restart: always + ports: + - 6379:6379 + volumes: + - redis_data:/data + + bullboard: + container_name: bullboard + image: deadly0/bull-board:latest + restart: always + ports: + - 3000:3000 + environment: + REDIS_HOST: redis + REDIS_PORT: 6379 + REDIS_USE_TLS: "false" + BULL_PREFIX: bull + depends_on: + - redis + volumes: psql_db: + redis_data: diff --git a/docker-compose.yml b/docker-compose.yml index ccd10cc07..529561973 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,6 +15,13 @@ services: reservations: cpus: "2" memory: 2G + redis: + image: redis:latest + container_name: redis_container + ports: + - "6379:6379" + volumes: + - redis_data:/data engine: build: @@ -38,3 +45,4 @@ services: volumes: db_data: + redis_data: diff --git a/package.json b/package.json index 03651bb90..6c77c3560 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "docker": "docker compose --env-file ./.env up --remove-orphans", "docker:build": "docker compose build --no-cache", - "dev": "yarn dev:infra && yarn dev:db && yarn dev:run", + "dev": "yarn dev:infra && yarn dev:run", "dev:infra": "docker compose -f ./docker-compose-infra.yml up -d", "dev:db": "yarn prisma:setup:dev", "dev:run": "nodemon --watch 'src/**/*.ts' --exec 'npx tsx ./src/index.ts' --files src/index.ts", @@ -47,6 +47,7 @@ "@types/base-64": "^1.0.2", "base-64": "^1.0.0", "body-parser": "^1.20.2", + "bullmq": "^5.4.2", "cookie": "^0.5.0", "cookie-parser": "^1.4.6", "copyfiles": "^2.4.1", @@ -54,9 +55,12 @@ "crypto-js": "^4.2.0", "dotenv": "^16.0.3", "ethers": "5", + "ethers-aws-kms-signer": "^1.3.2", + "ethers-gcp-kms-signer": "^1.1.6", "fastify": "^4.15.0", "fastify-plugin": "^4.5.0", "http-status-codes": "^2.2.0", + "ioredis": "^5.3.2", "knex": "^3.1.0", "mnemonist": "^0.39.8", "node-cron": "^3.0.2", @@ -64,7 +68,8 @@ "pino": "^8.15.1", "pino-pretty": "^10.0.0", "prisma": "^5.2.0", - "thirdweb": "^5.0.0-beta-ca68bc77e74f594360b4da1e9e77793b66cfb12a-20240323045412", + "superjson": "^2.2.1", + "thirdweb": "^5.1.0", "uuid": "^9.0.1", "viem": "^1.14.0", "zod": "^3.21.4" diff --git a/src/db/transactions/cleanTxs.ts b/src/db/transactions/cleanTxs.ts deleted file mode 100644 index 064f334e1..000000000 --- a/src/db/transactions/cleanTxs.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Transactions } from "@prisma/client"; -import { Static } from "@sinclair/typebox"; -import { transactionResponseSchema } from "../../server/schemas/transaction"; - -// TODO: This shouldn't need to exist with zod -export const cleanTxs = ( - txs: Transactions[], -): Static[] => { - return txs.map((tx) => { - return { - ...tx, - queueId: tx.id, - id: undefined, - queuedAt: tx.queuedAt.toISOString(), - sentAt: tx.sentAt?.toISOString() || null, - minedAt: tx.minedAt?.toISOString() || null, - cancelledAt: tx.cancelledAt?.toISOString() || null, - status: !!tx.errorMessage - ? "errored" - : !!tx.minedAt - ? "mined" - : !!tx.cancelledAt - ? "cancelled" - : !!tx.sentAt && tx.retryCount === 0 - ? "sent" - : !!tx.sentAt && tx.retryCount > 0 - ? "retried" - : "queued", - }; - }); -}; diff --git a/src/db/transactions/getAllTxs.ts b/src/db/transactions/getAllTxs.ts index 232217a05..a89c78929 100644 --- a/src/db/transactions/getAllTxs.ts +++ b/src/db/transactions/getAllTxs.ts @@ -1,13 +1,8 @@ import { Transactions } from "@prisma/client"; -import { Static } from "@sinclair/typebox"; import { ContractExtension } from "../../schema/extension"; import { PrismaTransaction } from "../../schema/prisma"; -import { - TransactionStatus, - transactionResponseSchema, -} from "../../server/schemas/transaction"; +import { TransactionStatus } from "../../server/schemas/transaction"; import { getPrismaWithPostgresTx } from "../client"; -import { cleanTxs } from "./cleanTxs"; interface GetAllTxsParams { pgtx?: PrismaTransaction; @@ -18,7 +13,7 @@ interface GetAllTxsParams { } interface GetAllTxsResponse { - transactions: Static[]; + transactions: Transactions[]; totalCount: number; } @@ -99,7 +94,7 @@ export const getAllTxs = async ({ const [totalCount, txs] = await Promise.all([totalCountPromise, txsPromise]); return { - transactions: cleanTxs(txs), + transactions: txs, totalCount: totalCount, }; }; diff --git a/src/db/transactions/getAllTxsByWallet.ts b/src/db/transactions/getAllTxsByWallet.ts index 791cefad4..e5c7c6427 100644 --- a/src/db/transactions/getAllTxsByWallet.ts +++ b/src/db/transactions/getAllTxsByWallet.ts @@ -1,6 +1,5 @@ import { PrismaTransaction } from "../../schema/prisma"; import { getPrismaWithPostgresTx } from "../client"; -import { cleanTxs } from "./cleanTxs"; interface GetAllTxsByWalletParams { pgtx?: PrismaTransaction; @@ -14,7 +13,7 @@ export const getAllTxsByWallet = async ({ }: GetAllTxsByWalletParams) => { const prisma = getPrismaWithPostgresTx(pgtx); - const txs = await prisma.transactions.findMany({ + return await prisma.transactions.findMany({ where: { fromAddress: walletAddress.toLowerCase(), }, @@ -24,6 +23,4 @@ export const getAllTxsByWallet = async ({ }, ], }); - - return cleanTxs(txs); }; diff --git a/src/db/transactions/getQueuedTxs.ts b/src/db/transactions/getQueuedTxs.ts index 5164b88b2..61d21dac7 100644 --- a/src/db/transactions/getQueuedTxs.ts +++ b/src/db/transactions/getQueuedTxs.ts @@ -23,6 +23,7 @@ export const getQueuedTxs = async ({ pgtx }: GetQueuedTxsParams = {}): Promise< "sentAt" IS NULL AND "minedAt" IS NULL AND "cancelledAt" IS NULL + AND "errorMessage" IS NULL ORDER BY "queuedAt" ASC diff --git a/src/db/transactions/getTxById.ts b/src/db/transactions/getTxById.ts index 618764fb0..8682a75f5 100644 --- a/src/db/transactions/getTxById.ts +++ b/src/db/transactions/getTxById.ts @@ -1,8 +1,10 @@ import { Static } from "@sinclair/typebox"; import { PrismaTransaction } from "../../schema/prisma"; -import { transactionResponseSchema } from "../../server/schemas/transaction"; +import { + toTransactionResponse, + transactionResponseSchema, +} from "../../server/schemas/transaction"; import { getPrismaWithPostgresTx } from "../client"; -import { cleanTxs } from "./cleanTxs"; interface GetTxByIdParams { queueId: string; @@ -24,11 +26,5 @@ export const getTxById = async ({ id: queueId, }, }); - - if (!tx) { - return null; - } - - const [cleanedTx] = cleanTxs([tx]); - return cleanedTx; + return tx ? toTransactionResponse(tx) : null; }; diff --git a/src/db/transactions/getTxByIds.ts b/src/db/transactions/getTxByIds.ts deleted file mode 100644 index 5ef87a9e2..000000000 --- a/src/db/transactions/getTxByIds.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Static } from "@sinclair/typebox"; -import { PrismaTransaction } from "../../schema/prisma"; -import { transactionResponseSchema } from "../../server/schemas/transaction"; -import { prisma } from "../client"; -import { cleanTxs } from "./cleanTxs"; -interface GetTxByIdsParams { - queueIds: string[]; - pgtx?: PrismaTransaction; -} - -export const getTxByIds = async ({ - queueIds, -}: GetTxByIdsParams): Promise< - Static[] | null -> => { - const tx = await prisma.transactions.findMany({ - where: { - id: { - in: queueIds, - }, - }, - }); - - if (!tx || tx.length === 0) { - return null; - } - - const cleanedTx = cleanTxs(tx); - return cleanedTx; -}; diff --git a/src/db/transactions/getTxsByGroupId.ts b/src/db/transactions/getTxsByGroupId.ts index f20c6d977..e14f6f741 100644 --- a/src/db/transactions/getTxsByGroupId.ts +++ b/src/db/transactions/getTxsByGroupId.ts @@ -1,6 +1,6 @@ +import { Transactions } from "@prisma/client"; import { PrismaTransaction } from "../../schema/prisma"; import { getPrismaWithPostgresTx } from "../client"; -import { cleanTxs } from "./cleanTxs"; interface GetTxsByGroupIdParams { pgtx?: PrismaTransaction; @@ -10,17 +10,12 @@ interface GetTxsByGroupIdParams { export const getTxsByGroupId = async ({ pgtx, groupId, -}: GetTxsByGroupIdParams) => { +}: GetTxsByGroupIdParams): Promise => { const prisma = getPrismaWithPostgresTx(pgtx); const txs = await prisma.transactions.findMany({ where: { groupId, }, }); - - if (!txs) { - return []; - } - - return cleanTxs(txs); + return txs; }; diff --git a/src/db/transactions/queueTx.ts b/src/db/transactions/queueTx.ts index 3643cf25c..891d51105 100644 --- a/src/db/transactions/queueTx.ts +++ b/src/db/transactions/queueTx.ts @@ -2,11 +2,10 @@ import type { DeployTransaction, Transaction } from "@thirdweb-dev/sdk"; import { ERC4337EthersSigner } from "@thirdweb-dev/wallets/dist/declarations/src/evm/connectors/smart-wallet/lib/erc4337-signer"; import { BigNumber } from "ethers"; import type { ContractExtension } from "../../schema/extension"; -import { PrismaTransaction } from "../../schema/prisma"; +import { InputTransaction } from "../../schema/transaction"; import { queueTxRaw } from "./queueTxRaw"; interface QueueTxParams { - pgtx?: PrismaTransaction; tx: Transaction | DeployTransaction; chainId: number; extension: ContractExtension; @@ -18,59 +17,48 @@ interface QueueTxParams { } export const queueTx = async ({ - pgtx, - tx, + tx: sdkTx, chainId, extension, deployedContractAddress, deployedContractType, simulateTx, idempotencyKey, -}: QueueTxParams) => { - // TODO: We need a much safer way of detecting if the transaction should be a user operation - const isUserOp = !!(tx.getSigner as ERC4337EthersSigner).erc4337provider; - const txData = { +}: QueueTxParams): Promise => { + const tx: InputTransaction = { + idempotencyKey, chainId: chainId.toString(), - functionName: tx.getMethod(), - functionArgs: JSON.stringify(tx.getArgs()), + functionName: sdkTx.getMethod(), + functionArgs: JSON.stringify(sdkTx.getArgs()), extension, - deployedContractAddress: deployedContractAddress, - deployedContractType: deployedContractType, - data: tx.encode(), - value: BigNumber.from(await tx.getValue()).toHexString(), + deployedContractAddress, + deployedContractType, + data: sdkTx.encode(), + value: BigNumber.from(await sdkTx.getValue()).toHexString(), }; + // TODO: We need a much safer way of detecting if the transaction should be a user operation + const isUserOp = !!(sdkTx.getSigner as ERC4337EthersSigner).erc4337provider; if (isUserOp) { - const signerAddress = await ( - tx.getSigner as ERC4337EthersSigner - ).originalSigner.getAddress(); - const accountAddress = await tx.getSignerAddress(); - const target = tx.getTarget(); - - const { id: queueId } = await queueTxRaw({ - pgtx, - ...txData, - signerAddress, - accountAddress, - target, + return await queueTxRaw({ + tx: { + ...tx, + signerAddress: await ( + sdkTx.getSigner as ERC4337EthersSigner + ).originalSigner.getAddress(), + accountAddress: await sdkTx.getSignerAddress(), + target: sdkTx.getTarget(), + }, simulateTx, - idempotencyKey, }); - - return queueId; - } else { - const fromAddress = await tx.getSignerAddress(); - const toAddress = tx.getTarget(); - - const { id: queueId } = await queueTxRaw({ - pgtx, - ...txData, - fromAddress, - toAddress, - simulateTx, - idempotencyKey, - }); - - return queueId; } + + return await queueTxRaw({ + tx: { + ...tx, + fromAddress: await sdkTx.getSignerAddress(), + toAddress: sdkTx.getTarget(), + }, + simulateTx, + }); }; diff --git a/src/db/transactions/queueTxRaw.ts b/src/db/transactions/queueTxRaw.ts index 2f25b9b61..ba95ae44a 100644 --- a/src/db/transactions/queueTxRaw.ts +++ b/src/db/transactions/queueTxRaw.ts @@ -1,111 +1,54 @@ -import type { Prisma, Transactions } from "@prisma/client"; -import { v4 as uuid } from "uuid"; -import { PrismaTransaction } from "../../schema/prisma"; -import { TransactionStatus } from "../../server/schemas/transaction"; -import { simulateTx } from "../../server/utils/simulateTx"; -import { UsageEventTxActionEnum, reportUsage } from "../../utils/usage"; -import { sendWebhooks } from "../../utils/webhook"; -import { getPrismaWithPostgresTx } from "../client"; +import { randomUUID } from "crypto"; +import { InputTransaction, QueuedTransaction } from "../../schema/transaction"; +import { simulate } from "../../server/utils/simulateTx"; +import { UsageEventType, reportUsage } from "../../utils/usage"; +import { IngestQueue } from "../../worker/queues/queues"; import { getWalletDetails } from "../wallets/getWalletDetails"; -type QueueTxRawParams = Omit< - Prisma.TransactionsCreateInput, - "fromAddress" | "signerAddress" -> & - ( - | { - fromAddress: string; - signerAddress?: never; - } - | { - fromAddress?: never; - signerAddress: string; - } - ) & { - pgtx?: PrismaTransaction; - simulateTx?: boolean; - idempotencyKey?: string; - }; +type QueueTxRawParams = { + tx: InputTransaction; + simulateTx?: boolean; +}; +/** + * Enqueues a transaction. + * @returns string The queueId generated for this transaction. + */ export const queueTxRaw = async ({ - pgtx, - simulateTx: shouldSimulate, - idempotencyKey, - ...tx -}: QueueTxRawParams): Promise => { - const prisma = getPrismaWithPostgresTx(pgtx); - + tx, + simulateTx, +}: QueueTxRawParams): Promise => { + // Assert a valid backend wallet address. + const walletAddress = tx.fromAddress || tx.signerAddress; + if (!walletAddress) { + throw new Error("Transaction missing sender address."); + } const walletDetails = await getWalletDetails({ - pgtx, - address: (tx.fromAddress || tx.signerAddress) as string, + address: walletAddress, }); - if (!walletDetails) { - throw new Error( - `No backend wallet found with address ${ - tx.fromAddress || tx.signerAddress - }`, - ); - } - - if (shouldSimulate) { - await simulateTx({ txRaw: tx }); + throw new Error(`Backend wallet not found: ${walletAddress}`); } - const insertData = { + // Build a QueuedTransaction. + const queueId = randomUUID(); + const queuedTx: QueuedTransaction = { ...tx, - id: uuid(), - fromAddress: tx.fromAddress?.toLowerCase(), - toAddress: tx.toAddress?.toLowerCase(), - target: tx.target?.toLowerCase(), - signerAddress: tx.signerAddress?.toLowerCase(), - accountAddress: tx.accountAddress?.toLowerCase(), + id: queueId, + idempotencyKey: tx.idempotencyKey ?? queueId, + value: tx.value ?? "0", + queuedAt: new Date(), }; - let txRow: Transactions; - if (idempotencyKey) { - // Upsert the tx (insert if not exists). - txRow = await prisma.transactions.upsert({ - where: { idempotencyKey }, - create: { - ...insertData, - idempotencyKey, - }, - update: {}, - }); - } else { - // Insert the tx. - txRow = await prisma.transactions.create({ - data: { - ...insertData, - // Use queueId to ensure uniqueness. - idempotencyKey: insertData.id, - }, - }); + // (Optional) Simulate the transaction. + if (simulateTx) { + await simulate({ txRaw: queuedTx }); } - // Send queued webhook. - sendWebhooks([ - { - queueId: txRow.id, - status: TransactionStatus.Queued, - }, - ]).catch((err) => {}); - - reportUsage([ - { - input: { - chainId: tx.chainId || undefined, - fromAddress: tx.fromAddress || undefined, - toAddress: tx.toAddress || undefined, - value: tx.value || undefined, - transactionHash: tx.transactionHash || undefined, - functionName: tx.functionName || undefined, - extension: tx.extension || undefined, - }, - action: UsageEventTxActionEnum.QueueTx, - }, - ]); + // Enqueue the job. + const job = { tx: queuedTx }; + await IngestQueue.add(job); - return txRow; + reportUsage([{ action: UsageEventType.QueueTx, data: queuedTx }]); + return queueId; }; diff --git a/src/db/transactions/updateTx.ts b/src/db/transactions/updateTx.ts index 139231e30..b5f9d6891 100644 --- a/src/db/transactions/updateTx.ts +++ b/src/db/transactions/updateTx.ts @@ -1,6 +1,9 @@ +import { Transactions } from "@prisma/client"; import { BigNumber, ethers } from "ethers"; import { PrismaTransaction } from "../../schema/prisma"; +import { WebhooksEventTypes } from "../../schema/webhooks"; import { TransactionStatus } from "../../server/schemas/transaction"; +import { WebhookQueue } from "../../worker/queues/queues"; import { getPrismaWithPostgresTx } from "../client"; interface UpdateTxParams { @@ -46,9 +49,12 @@ type UpdateTxData = export const updateTx = async ({ pgtx, queueId, data }: UpdateTxParams) => { const prisma = getPrismaWithPostgresTx(pgtx); + + let updatedTx: Transactions | null = null; + switch (data.status) { case TransactionStatus.Cancelled: - await prisma.transactions.update({ + updatedTx = await prisma.transactions.update({ where: { id: queueId, }, @@ -58,7 +64,7 @@ export const updateTx = async ({ pgtx, queueId, data }: UpdateTxParams) => { }); break; case TransactionStatus.Errored: - await prisma.transactions.update({ + updatedTx = await prisma.transactions.update({ where: { id: queueId, }, @@ -68,7 +74,7 @@ export const updateTx = async ({ pgtx, queueId, data }: UpdateTxParams) => { }); break; case TransactionStatus.Sent: - await prisma.transactions.update({ + updatedTx = await prisma.transactions.update({ where: { id: queueId, }, @@ -88,7 +94,7 @@ export const updateTx = async ({ pgtx, queueId, data }: UpdateTxParams) => { }); break; case TransactionStatus.UserOpSent: - await prisma.transactions.update({ + updatedTx = await prisma.transactions.update({ where: { id: queueId, }, @@ -99,11 +105,12 @@ export const updateTx = async ({ pgtx, queueId, data }: UpdateTxParams) => { }); break; case TransactionStatus.Mined: - await prisma.transactions.update({ + updatedTx = await prisma.transactions.update({ where: { id: queueId, }, data: { + transactionHash: data.transactionHash, minedAt: data.minedAt, blockNumber: data.blockNumber, onChainTxStatus: data.onChainTxStatus, @@ -112,8 +119,17 @@ export const updateTx = async ({ pgtx, queueId, data }: UpdateTxParams) => { gasLimit: data.gasLimit, maxFeePerGas: data.maxFeePerGas, maxPriorityFeePerGas: data.maxPriorityFeePerGas, + nonce: data.nonce, }, }); break; } + + if (updatedTx) { + await WebhookQueue.add({ + type: WebhooksEventTypes.ALL_TRANSACTIONS, + status: data.status, + tx: updatedTx, + }); + } }; diff --git a/src/db/wallets/createWalletDetails.ts b/src/db/wallets/createWalletDetails.ts index af6cdee2d..6728c583c 100644 --- a/src/db/wallets/createWalletDetails.ts +++ b/src/db/wallets/createWalletDetails.ts @@ -23,18 +23,6 @@ export const createWalletDetails = async ({ }: CreateWalletDetailsParams) => { const prisma = getPrismaWithPostgresTx(pgtx); - const wallet = await prisma.walletDetails.findUnique({ - where: { - address: walletDetails.address.toLowerCase(), - }, - }); - - if (wallet) { - throw new Error( - `Wallet with address ${walletDetails.address} has already been added!`, - ); - } - return prisma.walletDetails.create({ data: { ...walletDetails, diff --git a/src/db/wallets/getWalletDetails.ts b/src/db/wallets/getWalletDetails.ts index c37709064..ff309d717 100644 --- a/src/db/wallets/getWalletDetails.ts +++ b/src/db/wallets/getWalletDetails.ts @@ -1,20 +1,35 @@ -import { PrismaTransaction } from "../../schema/prisma"; +import { WalletDetails } from "@prisma/client"; +import { + cacheKeyWalletDetails, + getCache, + setCache, +} from "../../utils/redis/cache"; import { getPrismaWithPostgresTx } from "../client"; interface GetWalletDetailsParams { - pgtx?: PrismaTransaction; address: string; } export const getWalletDetails = async ({ - pgtx, - address, -}: GetWalletDetailsParams) => { - const prisma = getPrismaWithPostgresTx(pgtx); + address: _address, +}: GetWalletDetailsParams): Promise => { + const address = _address.toLowerCase(); - return prisma.walletDetails.findUnique({ + const key = cacheKeyWalletDetails(address); + const cached = await getCache(key); + if (cached) { + return cached; + } + + const prisma = getPrismaWithPostgresTx(); + const walletDetails = await prisma.walletDetails.findUnique({ where: { address: address.toLowerCase(), }, }); + + if (walletDetails) { + setCache(key, walletDetails); + } + return walletDetails; }; diff --git a/src/db/webhooks/deleteWebhook.ts b/src/db/webhooks/deleteWebhook.ts new file mode 100644 index 000000000..adc7870bc --- /dev/null +++ b/src/db/webhooks/deleteWebhook.ts @@ -0,0 +1,19 @@ +import { cacheKeyAllWebhooks, invalidateCache } from "../../utils/redis/cache"; +import { prisma } from "../client"; + +export const softDeleteWebhook = async (id: number) => { + const now = new Date(); + const webhook = await prisma.webhooks.update({ + where: { + id, + }, + data: { + revokedAt: now, + updatedAt: now, + }, + }); + + const key = cacheKeyAllWebhooks(); + await invalidateCache(key); + return webhook; +}; diff --git a/src/db/webhooks/getAllWebhooks.ts b/src/db/webhooks/getAllWebhooks.ts index 8583fa47d..b9fcb0429 100644 --- a/src/db/webhooks/getAllWebhooks.ts +++ b/src/db/webhooks/getAllWebhooks.ts @@ -1,27 +1,22 @@ import { Webhooks } from "@prisma/client"; -import { SanitizedWebHooksSchema } from "../../schema/webhooks"; +import { + cacheKeyAllWebhooks, + getCache, + setCache, +} from "../../utils/redis/cache"; import { prisma } from "../client"; -export const getAllWebhooks = async (): Promise => { - let webhooks = await prisma.webhooks.findMany({ - orderBy: { - id: "asc", - }, - }); - - return sanitizeData(webhooks); -}; +export const getAllWebhooks = async (): Promise => { + const key = cacheKeyAllWebhooks(); + const cached = await getCache(key); + if (cached) { + return cached; + } -const sanitizeData = (data: Webhooks[]): SanitizedWebHooksSchema[] => { - return data.map((webhook) => { - return { - url: webhook.url, - name: webhook.name, - eventType: webhook.eventType, - secret: webhook.secret ? webhook.secret : undefined, - createdAt: webhook.createdAt.toISOString(), - active: webhook.revokedAt ? false : true, - id: webhook.id, - }; + const allWebhooks = await prisma.webhooks.findMany({ + orderBy: { id: "asc" }, }); + await setCache(key, allWebhooks); + + return allWebhooks; }; diff --git a/src/db/webhooks/createWebhook.ts b/src/db/webhooks/insertWebhook.ts similarity index 74% rename from src/db/webhooks/createWebhook.ts rename to src/db/webhooks/insertWebhook.ts index f02a5e883..a1fcaa3a7 100644 --- a/src/db/webhooks/createWebhook.ts +++ b/src/db/webhooks/insertWebhook.ts @@ -1,5 +1,6 @@ import { createHash, randomBytes } from "crypto"; import { WebhooksEventTypes } from "../../schema/webhooks"; +import { cacheKeyAllWebhooks, invalidateCache } from "../../utils/redis/cache"; import { prisma } from "../client"; interface CreateWebhooksParams { @@ -18,7 +19,7 @@ export const insertWebhook = async ({ // hash the bytes to create the secret (this will not be stored by itself) const secret = createHash("sha512").update(bytes).digest("base64url"); - return prisma.webhooks.create({ + const webhook = await prisma.webhooks.create({ data: { url, name, @@ -26,4 +27,8 @@ export const insertWebhook = async ({ secret, }, }); + + const key = cacheKeyAllWebhooks(); + await invalidateCache(key); + return webhook; }; diff --git a/src/db/webhooks/revokeWebhook.ts b/src/db/webhooks/revokeWebhook.ts deleted file mode 100644 index 9d85f63bd..000000000 --- a/src/db/webhooks/revokeWebhook.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { StatusCodes } from "http-status-codes"; -import { createCustomError } from "../../server/middleware/error"; -import { prisma } from "../client"; - -interface RevokeWebhooksParams { - id: number; -} - -export const markWebhookAsRevoked = async ({ id }: RevokeWebhooksParams) => { - const currentTimestamp = new Date(); - - const exists = await prisma.webhooks.findUnique({ - where: { - id, - }, - }); - - if (!exists) - throw createCustomError( - `Webhook with id ${id} does not exist`, - StatusCodes.BAD_REQUEST, - "BAD_REQUEST", - ); - - return prisma.webhooks.update({ - where: { - id, - }, - data: { - revokedAt: currentTimestamp, - updatedAt: currentTimestamp, - }, - }); -}; diff --git a/src/index.ts b/src/index.ts index 20ddba2bc..4bcb2690e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,8 +2,10 @@ import { initServer } from "./server"; import { initWorker } from "./worker"; const main = async () => { - initServer(); - initWorker(); + // TODO: Revisit if/when we migrate data to Redis. + // await initSyncConfigFromPostgres(); + await initServer(); + await initWorker(); }; main(); diff --git a/src/prisma/schema.prisma b/src/prisma/schema.prisma index 57dfc7578..ede5314e0 100644 --- a/src/prisma/schema.prisma +++ b/src/prisma/schema.prisma @@ -125,6 +125,7 @@ model Transactions { signerAddress String? @map("signerAddress") accountAddress String? @map("accountAddress") target String? @map("target") + // @deprecated sender String? @map("sender") initCode String? @map("initCode") callData String? @map("callData") diff --git a/src/schema/transaction.ts b/src/schema/transaction.ts new file mode 100644 index 000000000..dcba42395 --- /dev/null +++ b/src/schema/transaction.ts @@ -0,0 +1,77 @@ +export type InputTransaction = { + groupId?: string; + idempotencyKey?: string; + + // Onchain data + chainId: string; + fromAddress?: string; + toAddress?: string; + data: string; + value?: string; + functionName?: string; + functionArgs?: string; + extension?: string; + + // User operation + target?: string; + signerAddress?: string; + accountAddress?: string; + + // Contract deployment + deployedContractAddress?: string; + deployedContractType?: string; +}; + +export type QueuedTransaction = InputTransaction & { + id: string; + idempotencyKey: string; + queuedAt: Date; + + // Onchain data + value: string; + + // User operation + initCode?: string; + callData?: string; + callGasLimit?: string; + verificationGasLimit?: string; + preVerificationGas?: string; + paymasterAndData?: string; +}; + +export type SentTransaction = QueuedTransaction & { + sentAt: Date; + sentAtBlockNumber: number; + + // Prepared onchain data + gasLimit: string; + nonce: number; + gasPrice: string; + maxFeePerGas: string; + maxPriorityFeePerGas: string; + + // From RPC response + transactionType: number; + transactionHash: string; + userOpHash?: string; + + // Retry data + retryCount: number; + retryGasValues?: boolean; + retryMaxPriorityFeePerGas?: string; + retryMaxFeePerGas?: string; +}; + +export type MinedTransaction = SentTransaction & { + minedAt: Date; + blockNumber: number; + onChainTxStatus: number; +}; + +export type ErroredTransaction = (QueuedTransaction | SentTransaction) & { + errorMessage: string; +}; + +export type CancelledTransaction = (QueuedTransaction | SentTransaction) & { + cancelledAt: Date; +}; diff --git a/src/schema/webhooks.ts b/src/schema/webhooks.ts index 01370cfdb..30035d09b 100644 --- a/src/schema/webhooks.ts +++ b/src/schema/webhooks.ts @@ -1,28 +1,5 @@ export enum WebhooksEventTypes { - QUEUED_TX = "queued_transaction", - SENT_TX = "sent_transaction", - MINED_TX = "mined_transaction", - ERRORED_TX = "errored_transaction", - CANCELLED_TX = "cancelled_transaction", - ALL_TX = "all_transactions", + ALL_TRANSACTIONS = "all_transactions", BACKEND_WALLET_BALANCE = "backend_wallet_balance", AUTH = "auth", } - -export interface SanitizedWebHooksSchema { - url: string; - name: string | null; - eventType: string; - secret?: string; - createdAt: string; - active: boolean; - id: number; -} - -export interface WalletBalanceWebhookSchema { - walletAddress: string; - minimumBalance: string; - currentBalance: string; - chainId: number; - message: string; -} diff --git a/src/scripts/sync-config-from-postgres.ts b/src/scripts/sync-config-from-postgres.ts new file mode 100644 index 000000000..3d683ba95 --- /dev/null +++ b/src/scripts/sync-config-from-postgres.ts @@ -0,0 +1,54 @@ +import { createHash } from "crypto"; +import { env } from "../utils/env"; + +// const engineConfigCacheKey = "engineConfig"; + +// export const initSyncConfigFromPostgres = async () => { +// logger({ +// level: "info", +// message: "Syncing config from Postgres to Redis", +// service: "cache", +// }); +// // Postgres DB Data +// const config = await getConfig(); +// let engineConfigCacheData = await redis.get(engineConfigCacheKey); + +// if (!engineConfigCacheData) { +// await redis.set(engineConfigCacheKey, JSON.stringify(config)); +// engineConfigCacheData = JSON.stringify(config); +// return; +// } + +// const isConfigDataChanged = compareData( +// config, +// JSON.parse(engineConfigCacheData), +// ); + +// if (isConfigDataChanged) { +// await redis.set(engineConfigCacheKey, JSON.stringify(config)); +// } +// }; + +// Function to generate a hash of your config data +const hashData = (data: string, salt: string): string => { + return createHash("sha256") + .update(data + salt) + .digest("hex"); +}; + +const compareData = (dataA: object, dataB: object): boolean => { + const redisCacheHash = hashData( + JSON.stringify(dataA), + env.THIRDWEB_API_SECRET_KEY, + ); + + const dbCacheHash = hashData( + JSON.stringify(dataB), + env.THIRDWEB_API_SECRET_KEY, + ); + + if (redisCacheHash !== dbCacheHash) { + return true; + } + return false; +}; diff --git a/src/server/middleware/auth.ts b/src/server/middleware/auth.ts index 4636e89bd..826c61483 100644 --- a/src/server/middleware/auth.ts +++ b/src/server/middleware/auth.ts @@ -17,7 +17,7 @@ import { WebhooksEventTypes } from "../../schema/webhooks"; import { getAccessToken } from "../../utils/cache/accessToken"; import { getAuthWallet } from "../../utils/cache/authWallet"; import { getConfig } from "../../utils/cache/getConfig"; -import { getWebhook } from "../../utils/cache/getWebhook"; +import { getWebhooksByType } from "../../utils/cache/getWebhook"; import { env } from "../../utils/env"; import { logger } from "../../utils/logger"; import { sendWebhookRequest } from "../../utils/webhook"; @@ -293,11 +293,11 @@ export const onRequest = async ({ // Auth via auth webhooks // Allow a request if it satisfies all configured auth webhooks. // Must have at least one auth webhook. - const authWebhooks = await getWebhook(WebhooksEventTypes.AUTH); + const authWebhooks = await getWebhooksByType(WebhooksEventTypes.AUTH); if (authWebhooks.length > 0) { const authResponses = await Promise.all( - authWebhooks.map((webhook) => - sendWebhookRequest(webhook, { + authWebhooks.map(async (webhook) => { + const { ok } = await sendWebhookRequest(webhook, { url: req.url, method: req.method, headers: req.headers, @@ -305,8 +305,9 @@ export const onRequest = async ({ query: req.query, cookies: req.cookies, body: req.body, - }), - ), + }); + return ok; + }), ); if (authResponses.every((ok) => !!ok)) { diff --git a/src/server/routes/backend-wallet/getTransactions.ts b/src/server/routes/backend-wallet/getTransactions.ts index aa3c25591..681bc95ce 100644 --- a/src/server/routes/backend-wallet/getTransactions.ts +++ b/src/server/routes/backend-wallet/getTransactions.ts @@ -3,7 +3,10 @@ import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { getAllTxsByWallet } from "../../../db/transactions/getAllTxsByWallet"; import { standardResponseSchema } from "../../schemas/sharedApiSchemas"; -import { transactionResponseSchema } from "../../schemas/transaction"; +import { + toTransactionResponse, + transactionResponseSchema, +} from "../../schemas/transaction"; import { walletParamSchema } from "../../schemas/wallet"; import { getChainIdFromChain } from "../../utils/chain"; @@ -40,7 +43,7 @@ export async function getAllTransactions(fastify: FastifyInstance) { res.status(StatusCodes.OK).send({ result: { - transactions, + transactions: transactions.map(toTransactionResponse), }, }); }, diff --git a/src/server/routes/backend-wallet/sendTransaction.ts b/src/server/routes/backend-wallet/sendTransaction.ts index 273fa8a6e..73f6863b6 100644 --- a/src/server/routes/backend-wallet/sendTransaction.ts +++ b/src/server/routes/backend-wallet/sendTransaction.ts @@ -32,7 +32,7 @@ requestBodySchema.examples = [ { toAddress: "0x7a0ce8524bea337f0bee853b68fabde145dac0a0", data: "0x449a52f800000000000000000000000043cae0d7fe86c713530e679ce02574743b2ee9fc0000000000000000000000000000000000000000000000000de0b6b3a7640000", - value: "0x00", + value: "0", }, ]; @@ -69,14 +69,16 @@ export async function sendTransaction(fastify: FastifyInstance) { } = request.headers as Static; const chainId = await getChainIdFromChain(chain); - const { id: queueId } = await queueTxRaw({ - chainId: chainId.toString(), - fromAddress, - toAddress, - data, - value, + const queueId = await queueTxRaw({ + tx: { + chainId: chainId.toString(), + fromAddress, + toAddress, + data, + value, + idempotencyKey, + }, simulateTx, - idempotencyKey, }); reply.status(StatusCodes.OK).send({ diff --git a/src/server/routes/backend-wallet/simulateTransaction.ts b/src/server/routes/backend-wallet/simulateTransaction.ts index 286fb5b87..e2b1def7c 100644 --- a/src/server/routes/backend-wallet/simulateTransaction.ts +++ b/src/server/routes/backend-wallet/simulateTransaction.ts @@ -1,14 +1,16 @@ import { Static, Type } from "@sinclair/typebox"; import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; +import { InputTransaction } from "../../../schema/transaction"; import { getContract } from "../../../utils/cache/getContract"; +import { createCustomError } from "../../middleware/error"; import { simulateResponseSchema, standardResponseSchema, } from "../../schemas/sharedApiSchemas"; import { walletHeaderSchema } from "../../schemas/wallet"; import { getChainIdFromChain } from "../../utils/chain"; -import { SimulateTxParams, simulateTx } from "../../utils/simulateTx"; +import { simulate } from "../../utils/simulateTx"; // INPUT const ParamsSchema = Type.Object({ @@ -75,7 +77,6 @@ export async function simulateTransaction(fastify: FastifyInstance) { }, }, handler: async (request, reply) => { - // Destruct core params const { chain } = request.params; const { toAddress, value, functionName, args, data } = request.body; const { @@ -84,36 +85,34 @@ export async function simulateTransaction(fastify: FastifyInstance) { } = request.headers as Static; const chainId = await getChainIdFromChain(chain); - // Get decoded tx simulate args - let simulateArgs: SimulateTxParams; if (functionName && args) { + // Simulate a function call to a contract. const contract = await getContract({ chainId, contractAddress: toAddress, walletAddress, accountAddress, }); - const tx = contract.prepare(functionName, args, { - value: value ?? "0", - }); - simulateArgs = { tx }; - } - // Get raw tx simulate args - else { - simulateArgs = { - txRaw: { - chainId: chainId.toString(), - fromAddress: walletAddress, - toAddress, - data, - value, - }, + const tx = contract.prepare(functionName, args, { value }); + await simulate({ tx }); + } else if (data) { + // Simulate from raw calldata. + const txRaw: InputTransaction = { + chainId: chainId.toString(), + fromAddress: walletAddress, + toAddress, + data, + value: value || "0", }; + await simulate({ txRaw }); + } else { + throw createCustomError( + "Missing params for simulation", + StatusCodes.BAD_REQUEST, + "INVALID_SIMULATION_PARAMS", + ); } - // Simulate raw tx - await simulateTx(simulateArgs); - // Return success reply.status(StatusCodes.OK).send({ result: { diff --git a/src/server/routes/backend-wallet/transfer.ts b/src/server/routes/backend-wallet/transfer.ts index d86bcf7eb..5a731092b 100644 --- a/src/server/routes/backend-wallet/transfer.ts +++ b/src/server/routes/backend-wallet/transfer.ts @@ -77,13 +77,12 @@ export async function transfer(fastify: FastifyInstance) { currencyAddress, ); - let queueId: string | null = null; + let queueId: string; if (isNativeToken(currencyAddress)) { const walletAddress = await sdk.getSigner()?.getAddress(); if (!walletAddress) throw new Error("No wallet address"); const balance = await sdk.getBalance(walletAddress); - if (balance.value.lt(normalizedValue)) { throw new Error("Insufficient balance"); } @@ -95,22 +94,24 @@ export async function transfer(fastify: FastifyInstance) { value: normalizedValue.toHexString(), }; - ({ id: queueId } = await queueTxRaw({ - chainId: chainId.toString(), - functionName: "transfer", - functionArgs: JSON.stringify([ - params.toAddress, - params.value, - params.currencyAddress, - ]), - extension: "none", - fromAddress: params.fromAddress, - toAddress: params.toAddress, - value: params.value, - data: "0x", + queueId = await queueTxRaw({ + tx: { + chainId: chainId.toString(), + functionName: "transfer", + functionArgs: JSON.stringify([ + params.toAddress, + params.value, + params.currencyAddress, + ]), + extension: "none", + fromAddress: params.fromAddress, + toAddress: params.toAddress, + value: params.value, + data: "0x", + idempotencyKey, + }, simulateTx, - idempotencyKey, - })); + }); } else { const contract = await getContract({ chainId, diff --git a/src/server/routes/backend-wallet/withdraw.ts b/src/server/routes/backend-wallet/withdraw.ts index 9529b6bcc..5dcf213de 100644 --- a/src/server/routes/backend-wallet/withdraw.ts +++ b/src/server/routes/backend-wallet/withdraw.ts @@ -48,14 +48,16 @@ export async function withdraw(fastify: FastifyInstance) { const chainId = await getChainIdFromChain(chain); - const { id: queueId } = await queueTxRaw({ - chainId: chainId.toString(), - extension: "withdraw", - functionName: "transfer", - fromAddress: walletAddress, - toAddress, - data: "0x", - idempotencyKey, + const queueId = await queueTxRaw({ + tx: { + chainId: chainId.toString(), + extension: "withdraw", + functionName: "transfer", + fromAddress: walletAddress, + toAddress, + data: "0x", + idempotencyKey, + }, }); reply.status(StatusCodes.OK).send({ diff --git a/src/server/routes/configuration/auth/update.ts b/src/server/routes/configuration/auth/update.ts index 294a81a67..835aa7807 100644 --- a/src/server/routes/configuration/auth/update.ts +++ b/src/server/routes/configuration/auth/update.ts @@ -2,7 +2,7 @@ import { Static, Type } from "@sinclair/typebox"; import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { updateConfiguration } from "../../../../db/configuration/updateConfiguration"; -import { getConfig } from "../../../../utils/cache/getConfig"; +import { clearConfigCache, getConfig } from "../../../../utils/cache/getConfig"; import { standardResponseSchema } from "../../../schemas/sharedApiSchemas"; import { ReplySchema } from "./get"; @@ -33,8 +33,8 @@ export async function updateAuthConfiguration(fastify: FastifyInstance) { authDomain: req.body.domain, }); - const config = await getConfig(false); - + await clearConfigCache(); + const config = await getConfig(); res.status(200).send({ result: { domain: config.authDomain, diff --git a/src/server/routes/configuration/backend-wallet-balance/update.ts b/src/server/routes/configuration/backend-wallet-balance/update.ts index f7af42343..85a0cf6b4 100644 --- a/src/server/routes/configuration/backend-wallet-balance/update.ts +++ b/src/server/routes/configuration/backend-wallet-balance/update.ts @@ -2,7 +2,7 @@ import { Static, Type } from "@sinclair/typebox"; import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { updateConfiguration } from "../../../../db/configuration/updateConfiguration"; -import { getConfig } from "../../../../utils/cache/getConfig"; +import { clearConfigCache, getConfig } from "../../../../utils/cache/getConfig"; import { standardResponseSchema } from "../../../schemas/sharedApiSchemas"; import { ReplySchema } from "./get"; @@ -35,8 +35,9 @@ export async function updateBackendWalletBalanceConfiguration( }, handler: async (req, res) => { await updateConfiguration({ ...req.body }); - const config = await getConfig(false); + await clearConfigCache(); + const config = await getConfig(); res.status(200).send({ result: { minWalletBalance: config.minWalletBalance, diff --git a/src/server/routes/configuration/cache/update.ts b/src/server/routes/configuration/cache/update.ts index a3476db80..e2cf5bc8e 100644 --- a/src/server/routes/configuration/cache/update.ts +++ b/src/server/routes/configuration/cache/update.ts @@ -2,7 +2,7 @@ import { Static, Type } from "@sinclair/typebox"; import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { updateConfiguration } from "../../../../db/configuration/updateConfiguration"; -import { getConfig } from "../../../../utils/cache/getConfig"; +import { clearConfigCache, getConfig } from "../../../../utils/cache/getConfig"; import { clearCacheCron } from "../../../../utils/cron/clearCacheCron"; import { isValidCron } from "../../../../utils/cron/isValidCron"; import { standardResponseSchema } from "../../../schemas/sharedApiSchemas"; @@ -46,7 +46,9 @@ export async function updateCacheConfiguration(fastify: FastifyInstance) { } await updateConfiguration({ ...req.body }); - const config = await getConfig(false); + + await clearConfigCache(); + const config = await getConfig(); // restarting cache cron with updated cron schedule await clearCacheCron("server"); res.status(200).send({ diff --git a/src/server/routes/configuration/chains/update.ts b/src/server/routes/configuration/chains/update.ts index d2aadca62..67e4555d9 100644 --- a/src/server/routes/configuration/chains/update.ts +++ b/src/server/routes/configuration/chains/update.ts @@ -2,7 +2,7 @@ import { Static, Type } from "@sinclair/typebox"; import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { updateConfiguration } from "../../../../db/configuration/updateConfiguration"; -import { getConfig } from "../../../../utils/cache/getConfig"; +import { clearConfigCache, getConfig } from "../../../../utils/cache/getConfig"; import { sdkCache } from "../../../../utils/cache/getSdk"; import { standardResponseSchema } from "../../../schemas/sharedApiSchemas"; import { ReplySchema } from "./get"; @@ -66,8 +66,9 @@ export async function updateChainsConfiguration(fastify: FastifyInstance) { chainOverrides: JSON.stringify(req.body.chainOverrides), }); - const config = await getConfig(false); sdkCache.clear(); + await clearConfigCache(); + const config = await getConfig(); res.status(200).send({ result: config.chainOverrides, }); diff --git a/src/server/routes/configuration/cors/add.ts b/src/server/routes/configuration/cors/add.ts index b004e5b57..991a79d15 100644 --- a/src/server/routes/configuration/cors/add.ts +++ b/src/server/routes/configuration/cors/add.ts @@ -2,7 +2,7 @@ import { Static, Type } from "@sinclair/typebox"; import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { updateConfiguration } from "../../../../db/configuration/updateConfiguration"; -import { getConfig } from "../../../../utils/cache/getConfig"; +import { clearConfigCache, getConfig } from "../../../../utils/cache/getConfig"; import { standardResponseSchema } from "../../../schemas/sharedApiSchemas"; import { mandatoryAllowedCorsUrls } from "../../../utils/cors-urls"; import { ReplySchema } from "./get"; @@ -62,8 +62,8 @@ export async function addUrlToCorsConfiguration(fastify: FastifyInstance) { ].join(","), }); - // Fetch and return the updated configuration - const config = await getConfig(false); + await clearConfigCache(); + const config = await getConfig(); res.status(200).send({ result: config.accessControlAllowOrigin.split(","), }); diff --git a/src/server/routes/configuration/cors/get.ts b/src/server/routes/configuration/cors/get.ts index f9ad642d0..da7d4ecb1 100644 --- a/src/server/routes/configuration/cors/get.ts +++ b/src/server/routes/configuration/cors/get.ts @@ -26,7 +26,7 @@ export async function getCorsConfiguration(fastify: FastifyInstance) { }, }, handler: async (req, res) => { - const config = await getConfig(false); + const config = await getConfig(); // Omit required domains. const omitted = config.accessControlAllowOrigin diff --git a/src/server/routes/configuration/cors/remove.ts b/src/server/routes/configuration/cors/remove.ts index 662c439eb..ce2d672d5 100644 --- a/src/server/routes/configuration/cors/remove.ts +++ b/src/server/routes/configuration/cors/remove.ts @@ -2,7 +2,7 @@ import { Static, Type } from "@sinclair/typebox"; import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { updateConfiguration } from "../../../../db/configuration/updateConfiguration"; -import { getConfig } from "../../../../utils/cache/getConfig"; +import { clearConfigCache, getConfig } from "../../../../utils/cache/getConfig"; import { standardResponseSchema } from "../../../schemas/sharedApiSchemas"; import { mandatoryAllowedCorsUrls } from "../../../utils/cors-urls"; import { ReplySchema } from "./get"; @@ -66,8 +66,8 @@ export async function removeUrlToCorsConfiguration(fastify: FastifyInstance) { ), }); - // Fetch and return the updated configuration - const newConfig = await getConfig(false); + await clearConfigCache(); + const newConfig = await getConfig(); res .status(200) .send({ result: newConfig.accessControlAllowOrigin.split(",") }); diff --git a/src/server/routes/configuration/cors/set.ts b/src/server/routes/configuration/cors/set.ts index a119e4ce8..0dd3d9f8f 100644 --- a/src/server/routes/configuration/cors/set.ts +++ b/src/server/routes/configuration/cors/set.ts @@ -2,7 +2,7 @@ import { Static, Type } from "@sinclair/typebox"; import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { updateConfiguration } from "../../../../db/configuration/updateConfiguration"; -import { getConfig } from "../../../../utils/cache/getConfig"; +import { clearConfigCache, getConfig } from "../../../../utils/cache/getConfig"; import { standardResponseSchema } from "../../../schemas/sharedApiSchemas"; import { mandatoryAllowedCorsUrls } from "../../../utils/cors-urls"; import { ReplySchema } from "./get"; @@ -53,8 +53,8 @@ export async function setUrlsToCorsConfiguration(fastify: FastifyInstance) { accessControlAllowOrigin: dedupe.join(","), }); - // Fetch and return the updated configuration - const config = await getConfig(false); + await clearConfigCache(); + const config = await getConfig(); res.status(200).send({ result: config.accessControlAllowOrigin.split(","), }); diff --git a/src/server/routes/configuration/events/update.ts b/src/server/routes/configuration/events/update.ts index 0aabc519f..5329fcfe8 100644 --- a/src/server/routes/configuration/events/update.ts +++ b/src/server/routes/configuration/events/update.ts @@ -2,7 +2,7 @@ import { Static, Type } from "@sinclair/typebox"; import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { updateConfiguration } from "../../../../db/configuration/updateConfiguration"; -import { getConfig } from "../../../../utils/cache/getConfig"; +import { clearConfigCache, getConfig } from "../../../../utils/cache/getConfig"; import { createCustomError } from "../../../middleware/error"; import { standardResponseSchema } from "../../../schemas/sharedApiSchemas"; import { ReplySchema } from "./get"; @@ -39,8 +39,9 @@ export async function updateEventsConfiguration(fastify: FastifyInstance) { } await updateConfiguration({ maxBlocksToIndex }); - const config = await getConfig(false); + await clearConfigCache(); + const config = await getConfig(); res.status(200).send({ result: { maxBlocksToIndex: config.maxBlocksToIndex, diff --git a/src/server/routes/configuration/transactions/update.ts b/src/server/routes/configuration/transactions/update.ts index d1bf0411c..cb58036b7 100644 --- a/src/server/routes/configuration/transactions/update.ts +++ b/src/server/routes/configuration/transactions/update.ts @@ -2,7 +2,7 @@ import { Static, Type } from "@sinclair/typebox"; import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { updateConfiguration } from "../../../../db/configuration/updateConfiguration"; -import { getConfig } from "../../../../utils/cache/getConfig"; +import { clearConfigCache, getConfig } from "../../../../utils/cache/getConfig"; import { standardResponseSchema } from "../../../schemas/sharedApiSchemas"; const BodySchema = Type.Partial( @@ -52,7 +52,8 @@ export async function updateTransactionConfiguration(fastify: FastifyInstance) { }, handler: async (req, res) => { await updateConfiguration({ ...req.body }); - const config = await getConfig(false); + await clearConfigCache(); + const config = await getConfig(); res.status(200).send({ result: { minTxsToProcess: config.minTxsToProcess, diff --git a/src/server/routes/configuration/wallets/update.ts b/src/server/routes/configuration/wallets/update.ts index 7b2a58cf7..9e68f5a6e 100644 --- a/src/server/routes/configuration/wallets/update.ts +++ b/src/server/routes/configuration/wallets/update.ts @@ -3,7 +3,7 @@ import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { updateConfiguration } from "../../../../db/configuration/updateConfiguration"; import { WalletType } from "../../../../schema/wallet"; -import { getConfig } from "../../../../utils/cache/getConfig"; +import { clearConfigCache, getConfig } from "../../../../utils/cache/getConfig"; import { standardResponseSchema } from "../../../schemas/sharedApiSchemas"; import { ReplySchema } from "./get"; @@ -126,7 +126,8 @@ export async function updateWalletsConfiguration(fastify: FastifyInstance) { break; } - const config = await getConfig(false); + await clearConfigCache(); + const config = await getConfig(); res.status(200).send({ result: config.walletConfiguration, }); diff --git a/src/server/routes/relayer/index.ts b/src/server/routes/relayer/index.ts index ced08b93e..9d3a37b2a 100644 --- a/src/server/routes/relayer/index.ts +++ b/src/server/routes/relayer/index.ts @@ -136,7 +136,7 @@ export async function relayTransaction(fastify: FastifyInstance) { NativeMetaTransaction, ); - const tx = await target.prepare("executeMetaTransaction", [ + const tx = target.prepare("executeMetaTransaction", [ request.from, request.data, r, @@ -178,7 +178,7 @@ export async function relayTransaction(fastify: FastifyInstance) { ERC20PermitAbi, ); - const tx = await target.prepare("permit", [ + const tx = target.prepare("permit", [ request.owner, request.spender, request.value, @@ -264,10 +264,10 @@ export async function relayTransaction(fastify: FastifyInstance) { forwarderAbi, ); - const valid = await forwarder.call("verify", [ - request, - ethers.utils.joinSignature(ethers.utils.splitSignature(signature)), - ]); + const fixedSignature = ethers.utils.joinSignature( + ethers.utils.splitSignature(signature), + ); + const valid = await forwarder.call("verify", [request, fixedSignature]); if (!valid) { res.status(400).send({ @@ -278,7 +278,7 @@ export async function relayTransaction(fastify: FastifyInstance) { return; } - const tx = await forwarder.prepare("execute", [request, signature]); + const tx = forwarder.prepare("execute", [request, fixedSignature]); const queueId = await queueTx({ tx, chainId: relayer.chainId, diff --git a/src/server/routes/transaction/getAll.ts b/src/server/routes/transaction/getAll.ts index f9728febd..8301cd12b 100644 --- a/src/server/routes/transaction/getAll.ts +++ b/src/server/routes/transaction/getAll.ts @@ -4,7 +4,10 @@ import { StatusCodes } from "http-status-codes"; import { getAllTxs } from "../../../db/transactions/getAllTxs"; import { createCustomError } from "../../middleware/error"; import { standardResponseSchema } from "../../schemas/sharedApiSchemas"; -import { transactionResponseSchema } from "../../schemas/transaction"; +import { + toTransactionResponse, + transactionResponseSchema, +} from "../../schemas/transaction"; // INPUT const requestQuerySchema = Type.Object({ @@ -48,7 +51,7 @@ responseBodySchema.example = { toAddress: "0x365b83d67d5539c6583b9c0266a548926bf216f4", data: "0xa9059cbb0000000000000000000000003ecdbf3b911d0e9052b64850693888b008e183730000000000000000000000000000000000000000000000000000000000000064", extension: "none", - value: "0x00", + value: "0", nonce: 1758, gasLimit: "39580", gasPrice: "2008818932", @@ -127,7 +130,7 @@ export async function getAllTx(fastify: FastifyInstance) { throw customError; } - const trasactionsData = await getAllTxs({ + const { transactions, totalCount } = await getAllTxs({ page: parseInt(page, 10), limit: parseInt(limit, 10), // filter: filter && filter !== "all" ? filter : undefined, // TODO: To bring this back for Paid feature @@ -135,8 +138,8 @@ export async function getAllTx(fastify: FastifyInstance) { reply.status(StatusCodes.OK).send({ result: { - transactions: trasactionsData.transactions, - totalCount: trasactionsData.totalCount, + transactions: transactions.map(toTransactionResponse), + totalCount, }, }); }, diff --git a/src/server/routes/transaction/getAllDeployedContracts.ts b/src/server/routes/transaction/getAllDeployedContracts.ts index accce1197..224c3e061 100644 --- a/src/server/routes/transaction/getAllDeployedContracts.ts +++ b/src/server/routes/transaction/getAllDeployedContracts.ts @@ -4,7 +4,10 @@ import { StatusCodes } from "http-status-codes"; import { getAllTxs } from "../../../db/transactions/getAllTxs"; import { createCustomError } from "../../middleware/error"; import { standardResponseSchema } from "../../schemas/sharedApiSchemas"; -import { transactionResponseSchema } from "../../schemas/transaction"; +import { + toTransactionResponse, + transactionResponseSchema, +} from "../../schemas/transaction"; // INPUT const requestQuerySchema = Type.Object({ @@ -105,7 +108,7 @@ export async function getAllDeployedContracts(fastify: FastifyInstance) { ); throw customError; } - const txsData = await getAllTxs({ + const { transactions, totalCount } = await getAllTxs({ page: parseInt(page, 10), limit: parseInt(limit, 10), // filter: filter && filter !== "all" ? filter : undefined, @@ -114,8 +117,8 @@ export async function getAllDeployedContracts(fastify: FastifyInstance) { reply.status(StatusCodes.OK).send({ result: { - transactions: txsData.transactions, - totalCount: txsData.totalCount, + transactions: transactions.map(toTransactionResponse), + totalCount, }, }); }, diff --git a/src/server/routes/transaction/group.ts b/src/server/routes/transaction/group.ts index 4a0f7ddba..a48c1efdd 100644 --- a/src/server/routes/transaction/group.ts +++ b/src/server/routes/transaction/group.ts @@ -3,7 +3,10 @@ import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { getTxsByGroupId } from "../../../db/transactions/getTxsByGroupId"; import { standardResponseSchema } from "../../schemas/sharedApiSchemas"; -import { transactionResponseSchema } from "../../schemas/transaction"; +import { + toTransactionResponse, + transactionResponseSchema, +} from "../../schemas/transaction"; const ParamsSchema = Type.Object({ groupId: Type.String(), @@ -36,7 +39,7 @@ export async function checkGroupStatus(fastify: FastifyInstance) { const txs = await getTxsByGroupId({ groupId }); res.status(StatusCodes.OK).send({ - result: txs, + result: txs.map(toTransactionResponse), }); }, }); diff --git a/src/server/routes/transaction/syncRetry.ts b/src/server/routes/transaction/syncRetry.ts index f65f6b797..96c1e826a 100644 --- a/src/server/routes/transaction/syncRetry.ts +++ b/src/server/routes/transaction/syncRetry.ts @@ -7,7 +7,7 @@ import { updateTx } from "../../../db/transactions/updateTx"; import { getSdk } from "../../../utils/cache/getSdk"; import { msSince } from "../../../utils/date"; import { parseTxError } from "../../../utils/errors"; -import { UsageEventTxActionEnum, reportUsage } from "../../../utils/usage"; +import { UsageEventType, reportUsage } from "../../../utils/usage"; import { createCustomError } from "../../middleware/error"; import { standardResponseSchema } from "../../schemas/sharedApiSchemas"; import { TransactionStatus } from "../../schemas/transaction"; @@ -150,8 +150,8 @@ export async function syncRetryTransaction(fastify: FastifyInstance) { }); reportUsage([ { - action: UsageEventTxActionEnum.SendTx, - input: { + action: UsageEventType.SendTx, + data: { fromAddress: tx.fromAddress, toAddress: tx.toAddress, value: tx.value, diff --git a/src/server/routes/webhooks/create.ts b/src/server/routes/webhooks/create.ts index 3cd1784b6..ec7858c80 100644 --- a/src/server/routes/webhooks/create.ts +++ b/src/server/routes/webhooks/create.ts @@ -2,10 +2,14 @@ import { Static, Type } from "@sinclair/typebox"; import { TypeSystem } from "@sinclair/typebox/system"; import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; -import { insertWebhook } from "../../../db/webhooks/createWebhook"; +import { insertWebhook } from "../../../db/webhooks/insertWebhook"; import { WebhooksEventTypes } from "../../../schema/webhooks"; import { isLocalhost } from "../../../utils/url"; import { standardResponseSchema } from "../../schemas/sharedApiSchemas"; +import { + WebhookResponseSchema, + toWebhookResponse, +} from "../../schemas/webhooks"; const uriFormat = TypeSystem.Format("uri", (input: string) => { // Assert valid URL. @@ -35,55 +39,23 @@ const BodySchema = Type.Object({ BodySchema.examples = [ { url: "https://example.com/allTxUpdate", - name: "All Transaction Events", - eventType: WebhooksEventTypes.ALL_TX, - }, - { - url: "https://example.com/queuedTx", - name: "QueuedTx", - eventType: WebhooksEventTypes.QUEUED_TX, - }, - { - url: "https://example.com/sentTx", - name: "Sent Transaction Event", - eventType: WebhooksEventTypes.SENT_TX, - }, - { - url: "https://example.com/minedTx", - name: "Mined Transaction Event", - eventType: WebhooksEventTypes.MINED_TX, - }, - { - url: "https://example.com/erroredTx", - name: "Errored Transaction Event", - eventType: WebhooksEventTypes.ERRORED_TX, - }, - { - url: "https://example.com/cancelledTx", - name: "Cancelled Transaction Event", - eventType: WebhooksEventTypes.CANCELLED_TX, + name: "All transaction events", + eventType: WebhooksEventTypes.ALL_TRANSACTIONS, }, { url: "https://example.com/walletBalance", - name: "Backend Wallet Balance Event", + name: "Backend wallet balance event", eventType: WebhooksEventTypes.BACKEND_WALLET_BALANCE, }, { url: "https://example.com/auth", - name: "Auth Check", + name: "Authentication", eventType: WebhooksEventTypes.AUTH, }, ]; const ReplySchema = Type.Object({ - result: Type.Object({ - url: Type.String(), - name: Type.String(), - createdAt: Type.String(), - eventType: Type.String(), - secret: Type.Optional(Type.String()), - id: Type.Number(), - }), + result: WebhookResponseSchema, }); export async function createWebhook(fastify: FastifyInstance) { @@ -105,11 +77,11 @@ export async function createWebhook(fastify: FastifyInstance) { }, }, handler: async (req, res) => { - const config = await insertWebhook({ ...req.body }); + const { url, name, eventType } = req.body; + const webhook = await insertWebhook({ url, name, eventType }); + res.status(200).send({ - result: { - ...config, - }, + result: toWebhookResponse(webhook), }); }, }); diff --git a/src/server/routes/webhooks/events.ts b/src/server/routes/webhooks/events.ts index 6257b2247..1ea5a1297 100644 --- a/src/server/routes/webhooks/events.ts +++ b/src/server/routes/webhooks/events.ts @@ -26,6 +26,7 @@ export async function getWebhooksEventTypes(fastify: FastifyInstance) { }, handler: async (req, res) => { const eventTypesArray = Object.values(WebhooksEventTypes); + res.status(200).send({ result: eventTypesArray, }); diff --git a/src/server/routes/webhooks/getAll.ts b/src/server/routes/webhooks/getAll.ts index d5ff63d68..701363e9f 100644 --- a/src/server/routes/webhooks/getAll.ts +++ b/src/server/routes/webhooks/getAll.ts @@ -3,19 +3,13 @@ import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { getAllWebhooks } from "../../../db/webhooks/getAllWebhooks"; import { standardResponseSchema } from "../../schemas/sharedApiSchemas"; +import { + WebhookResponseSchema, + toWebhookResponse, +} from "../../schemas/webhooks"; const ReplySchema = Type.Object({ - result: Type.Array( - Type.Object({ - url: Type.String(), - name: Type.Union([Type.String(), Type.Null()]), - secret: Type.Optional(Type.String()), - eventType: Type.String(), - active: Type.Boolean(), - createdAt: Type.String(), - id: Type.Number(), - }), - ), + result: Type.Array(WebhookResponseSchema), }); export async function getAllWebhooksData(fastify: FastifyInstance) { @@ -35,9 +29,10 @@ export async function getAllWebhooksData(fastify: FastifyInstance) { }, }, handler: async (req, res) => { - const webhooksData = await getAllWebhooks(); + const allWebhooks = await getAllWebhooks(); + res.status(200).send({ - result: webhooksData, + result: allWebhooks.map(toWebhookResponse), }); }, }); diff --git a/src/server/routes/webhooks/revoke.ts b/src/server/routes/webhooks/revoke.ts index a94bb87ef..c7cd4db0f 100644 --- a/src/server/routes/webhooks/revoke.ts +++ b/src/server/routes/webhooks/revoke.ts @@ -1,7 +1,7 @@ import { Static, Type } from "@sinclair/typebox"; import { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; -import { markWebhookAsRevoked } from "../../../db/webhooks/revokeWebhook"; +import { softDeleteWebhook } from "../../../db/webhooks/deleteWebhook"; import { standardResponseSchema } from "../../schemas/sharedApiSchemas"; const BodySchema = Type.Object({ @@ -34,9 +34,9 @@ export async function revokeWebhook(fastify: FastifyInstance) { }, handler: async (req, res) => { const { id } = req.body; - await markWebhookAsRevoked({ - id, - }); + + await softDeleteWebhook(id); + res.status(200).send({ result: { success: true, diff --git a/src/server/schemas/transaction/index.ts b/src/server/schemas/transaction/index.ts index 88f363891..9d74a0bce 100644 --- a/src/server/schemas/transaction/index.ts +++ b/src/server/schemas/transaction/index.ts @@ -1,4 +1,5 @@ -import { Type } from "@sinclair/typebox"; +import { Transactions } from "@prisma/client"; +import { Static, Type } from "@sinclair/typebox"; export const transactionResponseSchema = Type.Object({ queueId: Type.Union([ @@ -199,40 +200,22 @@ export enum TransactionStatus { Cancelled = "cancelled", } -export interface TransactionSchema { - identifier?: string; - walletAddress?: string; - contractAddress?: string; - chainId?: string; - extension?: string; - rawFunctionName?: string; - rawFunctionArgs?: string; - txProcessed?: boolean; - txSubmitted?: boolean; - txErrored?: boolean; - txMined?: boolean; - encodedInputData?: string; - txType?: number; - gasPrice?: string; - gasLimit?: string; - maxPriorityFeePerGas?: string; - maxFeePerGas?: string; - txHash?: string; - status?: string; - createdTimestamp?: Date; - txSubmittedTimestamp?: Date; - txProcessedTimestamp?: Date; - submittedTxNonce?: number; - deployedContractAddress?: string; - contractType?: string; - txValue?: string; - errorMessage?: string; - txMinedTimestamp?: Date; - blockNumber?: number; - toAddress?: string; - txSubmittedAtBlockNumber?: number; - numberOfRetries?: number; - overrideGasValuesForTx?: boolean; - overrideMaxFeePerGas?: string; - overrideMaxPriorityFeePerGas?: string; -} +export const toTransactionResponse = ( + raw: Transactions, +): Static => ({ + ...raw, + queueId: raw.id, + queuedAt: raw.queuedAt.toISOString(), + sentAt: raw.sentAt?.toISOString() || null, + minedAt: raw.minedAt?.toISOString() || null, + cancelledAt: raw.cancelledAt?.toISOString() || null, + status: raw.errorMessage + ? "errored" + : raw.minedAt + ? "mined" + : raw.cancelledAt + ? "cancelled" + : raw.sentAt + ? "sent" + : "queued", +}); diff --git a/src/server/schemas/webhooks.ts b/src/server/schemas/webhooks.ts new file mode 100644 index 000000000..2d659ad58 --- /dev/null +++ b/src/server/schemas/webhooks.ts @@ -0,0 +1,21 @@ +import { Webhooks } from "@prisma/client"; +import { Static, Type } from "@sinclair/typebox"; + +export const WebhookResponseSchema = Type.Object({ + id: Type.String(), + url: Type.String(), + name: Type.Union([Type.String(), Type.Null()]), + secret: Type.String(), + eventType: Type.String(), + active: Type.Boolean(), + createdAt: Type.String(), +}); + +export const toWebhookResponse = ( + raw: Webhooks, +): Static => ({ + ...raw, + id: raw.id.toString(), + active: !raw.revokedAt, + createdAt: raw.createdAt.toISOString(), +}); diff --git a/src/server/utils/simulateTx.ts b/src/server/utils/simulateTx.ts index 1178f8ccc..d419d3b5a 100644 --- a/src/server/utils/simulateTx.ts +++ b/src/server/utils/simulateTx.ts @@ -1,49 +1,56 @@ +import { getChainByChainIdAsync } from "@thirdweb-dev/chains"; import { DeployTransaction, Transaction, TransactionError, } from "@thirdweb-dev/sdk"; -import { ethers } from "ethers"; -import { getSdk } from "../../utils/cache/getSdk"; +import { prepareTransaction, simulateTransaction } from "thirdweb"; +import { InputTransaction } from "../../schema/transaction"; +import { thirdwebClient } from "../../utils/sdk"; import { createCustomError } from "../middleware/error"; -type SimulateTxRawParams = { - chainId: string; - toAddress?: string | null; - fromAddress?: string | null; - data?: string | null; - value?: any; -}; +/** + * Simulates a transaction. + * @returns Error message if the simulation fails. Else null. + */ +export const simulateRaw = async (tx: InputTransaction) => { + if (!tx.toAddress || !tx.fromAddress) { + return; + } -const simulateTxRaw = async (args: SimulateTxRawParams) => { - const sdk = await getSdk({ chainId: parseInt(args.chainId) }); - const simulateResult = await sdk.getProvider().call({ - to: `${args.toAddress}`, - from: `${args.fromAddress}`, - data: `${args.data}`, - value: `${args.value}`, - }); - if (simulateResult.length > 2) { - // '0x' is the success result value - const decoded = ethers.utils.defaultAbiCoder.decode( - ["string"], - ethers.utils.hexDataSlice(simulateResult, 4), - ); - throw new Error(decoded[0]); + try { + const chain = await getChainByChainIdAsync(Number(tx.chainId)); + const transaction = prepareTransaction({ + to: tx.toAddress, + value: tx.value ? BigInt(tx.value) : undefined, + data: tx.data as `0x${string}`, + chain: { + id: Number(tx.chainId), + rpc: chain.rpc[0], + }, + client: thirdwebClient, + }); + + await simulateTransaction({ + transaction, + from: tx.fromAddress, + }); + } catch (e: any) { + throw e?.message || e.toString(); } }; -export type SimulateTxParams = { +interface SimulateParams { tx?: Transaction | DeployTransaction; - txRaw?: SimulateTxRawParams; -}; + txRaw?: InputTransaction; +} -export const simulateTx = async ({ tx, txRaw }: SimulateTxParams) => { +export const simulate = async ({ tx, txRaw }: SimulateParams) => { try { if (tx) { await tx.simulate(); } else if (txRaw) { - await simulateTxRaw(txRaw); + await simulateRaw(txRaw); } else { throw new Error("No transaction to simulate"); } diff --git a/src/server/utils/transaction.ts b/src/server/utils/transaction.ts index 1db9e3395..1ac7377da 100644 --- a/src/server/utils/transaction.ts +++ b/src/server/utils/transaction.ts @@ -117,7 +117,7 @@ export const cancelTransactionAndUpdate = async ({ to: txData.fromAddress!, from: txData.fromAddress!, data: "0x", - value: "0x00", + value: "0", nonce: txData.nonce!, ...multiplyGasOverrides(gasOverrides, 2), }); diff --git a/src/utils/cache/clearCache.ts b/src/utils/cache/clearCache.ts index ea22928cf..0ad8fb1bc 100644 --- a/src/utils/cache/clearCache.ts +++ b/src/utils/cache/clearCache.ts @@ -1,16 +1,12 @@ import { env } from "../env"; import { accessTokenCache } from "./accessToken"; -import { configCache } from "./getConfig"; +import { clearConfigCache } from "./getConfig"; import { sdkCache } from "./getSdk"; -import { walletsCache } from "./getWallet"; -import { webhookCache } from "./getWebhook"; export const clearCache = async ( service: (typeof env)["LOG_SERVICES"][0], ): Promise => { - configCache.clear(); - webhookCache.clear(); + await clearConfigCache(); sdkCache.clear(); - walletsCache.clear(); accessTokenCache.clear(); }; diff --git a/src/utils/cache/getConfig.ts b/src/utils/cache/getConfig.ts index 2f2f069a6..2224454cf 100644 --- a/src/utils/cache/getConfig.ts +++ b/src/utils/cache/getConfig.ts @@ -1,8 +1,13 @@ import { Configuration } from "@prisma/client"; import { getConfiguration } from "../../db/configuration/getConfiguration"; import { WalletType } from "../../schema/wallet"; +import { + cacheKeyConfiguration, + getCache, + invalidateCache, + setCache, +} from "../redis/cache"; -const cacheKey = "config"; interface Config extends Omit< Configuration, @@ -35,23 +40,19 @@ interface Config }; } -export const configCache = new Map(); - -export const getConfig = async (retrieveFromCache = true): Promise => { - if ( - configCache.has(cacheKey) && - configCache.get(cacheKey) && - retrieveFromCache - ) { - const config = configCache.get(cacheKey) as Config; - - if (config.authDomain && config.authWalletEncryptedJson) { - return config; - } +export const getConfig = async (): Promise => { + const key = cacheKeyConfiguration(); + const cached = await getCache(key); + if (cached) { + return cached; } - const configData = await getConfiguration(); + const config = await getConfiguration(); + await setCache(key, config); + return config; +}; - configCache.set(cacheKey, configData); - return configData; +export const clearConfigCache = async (): Promise => { + const key = cacheKeyConfiguration(); + await invalidateCache(key); }; diff --git a/src/utils/cache/getContractV5.ts b/src/utils/cache/getContractV5.ts new file mode 100644 index 000000000..325ef97fa --- /dev/null +++ b/src/utils/cache/getContractV5.ts @@ -0,0 +1,34 @@ +import { ContractOptions, defineChain, getContract } from "thirdweb"; +import { thirdwebClient } from "../sdk"; + +interface GetContractParams { + chainId: number; + contractAddress: string; +} + +// export const definedChainCache = new Map(); // This is a cache +export const contractCache = new Map>(); // This is a cache + +// Using new v5 SDK +export const getContractV5 = ({ + chainId, + contractAddress, +}: GetContractParams) => { + const definedChain = defineChain(chainId); + const cacheKey = `${definedChain.id}-${contractAddress}`; + const cachedContractData = contractCache.get(cacheKey); + + if (cachedContractData) { + return cachedContractData; + } + + // get a contract + return getContract({ + // the client you have created via `createThirdwebClient()` + client: thirdwebClient, + // the contract's address + address: contractAddress, + // the chain the contract is deployed on + chain: definedChain, + }); +}; diff --git a/src/utils/cache/getSdk.ts b/src/utils/cache/getSdk.ts index 14b02e4a7..272142644 100644 --- a/src/utils/cache/getSdk.ts +++ b/src/utils/cache/getSdk.ts @@ -119,7 +119,6 @@ export const getSdk = async ({ }); } else { const wallet = await getWallet({ - pgtx, chainId, walletAddress, accountAddress, diff --git a/src/utils/cache/getWallet.ts b/src/utils/cache/getWallet.ts index 778a8aa24..af43cf0c8 100644 --- a/src/utils/cache/getWallet.ts +++ b/src/utils/cache/getWallet.ts @@ -1,39 +1,33 @@ import { EVMWallet } from "@thirdweb-dev/wallets"; import { getWalletDetails } from "../../db/wallets/getWalletDetails"; -import { PrismaTransaction } from "../../schema/prisma"; 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 } from "../../server/utils/wallets/getLocalWallet"; import { getSmartWallet } from "../../server/utils/wallets/getSmartWallet"; - -export const walletsCache = new Map(); +import { cacheKeyWallet, getCache, setCache } from "../redis/cache"; interface GetWalletParams { - pgtx?: PrismaTransaction; chainId: number; walletAddress: string; accountAddress?: string; } export const getWallet = async ({ - pgtx, chainId, walletAddress, accountAddress, }: GetWalletParams): Promise => { - const cacheKey = accountAddress - ? `${chainId}-${walletAddress}-${accountAddress}` - : `${chainId}-${walletAddress}`; - if (walletsCache.has(cacheKey)) { - return walletsCache.get(cacheKey)! as TWallet; + const key = cacheKeyWallet(chainId, walletAddress, accountAddress); + const cached = await getCache(key); + if (cached) { + return cached; } const walletDetails = await getWalletDetails({ - pgtx, address: walletAddress, }); - if (!walletDetails) { throw new Error(`No configured wallet found with address ${walletAddress}`); } @@ -55,14 +49,16 @@ export const getWallet = async ({ wallet = await getLocalWallet({ chainId, walletAddress }); break; default: - throw new Error( + throw createCustomError( `Wallet with address ${walletAddress} was configured with unknown wallet type ${walletDetails.type}`, + 400, + "BAD_REQUEST", ); } if (!accountAddress) { // If no account is specified, use the backend wallet itself - walletsCache.set(cacheKey, wallet); + await setCache(key, wallet); return wallet as TWallet; } @@ -73,6 +69,6 @@ export const getWallet = async ({ accountAddress: accountAddress, }); - walletsCache.set(cacheKey, smartWallet); + await setCache(key, smartWallet); return smartWallet as TWallet; }; diff --git a/src/utils/cache/getWebhook.ts b/src/utils/cache/getWebhook.ts index 5995390b5..035d9d5f4 100644 --- a/src/utils/cache/getWebhook.ts +++ b/src/utils/cache/getWebhook.ts @@ -1,29 +1,12 @@ +import { Webhooks } from "@prisma/client"; import { getAllWebhooks } from "../../db/webhooks/getAllWebhooks"; -import { - SanitizedWebHooksSchema, - WebhooksEventTypes, -} from "../../schema/webhooks"; - -export const webhookCache = new Map(); - -export const getWebhook = async ( - eventType: WebhooksEventTypes, - retrieveFromCache = true, -): Promise => { - const cacheKey = eventType; - - if (retrieveFromCache && webhookCache.has(cacheKey)) { - return webhookCache.get(cacheKey) as SanitizedWebHooksSchema[]; - } - - const webhookConfig = await getAllWebhooks(); - - const eventTypeWebhookDetails = webhookConfig.filter((webhook) => { - if (webhook.active && webhook.eventType === eventType) { - return webhook; - } - }); - - webhookCache.set(cacheKey, eventTypeWebhookDetails); - return eventTypeWebhookDetails; +import { WebhooksEventTypes } from "../../schema/webhooks"; + +export const getWebhooksByType = async ( + type: WebhooksEventTypes, +): Promise => { + const allWebhooks = await getAllWebhooks(); + return allWebhooks.filter( + (webhook) => !webhook.revokedAt && webhook.eventType === type, + ); }; diff --git a/src/utils/env.ts b/src/utils/env.ts index 05e538c5d..fd3e5b138 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -52,7 +52,7 @@ export const env = createEnv({ .default("server,worker,cache,websocket") .transform((s) => z - .array(z.enum(["server", "worker", "cache", "websocket"])) + .array(z.enum(["server", "worker", "cache", "websocket", "database"])) .parse(s.split(",")), ), THIRDWEB_API_SECRET_KEY: z.string().min(1), @@ -73,6 +73,8 @@ export const env = createEnv({ .default("https://c.thirdweb.com/event"), SDK_BATCH_TIME_LIMIT: z.coerce.number().default(0), SDK_BATCH_SIZE_LIMIT: z.coerce.number().default(100), + REDIS_URL: z.string().default("redis://localhost:6379"), + INGEST_WORKER_CONCURRENCY: z.coerce.number().default(20), }, clientPrefix: "NEVER_USED", client: {}, @@ -93,6 +95,8 @@ export const env = createEnv({ CLIENT_ANALYTICS_URL: process.env.CLIENT_ANALYTICS_URL, SDK_BATCH_TIME_LIMIT: process.env.SDK_BATCH_TIME_LIMIT, SDK_BATCH_SIZE_LIMIT: process.env.SDK_BATCH_SIZE_LIMIT, + REDIS_URL: process.env.REDIS_URL, + INGEST_WORKER_CONCURRENCY: process.env.INGEST_WORKER_CONCURRENCY, }, onValidationError: (error: ZodError) => { console.error( diff --git a/src/utils/errors.ts b/src/utils/errors.ts index e5243e1f4..498dc7d16 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -1,12 +1,7 @@ import { Transactions } from ".prisma/client"; import { getChainByChainIdAsync } from "@thirdweb-dev/chains"; import { ethers } from "ethers"; -import { - createThirdwebClient, - prepareTransaction, - simulateTransaction, -} from "thirdweb"; -import { env } from "./env"; +import { simulateRaw } from "../server/utils/simulateTx"; interface EthersError { reason: string; @@ -16,10 +11,6 @@ interface EthersError { transaction: any; } -const client = createThirdwebClient({ - secretKey: env.THIRDWEB_API_SECRET_KEY, -}); - export const parseTxError = async ( tx: Transactions, err: any, @@ -29,29 +20,22 @@ export const parseTxError = async ( } // EOA transactions - if (tx.toAddress && tx.fromAddress && tx.data) { + if ((err as EthersError)?.code === ethers.errors.INSUFFICIENT_FUNDS) { const chain = await getChainByChainIdAsync(Number(tx.chainId)); + return `Insufficient ${chain.nativeCurrency?.symbol} on ${chain.name} in backend wallet ${tx.fromAddress}.`; + } - if ((err as EthersError)?.code === ethers.errors.INSUFFICIENT_FUNDS) { - return `Insufficient ${chain.nativeCurrency?.symbol} on ${chain.name} in backend wallet ${tx.fromAddress}.`; - } - - if ((err as EthersError)?.code === ethers.errors.UNPREDICTABLE_GAS_LIMIT) { - try { - const transaction = prepareTransaction({ - to: tx.toAddress, - value: BigInt(tx.value || "0"), - data: tx.data as `0x${string}`, - chain: { - id: Number(tx.chainId), - rpc: chain.rpc[0], - }, - client, - }); - await simulateTransaction({ transaction, from: tx.fromAddress }); - } catch (simErr: any) { - return simErr?.message ?? simErr.toString(); - } + if ((err as EthersError)?.code === ethers.errors.UNPREDICTABLE_GAS_LIMIT) { + try { + await simulateRaw({ + chainId: tx.chainId, + fromAddress: tx.fromAddress ?? undefined, + toAddress: tx.toAddress ?? undefined, + data: tx.data ?? "0x", + value: tx.value ?? undefined, + }); + } catch (simulationErr: any) { + return simulationErr; } } diff --git a/src/utils/redis/cache.ts b/src/utils/redis/cache.ts new file mode 100644 index 000000000..80e5b3f83 --- /dev/null +++ b/src/utils/redis/cache.ts @@ -0,0 +1,39 @@ +import superjson from "superjson"; +import { redis } from "./redis"; + +const DEFAULT_TTL_SECONDS = 60; + +export const getCache = async (key: string): Promise => { + const val = await redis.get(key); + return val ? superjson.parse(val) : null; +}; + +export const setCache = async ( + key: string, + val: any, + ttlSeconds = DEFAULT_TTL_SECONDS, +) => { + await redis.setex(key, ttlSeconds, superjson.stringify(val)); +}; + +export const invalidateCache = async (key: string) => await redis.del(key); + +/** + * Cache keys + */ + +export const cacheKeyWalletDetails = (address: string) => + `wallet_details:${address.toLowerCase()}`; + +export const cacheKeyAllWebhooks = () => "webhooks"; + +export const cacheKeyConfiguration = () => "configuration"; + +export const cacheKeyWallet = ( + chainId: number, + walletAddress: string, + accountAddress?: string, +) => + accountAddress + ? `${chainId}:${walletAddress}:${accountAddress}` + : `${chainId}:${walletAddress}`; diff --git a/src/utils/redis/redis.ts b/src/utils/redis/redis.ts new file mode 100644 index 000000000..2ae9b1262 --- /dev/null +++ b/src/utils/redis/redis.ts @@ -0,0 +1,36 @@ +import Redis from "ioredis"; +import { env } from "../env"; +import { logger } from "../logger"; + +export const redis = new Redis(env.REDIS_URL, { + maxRetriesPerRequest: null, +}); + +redis.on("error", (err) => () => { + logger({ + level: "error", + message: `Redis error: ${err}`, + service: "database", + }); +}); +redis.on("connect", () => + logger({ + level: "info", + message: "Redis connected", + service: "database", + }), +); +redis.on("reconnecting", () => + logger({ + level: "info", + message: "Redis reconnecting", + service: "database", + }), +); +redis.on("ready", () => { + logger({ + level: "info", + message: "Redis ready", + service: "database", + }); +}); diff --git a/src/utils/sdk.ts b/src/utils/sdk.ts index 496729665..5df095197 100644 --- a/src/utils/sdk.ts +++ b/src/utils/sdk.ts @@ -7,5 +7,11 @@ export const thirdwebClientId = sha256HexSync( ).slice(0, 32); export const thirdwebClient = createThirdwebClient({ - clientId: thirdwebClientId, + secretKey: env.THIRDWEB_API_SECRET_KEY, + config: { + rpc: { + maxBatchSize: env.SDK_BATCH_SIZE_LIMIT, + batchTimeoutMs: env.SDK_BATCH_TIME_LIMIT, + }, + }, }); diff --git a/src/utils/usage.ts b/src/utils/usage.ts index 09cae8916..ae4f81998 100644 --- a/src/utils/usage.ts +++ b/src/utils/usage.ts @@ -9,16 +9,10 @@ import { env } from "./env"; import { logger } from "./logger"; import { thirdwebClientId } from "./sdk"; -type CreateHeaderForRequestParams = { - clientId: string; - backendwalletAddress?: string; - chainId?: string; -}; - export interface ReportUsageParams { - action: UsageEventTxActionEnum; - input: { - chainId?: string; + action: UsageEventType; + data: { + chainId: string; fromAddress?: string; toAddress?: string; value?: string; @@ -40,7 +34,7 @@ const EngineRequestParams = Type.Object({ ...walletParamSchema.properties, }); -export enum UsageEventTxActionEnum { +export enum UsageEventType { MineTx = "mine_tx", NotSendTx = "not_send_tx", QueueTx = "queue_tx", @@ -50,10 +44,6 @@ export enum UsageEventTxActionEnum { ErrorTx = "error_tx", } -interface UsageEventSchema extends Omit { - action: UsageEventTxActionEnum; -} - const URLS_LIST_TO_NOT_REPORT_USAGE = new Set([ "/", "/favicon.ico", @@ -63,13 +53,11 @@ const URLS_LIST_TO_NOT_REPORT_USAGE = new Set([ "", ]); -const createHeaderForRequest = (input: CreateHeaderForRequestParams) => { - return { - "Content-Type": "application/json", - "x-sdk-version": process.env.ENGINE_VERSION, - "x-product-name": "engine", - "x-client-id": input.clientId, - } as HeadersInit; +const defaultHeaders: HeadersInit = { + "Content-Type": "application/json", + "x-sdk-version": process.env.ENGINE_VERSION ?? "", + "x-product-name": "engine", + "x-client-id": thirdwebClientId, }; export const withServerUsageReporting = (server: FastifyInstance) => { @@ -86,85 +74,73 @@ export const withServerUsageReporting = (server: FastifyInstance) => { return; } - const headers = createHeaderForRequest({ - clientId: thirdwebClientId, - }); - const requestParams = request?.params as Static; const chainId = requestParams?.chain ? await getChainIdFromChain(requestParams.chain) - : ""; + : undefined; - const requestBody: UsageEventSchema = { + const requestBody: UsageEvent = { source: "engine", - action: UsageEventTxActionEnum.APIRequest, + action: UsageEventType.APIRequest, clientId: thirdwebClientId, pathname: reply.request.routerPath, - chainId: chainId || undefined, - walletAddress: requestParams.walletAddress || undefined, - contractAddress: requestParams.contractAddress || undefined, + chainId, + walletAddress: requestParams.walletAddress, + contractAddress: requestParams.contractAddress, httpStatusCode: reply.statusCode, msTotalDuration: Math.ceil(reply.getResponseTime()), }; fetch(env.CLIENT_ANALYTICS_URL, { method: "POST", - headers, + headers: defaultHeaders, body: JSON.stringify(requestBody), }).catch(() => {}); // Catch uncaught exceptions since this fetch call is non-blocking. }); }; -export const reportUsage = (usageParams: ReportUsageParams[]) => { +export const reportUsage = (usageEvents: ReportUsageParams[]) => { // Skip reporting if CLIENT_ANALYTICS_URL is not set. if (env.CLIENT_ANALYTICS_URL === "") { return; } - usageParams.map(async (item) => { + usageEvents.map(async (event) => { try { - const headers = createHeaderForRequest({ - clientId: thirdwebClientId, - }); - - const chainId = item.input.chainId - ? parseInt(item.input.chainId) - : undefined; - const requestBody: UsageEventSchema = { + const requestBody: UsageEvent = { source: "engine", - action: item.action, + action: event.action, clientId: thirdwebClientId, - chainId, - walletAddress: item.input.fromAddress || undefined, - contractAddress: item.input.toAddress || undefined, - transactionValue: item.input.value || undefined, - transactionHash: item.input.transactionHash || undefined, - userOpHash: item.input.userOpHash || undefined, - errorCode: item.input.onChainTxStatus - ? item.input.onChainTxStatus === 0 + chainId: parseInt(event.data.chainId), + walletAddress: event.data.fromAddress, + contractAddress: event.data.toAddress, + transactionValue: event.data.value, + transactionHash: event.data.transactionHash, + userOpHash: event.data.userOpHash, + errorCode: + event.data.onChainTxStatus === 0 ? "EXECUTION_REVERTED" - : undefined - : item.error?.reason || undefined, - functionName: item.input.functionName || undefined, - extension: item.input.extension || undefined, - retryCount: item.input.retryCount || undefined, - provider: item.input.provider || undefined, - msSinceSend: item.input.msSinceSend || undefined, - msSinceQueue: item.input.msSinceQueue || undefined, + : event.error?.reason, + functionName: event.data.functionName, + extension: event.data.extension, + retryCount: event.data.retryCount, + provider: event.data.provider, + msSinceSend: event.data.msSinceSend, + msSinceQueue: event.data.msSinceQueue, }; fetch(env.CLIENT_ANALYTICS_URL, { method: "POST", - headers, + headers: defaultHeaders, body: JSON.stringify(requestBody), }).catch(() => {}); // Catch uncaught exceptions since this fetch call is non-blocking. - } catch (e) { + } catch (error) { logger({ service: "worker", level: "error", message: `Error:`, - error: e, + error, }); } }); diff --git a/src/utils/webhook.ts b/src/utils/webhook.ts index e71b406e3..5f0afd621 100644 --- a/src/utils/webhook.ts +++ b/src/utils/webhook.ts @@ -1,15 +1,5 @@ +import { Webhooks } from "@prisma/client"; import crypto from "crypto"; -import { getTxByIds } from "../db/transactions/getTxByIds"; -import { - SanitizedWebHooksSchema, - WalletBalanceWebhookSchema, - WebhooksEventTypes, -} from "../schema/webhooks"; -import { TransactionStatus } from "../server/schemas/transaction"; -import { getWebhook } from "./cache/getWebhook"; -import { logger } from "./logger"; - -let balanceNotificationLastSentAt = -1; export const generateSignature = ( body: Record, @@ -22,7 +12,7 @@ export const generateSignature = ( }; export const createWebhookRequestHeaders = async ( - webhookConfig: SanitizedWebHooksSchema, + webhook: Webhooks, body: Record, ): Promise => { const headers: { @@ -36,11 +26,11 @@ export const createWebhookRequestHeaders = async ( "Content-Type": "application/json", }; - if (webhookConfig.secret) { + if (webhook.secret) { const timestamp = Math.floor(Date.now() / 1000).toString(); - const signature = generateSignature(body, timestamp, webhookConfig.secret); + const signature = generateSignature(body, timestamp, webhook.secret); - headers["Authorization"] = `Bearer ${webhookConfig.secret}`; + headers["Authorization"] = `Bearer ${webhook.secret}`; headers["x-engine-signature"] = signature; headers["x-engine-timestamp"] = timestamp; } @@ -48,154 +38,42 @@ export const createWebhookRequestHeaders = async ( return headers; }; +export interface WebhookResponse { + ok: boolean; + status: number; + body: string; +} + +/** + * Calls a webhok URL with the appropriate headers and body. + * @param webhook + * @param body + * @returns ok boolean Whether the response was 2xx. + * @returns status number The response status code. + * @returns body string The response body. + */ export const sendWebhookRequest = async ( - webhookConfig: SanitizedWebHooksSchema, + webhook: Webhooks, body: Record, -): Promise => { +): Promise => { try { - const headers = await createWebhookRequestHeaders(webhookConfig, body); - const response = await fetch(webhookConfig?.url, { + const headers = await createWebhookRequestHeaders(webhook, body); + const resp = await fetch(webhook.url, { method: "POST", - headers: headers, + headers, body: JSON.stringify(body), }); - if (!response.ok) { - logger({ - service: "server", - level: "error", - message: `[sendWebhook] Webhook request error: ${response.status} ${response.statusText}`, - }); - - return false; - } - - return true; - } catch (error) { - logger({ - service: "server", - level: "error", - message: `[sendWebhook] Webhook request error: ${error}`, - error, - }); - return false; - } -}; - -export interface WebhookData { - queueId: string; - status: TransactionStatus; -} - -export const sendWebhooks = async (webhooks: WebhookData[]) => { - const queueIds = webhooks.map((webhook) => webhook.queueId); - const txs = await getTxByIds({ queueIds }); - if (!txs || txs.length === 0) { - return; - } - - const webhooksWithTxs = webhooks - .map((webhook) => { - const tx = txs.find((tx) => tx.queueId === webhook.queueId); - return { - ...webhook, - tx, - }; - }) - .filter((webhook) => !!webhook.tx); - - for (const webhook of webhooksWithTxs) { - const webhookStatus = - webhook.status === TransactionStatus.Queued - ? WebhooksEventTypes.QUEUED_TX - : webhook.status === TransactionStatus.Sent - ? WebhooksEventTypes.SENT_TX - : webhook.status === TransactionStatus.Mined - ? WebhooksEventTypes.MINED_TX - : webhook.status === TransactionStatus.Errored - ? WebhooksEventTypes.ERRORED_TX - : webhook.status === TransactionStatus.Cancelled - ? WebhooksEventTypes.CANCELLED_TX - : undefined; - - const webhookConfigs = await Promise.all([ - ...((await getWebhook(WebhooksEventTypes.ALL_TX)) || []), - ...(webhookStatus ? await getWebhook(webhookStatus) : []), - ]); - - await Promise.all( - webhookConfigs.map(async (webhookConfig) => { - if (!webhookConfig.active) { - logger({ - service: "server", - level: "debug", - message: "No webhook set or active, skipping webhook send", - }); - - return; - } - - await sendWebhookRequest( - webhookConfig, - webhook.tx as Record, - ); - }), - ); - } -}; - -// TODO: Add retry logic upto -export const sendBalanceWebhook = async ( - data: WalletBalanceWebhookSchema, -): Promise => { - try { - const elaspsedTime = Date.now() - balanceNotificationLastSentAt; - if (elaspsedTime < 30000) { - logger({ - service: "server", - level: "warn", - message: `[sendBalanceWebhook] Low wallet balance notification sent within last 30 Seconds. Skipping.`, - }); - return; - } - - const webhooks = await getWebhook( - WebhooksEventTypes.BACKEND_WALLET_BALANCE, - ); - - if (webhooks.length === 0) { - logger({ - service: "server", - level: "debug", - message: "No webhook set, skipping webhook send", - }); - - return; - } - - webhooks.map(async (config) => { - if (!config || !config.active) { - logger({ - service: "server", - level: "debug", - message: "No webhook set or active, skipping webhook send", - }); - - return; - } - - const success = await sendWebhookRequest(config, data); - - if (success) { - balanceNotificationLastSentAt = Date.now(); - } - }); - } catch (error) { - logger({ - service: "server", - level: "error", - message: `Failed to send balance webhook`, - error, - }); + return { + ok: resp.ok, + status: resp.status, + body: await resp.text(), + }; + } catch (e: any) { + return { + ok: false, + status: 500, + body: e, + }; } }; diff --git a/src/worker/index.ts b/src/worker/index.ts index fc393219b..107e9c305 100644 --- a/src/worker/index.ts +++ b/src/worker/index.ts @@ -7,10 +7,10 @@ import { deleteProcessedTx } from "./listeners/deleteProcessedTx"; import { minedTxListener } from "./listeners/minedTxListener"; import { queuedTxListener } from "./listeners/queuedTxListener"; import { retryTxListener } from "./listeners/retryTxListener"; -import { - newWebhooksListener, - updatedWebhooksListener, -} from "./listeners/webhookListener"; + +// Init Redis workers +import "./tasks/ingestWorker"; +import "./tasks/webhookWorker"; export const initWorker = async () => { // Listen for queued transactions to process @@ -29,9 +29,5 @@ export const initWorker = async () => { await newConfigurationListener(); await updatedConfigurationListener(); - // Listen for new & updated webhooks data - await newWebhooksListener(); - await updatedWebhooksListener(); - await chainIndexerListener(); }; diff --git a/src/worker/listeners/configListener.ts b/src/worker/listeners/configListener.ts index a00759d94..240e58388 100644 --- a/src/worker/listeners/configListener.ts +++ b/src/worker/listeners/configListener.ts @@ -1,5 +1,5 @@ import { knex } from "../../db/client"; -import { getConfig } from "../../utils/cache/getConfig"; +import { clearConfigCache, getConfig } from "../../utils/cache/getConfig"; import { clearCacheCron } from "../../utils/cron/clearCacheCron"; import { logger } from "../../utils/logger"; import { chainIndexerListener } from "./chainIndexerListener"; @@ -22,8 +22,8 @@ export const newConfigurationListener = async (): Promise => { connection.on( "notification", async (msg: { channel: string; payload: string }) => { - // Update Configs Data - await getConfig(false); + await clearConfigCache(); + await getConfig(); }, ); @@ -79,7 +79,8 @@ export const updatedConfigurationListener = async (): Promise => { level: "info", message: `Updated configuration data`, }); - await getConfig(false); + await clearConfigCache(); + await getConfig(); await queuedTxListener(); await minedTxListener(); await retryTxListener(); diff --git a/src/worker/listeners/webhookListener.ts b/src/worker/listeners/webhookListener.ts deleted file mode 100644 index 2ab19ba2f..000000000 --- a/src/worker/listeners/webhookListener.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { knex } from "../../db/client"; -import { webhookCache } from "../../utils/cache/getWebhook"; -import { logger } from "../../utils/logger"; - -export const newWebhooksListener = async (): Promise => { - logger({ - service: "worker", - level: "info", - message: `Listening for new webhooks data`, - }); - - // TODO: This doesn't even need to be a listener - const connection = await knex.client.acquireConnection(); - connection.query(`LISTEN new_webhook_data`); - - // Whenever we receive a new transaction, process it - connection.on( - "notification", - async (msg: { channel: string; payload: string }) => { - logger({ - service: "worker", - level: "info", - message: `Received new webhooks data`, - }); - // Update Webhooks Data - webhookCache.clear(); - }, - ); - - connection.on("end", async () => { - await knex.destroy(); - knex.client.releaseConnection(connection); - - logger({ - service: "worker", - level: "info", - message: `Released database connection on end`, - }); - }); - - connection.on("error", async (err: any) => { - logger({ - service: "worker", - level: "error", - message: `Database connection error`, - error: err, - }); - - await knex.destroy(); - knex.client.releaseConnection(connection); - - logger({ - service: "worker", - level: "info", - message: `Released database connection on error`, - error: err, - }); - }); -}; - -export const updatedWebhooksListener = async (): Promise => { - logger({ - service: "worker", - level: "info", - message: `Listening for updated webhooks data`, - }); - - // TODO: This doesn't even need to be a listener - const connection = await knex.client.acquireConnection(); - connection.query(`LISTEN updated_webhook_data`); - - // Whenever we receive a new transaction, process it - connection.on( - "notification", - async (msg: { channel: string; payload: string }) => { - // Update Configs Data - logger({ - service: "worker", - level: "info", - message: `Received updated webhooks data`, - }); - webhookCache.clear(); - }, - ); - - connection.on("end", async () => { - await knex.destroy(); - knex.client.releaseConnection(connection); - - logger({ - service: "worker", - level: "info", - message: `Released database connection on end`, - }); - }); - - connection.on("error", async (err: any) => { - logger({ - service: "worker", - level: "error", - message: `Database connection error`, - error: err, - }); - - await knex.destroy(); - knex.client.releaseConnection(connection); - - logger({ - service: "worker", - level: "info", - message: `Released database connection on error`, - error: err, - }); - }); -}; diff --git a/src/worker/queues/queues.ts b/src/worker/queues/queues.ts new file mode 100644 index 000000000..7f1ac1616 --- /dev/null +++ b/src/worker/queues/queues.ts @@ -0,0 +1,82 @@ +import { Transactions, Webhooks } from "@prisma/client"; +import { JobsOptions, Queue } from "bullmq"; +import superjson from "superjson"; +import { getAllWebhooks } from "../../db/webhooks/getAllWebhooks"; +import { QueuedTransaction } from "../../schema/transaction"; +import { WebhooksEventTypes } from "../../schema/webhooks"; +import { TransactionStatus } from "../../server/schemas/transaction"; +import { redis } from "../../utils/redis/redis"; + +const defaultJobOptions: JobsOptions = { + attempts: 3, + // Retries after 5s, 10s, 20s. + backoff: { type: "exponential", delay: 5_000 }, + // Purges completed jobs past 500 or 2 days. + removeOnComplete: { + age: 60 * 60 * 24 * 2, + count: 500, + }, +}; + +export type QueueType = "ingest" | "webhook"; + +/** + * Processes write transaction requests. + */ +export interface IngestJob { + tx: QueuedTransaction; +} +export class IngestQueue { + private static q = new Queue("ingest", { + connection: redis, + defaultJobOptions, + }); + + static add = async (data: IngestJob) => { + const serialized = superjson.stringify(data); + await this.q.add(data.tx.idempotencyKey, serialized, { + jobId: data.tx.idempotencyKey, + }); + }; +} + +/** + * Sends webhooks to configured webhook URLs. + */ +export type EnqueueWebhookData = + | { + type: WebhooksEventTypes.ALL_TRANSACTIONS; + status: TransactionStatus; + tx: Transactions; + } + | { + type: WebhooksEventTypes.BACKEND_WALLET_BALANCE; + walletAddress: string; + minimumBalance: string; + currentBalance: string; + chainId: number; + message: string; + }; +export interface WebhookJob { + data: EnqueueWebhookData; + webhook: Webhooks; +} + +export class WebhookQueue { + private static q = new Queue("webhook", { + connection: redis, + defaultJobOptions, + }); + + static add = async (data: EnqueueWebhookData) => { + const webhooks = await getAllWebhooks(); + + for (const webhook of webhooks) { + if (!webhook.revokedAt && data.type === webhook.eventType) { + const job: WebhookJob = { data, webhook }; + const serialized = superjson.stringify(job); + await this.q.add(`${data.type}:${webhook.id}`, serialized); + } + } + }; +} diff --git a/src/worker/queues/workers.ts b/src/worker/queues/workers.ts new file mode 100644 index 000000000..56836dee7 --- /dev/null +++ b/src/worker/queues/workers.ts @@ -0,0 +1,29 @@ +import { Job, Worker } from "bullmq"; +import { env } from "../../utils/env"; +import { logger } from "../../utils/logger"; + +export const logWorkerEvents = (worker: Worker) => { + worker.on("active", (job: Job) => { + logger({ + level: "debug", + message: `Processing job ${job.id}`, + service: "worker", + }); + }); + worker.on("completed", (job: Job) => { + logger({ + level: "debug", + message: `[${worker.name}] Job ${job.id} has completed!`, + service: "worker", + }); + }); + worker.on("failed", (job: Job | undefined, err: Error) => { + logger({ + level: "error", + message: `[${worker.name}] Job ${job?.id ?? "?"} has failed with ${ + err.message + } ${env.NODE_ENV === "development" ? err.stack : ""}`, + service: "worker", + }); + }); +}; diff --git a/src/worker/tasks/ingestWorker.ts b/src/worker/tasks/ingestWorker.ts new file mode 100644 index 000000000..eb2050be7 --- /dev/null +++ b/src/worker/tasks/ingestWorker.ts @@ -0,0 +1,26 @@ +import { Job, Processor, Worker } from "bullmq"; +import superjson from "superjson"; +import { prisma } from "../../db/client"; +import { env } from "../../utils/env"; +import { redis } from "../../utils/redis/redis"; +import { IngestJob } from "../queues/queues"; +import { logWorkerEvents } from "../queues/workers"; + +const handleIngest: Processor = async (job: Job) => { + const { tx } = superjson.parse(job.data); + + // Insert if the idempotency key does not exist. Else no-op. + await prisma.transactions.upsert({ + where: { + idempotencyKey: tx.idempotencyKey, + }, + create: tx, + update: {}, + }); +}; + +const ingestWorker = new Worker("ingest", handleIngest, { + concurrency: env.INGEST_WORKER_CONCURRENCY, + connection: redis, +}); +logWorkerEvents(ingestWorker); diff --git a/src/worker/tasks/processTx.ts b/src/worker/tasks/processTx.ts index 80ac0eca3..db3e782a6 100644 --- a/src/worker/tasks/processTx.ts +++ b/src/worker/tasks/processTx.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { Transactions } from ".prisma/client"; import { getChainByChainIdAsync } from "@thirdweb-dev/chains"; import { @@ -15,7 +14,7 @@ import { getQueuedTxs } from "../../db/transactions/getQueuedTxs"; import { updateTx } from "../../db/transactions/updateTx"; import { getWalletNonce } from "../../db/wallets/getWalletNonce"; import { updateWalletNonce } from "../../db/wallets/updateWalletNonce"; -import { WalletBalanceWebhookSchema } from "../../schema/webhooks"; +import { WebhooksEventTypes } from "../../schema/webhooks"; import { TransactionStatus } from "../../server/schemas/transaction"; import { getConfig } from "../../utils/cache/getConfig"; import { getSdk } from "../../utils/cache/getSdk"; @@ -25,14 +24,10 @@ import { parseTxError } from "../../utils/errors"; import { logger } from "../../utils/logger"; import { ReportUsageParams, - UsageEventTxActionEnum, + UsageEventType, reportUsage, } from "../../utils/usage"; -import { - WebhookData, - sendBalanceWebhook, - sendWebhooks, -} from "../../utils/webhook"; +import { WebhookQueue } from "../queues/queues"; import { randomNonce } from "../utils/nonce"; import { getWithdrawValue } from "../utils/withdraw"; @@ -44,7 +39,6 @@ type RpcResponseData = { export const processTx = async () => { try { - const sendWebhookForQueueIds: WebhookData[] = []; const reportUsageForQueueIds: ReportUsageParams[] = []; await prisma.$transaction( async (pgtx) => { @@ -71,7 +65,6 @@ export const processTx = async () => { queueId: tx.id, message: `Processing`, }); - if (tx.accountAddress && tx.signerAddress) { userOpsToSend.push(tx); } else { @@ -208,10 +201,6 @@ export const processTx = async () => { txRequest, rpcResponse, }); - sendWebhookForQueueIds.push({ - queueId: tx.id, - status: TransactionStatus.Sent, - }); } else if ( typeof rpcResponse.error?.message === "string" && (rpcResponse.error.message as string) @@ -229,31 +218,31 @@ export const processTx = async () => { txRequest, rpcResponse, }); - sendWebhookForQueueIds.push({ - queueId: tx.id, - status: TransactionStatus.Errored, - }); } } catch (err: any) { // Error. Continue to the next transaction. txIndex++; - sendWebhookForQueueIds.push({ + await updateTx({ + pgtx, queueId: tx.id, - status: TransactionStatus.Errored, + data: { + status: TransactionStatus.Errored, + errorMessage: await parseTxError(tx, err), + }, }); reportUsageForQueueIds.push({ - input: { + data: { fromAddress: tx.fromAddress || undefined, toAddress: tx.toAddress || undefined, value: tx.value || undefined, - chainId: tx.chainId || undefined, + chainId: tx.chainId, functionName: tx.functionName || undefined, extension: tx.extension || undefined, provider: provider.connection.url || undefined, msSinceQueue: msSince(tx.queuedAt), }, - action: UsageEventTxActionEnum.NotSendTx, + action: UsageEventType.NotSendTx, }); logger({ @@ -263,15 +252,6 @@ export const processTx = async () => { message: `Failed to send`, error: err, }); - - await updateTx({ - pgtx, - queueId: tx.id, - data: { - status: TransactionStatus.Errored, - errorMessage: await parseTxError(tx, err), - }, - }); } } @@ -300,7 +280,7 @@ export const processTx = async () => { }, }); reportUsageForQueueIds.push({ - input: { + data: { fromAddress: txRequest.from, toAddress: txRequest.to, value: (txRequest.value ?? 0).toString(), @@ -311,7 +291,7 @@ export const processTx = async () => { provider: provider.connection.url || undefined, msSinceQueue: msSince(tx.queuedAt), }, - action: UsageEventTxActionEnum.SendTx, + action: UsageEventType.SendTx, }); } else { // Transaction failed. @@ -324,7 +304,7 @@ export const processTx = async () => { }, }); reportUsageForQueueIds.push({ - input: { + data: { fromAddress: txRequest.from, toAddress: txRequest.to, value: (txRequest.value ?? 0).toString(), @@ -334,7 +314,7 @@ export const processTx = async () => { provider: provider.connection.url || undefined, msSinceQueue: msSince(tx.queuedAt), }, - action: UsageEventTxActionEnum.NotSendTx, + action: UsageEventType.NotSendTx, }); } }), @@ -383,23 +363,19 @@ export const processTx = async () => { userOpHash, }, }); - sendWebhookForQueueIds.push({ - queueId: tx.id, - status: TransactionStatus.UserOpSent, - }); reportUsageForQueueIds.push({ - input: { + data: { fromAddress: tx.accountAddress || undefined, toAddress: tx.toAddress || undefined, value: tx.value || undefined, - chainId: tx.chainId || undefined, + chainId: tx.chainId, userOpHash, functionName: tx.functionName || undefined, extension: tx.extension || undefined, - provider: signer.httpRpcClient.bundlerUrl || undefined, + provider: signer.httpRpcClient.bundlerUrl, msSinceQueue: msSince(tx.queuedAt), }, - action: UsageEventTxActionEnum.SendTx, + action: UsageEventType.SendTx, }); } catch (err: any) { logger({ @@ -418,21 +394,17 @@ export const processTx = async () => { errorMessage: await parseTxError(tx, err), }, }); - sendWebhookForQueueIds.push({ - queueId: tx.id, - status: TransactionStatus.Errored, - }); reportUsageForQueueIds.push({ - input: { + data: { fromAddress: tx.accountAddress || undefined, toAddress: tx.toAddress || undefined, value: tx.value || undefined, - chainId: tx.chainId || undefined, + chainId: tx.chainId, functionName: tx.functionName || undefined, extension: tx.extension || undefined, msSinceQueue: msSince(tx.queuedAt), }, - action: UsageEventTxActionEnum.NotSendTx, + action: UsageEventType.NotSendTx, }); } }); @@ -445,7 +417,6 @@ export const processTx = async () => { }, ); - await sendWebhooks(sendWebhookForQueueIds); reportUsage(reportUsageForQueueIds); } catch (err: any) { logger({ @@ -469,15 +440,14 @@ const alertOnBackendWalletLowBalance = async (wallet: UserWallet) => { config.minWalletBalance, ); - const walletBalanceData: WalletBalanceWebhookSchema = { + await WebhookQueue.add({ + type: WebhooksEventTypes.BACKEND_WALLET_BALANCE, walletAddress: address, minimumBalance: minBalanceDisplay, currentBalance: balance.displayValue, chainId: chain.chainId, message: `Backend wallet ${address} has below ${minBalanceDisplay} ${chain.nativeCurrency.symbol}.`, - }; - - await sendBalanceWebhook(walletBalanceData); + }); } } catch (e) {} }; diff --git a/src/worker/tasks/retryTx.ts b/src/worker/tasks/retryTx.ts index 7b681a462..8fd0506f7 100644 --- a/src/worker/tasks/retryTx.ts +++ b/src/worker/tasks/retryTx.ts @@ -11,7 +11,7 @@ import { getGasSettingsForRetry } from "../../utils/gas"; import { logger } from "../../utils/logger"; import { ReportUsageParams, - UsageEventTxActionEnum, + UsageEventType, reportUsage, } from "../../utils/usage"; @@ -88,17 +88,17 @@ export const retryTx = async () => { }); reportUsageForQueueIds.push({ - input: { + data: { fromAddress: tx.fromAddress || undefined, toAddress: tx.toAddress || undefined, value: tx.value || undefined, - chainId: tx.chainId || undefined, + chainId: tx.chainId, functionName: tx.functionName || undefined, extension: tx.extension || undefined, - retryCount: tx.retryCount + 1 || 0, - provider: provider.connection.url || undefined, + retryCount: tx.retryCount + 1, + provider: provider.connection.url, }, - action: UsageEventTxActionEnum.ErrorTx, + action: UsageEventType.ErrorTx, }); reportUsage(reportUsageForQueueIds); @@ -120,18 +120,18 @@ export const retryTx = async () => { }); reportUsageForQueueIds.push({ - input: { + data: { fromAddress: tx.fromAddress || undefined, toAddress: tx.toAddress || undefined, value: tx.value || undefined, - chainId: tx.chainId || undefined, + chainId: tx.chainId, functionName: tx.functionName || undefined, extension: tx.extension || undefined, retryCount: tx.retryCount + 1, transactionHash: res.hash || undefined, - provider: provider.connection.url || undefined, + provider: provider.connection.url, }, - action: UsageEventTxActionEnum.SendTx, + action: UsageEventType.SendTx, }); reportUsage(reportUsageForQueueIds); diff --git a/src/worker/tasks/updateMinedTx.ts b/src/worker/tasks/updateMinedTx.ts index e3cf58a0e..57cd3f6fd 100644 --- a/src/worker/tasks/updateMinedTx.ts +++ b/src/worker/tasks/updateMinedTx.ts @@ -7,19 +7,18 @@ import { updateTx } from "../../db/transactions/updateTx"; import { TransactionStatus } from "../../server/schemas/transaction"; import { cancelTransactionAndUpdate } from "../../server/utils/transaction"; import { getSdk } from "../../utils/cache/getSdk"; +import { msSince } from "../../utils/date"; import { logger } from "../../utils/logger"; import { ReportUsageParams, - UsageEventTxActionEnum, + UsageEventType, reportUsage, } from "../../utils/usage"; -import { WebhookData, sendWebhooks } from "../../utils/webhook"; const MEMPOOL_DURATION_TIMEOUT_MS = 1000 * 60 * 60; export const updateMinedTx = async () => { try { - const sendWebhookForQueueIds: WebhookData[] = []; const reportUsageForQueueIds: ReportUsageParams[] = []; await prisma.$transaction( async (pgtx) => { @@ -52,22 +51,17 @@ export const updateMinedTx = async () => { pgtx, }); - sendWebhookForQueueIds.push({ - queueId: tx.id, - status: TransactionStatus.Cancelled, - }); - reportUsageForQueueIds.push({ - input: { + data: { fromAddress: tx.fromAddress || undefined, toAddress: tx.toAddress || undefined, value: tx.value || undefined, - chainId: tx.chainId || undefined, + chainId: tx.chainId, transactionHash: tx.transactionHash || undefined, provider: provider.connection.url || undefined, - msSinceSend: Date.now() - tx.sentAt!.getTime(), + msSinceSend: msSince(new Date(tx.sentAt!)), }, - action: UsageEventTxActionEnum.CancelTx, + action: UsageEventType.CancelTx, }); } catch (error) { await updateTx({ @@ -79,22 +73,17 @@ export const updateMinedTx = async () => { }, }); - sendWebhookForQueueIds.push({ - queueId: tx.id, - status: TransactionStatus.Errored, - }); - reportUsageForQueueIds.push({ - input: { + data: { fromAddress: tx.fromAddress || undefined, toAddress: tx.toAddress || undefined, value: tx.value || undefined, - chainId: tx.chainId || undefined, + chainId: tx.chainId, transactionHash: tx.transactionHash || undefined, provider: provider.connection.url || undefined, - msSinceSend: Date.now() - tx.sentAt!.getTime(), + msSinceSend: msSince(new Date(tx.sentAt!)), }, - action: UsageEventTxActionEnum.ErrorTx, + action: UsageEventType.ErrorTx, }); } } @@ -171,27 +160,22 @@ export const updateMinedTx = async () => { message: "Updated mined tx.", }); - sendWebhookForQueueIds.push({ - queueId: txWithReceipt.tx.id, - status: TransactionStatus.Mined, - }); - reportUsageForQueueIds.push({ - input: { + data: { fromAddress: txWithReceipt.tx.fromAddress || undefined, toAddress: txWithReceipt.tx.toAddress || undefined, value: txWithReceipt.tx.value || undefined, - chainId: txWithReceipt.tx.chainId || undefined, + chainId: txWithReceipt.tx.chainId, transactionHash: txWithReceipt.tx.transactionHash || undefined, onChainTxStatus: txWithReceipt.receipt.status, functionName: txWithReceipt.tx.functionName || undefined, extension: txWithReceipt.tx.extension || undefined, - provider: txWithReceipt.provider || undefined, + provider: txWithReceipt.provider, msSinceSend: txWithReceipt.minedAt.getTime() - txWithReceipt.tx.sentAt!.getTime(), }, - action: UsageEventTxActionEnum.MineTx, + action: UsageEventType.MineTx, }); }), ); @@ -201,7 +185,6 @@ export const updateMinedTx = async () => { }, ); - await sendWebhooks(sendWebhookForQueueIds); reportUsage(reportUsageForQueueIds); } catch (err) { logger({ diff --git a/src/worker/tasks/updateMinedUserOps.ts b/src/worker/tasks/updateMinedUserOps.ts index 23b2422cd..5ef78db97 100644 --- a/src/worker/tasks/updateMinedUserOps.ts +++ b/src/worker/tasks/updateMinedUserOps.ts @@ -8,14 +8,12 @@ import { getSdk } from "../../utils/cache/getSdk"; import { logger } from "../../utils/logger"; import { ReportUsageParams, - UsageEventTxActionEnum, + UsageEventType, reportUsage, } from "../../utils/usage"; -import { WebhookData, sendWebhooks } from "../../utils/webhook"; export const updateMinedUserOps = async () => { try { - const sendWebhookForQueueIds: WebhookData[] = []; const reportUsageForQueueIds: ReportUsageParams[] = []; await prisma.$transaction( async (pgtx) => { @@ -90,9 +88,9 @@ export const updateMinedUserOps = async () => { onChainTxStatus: userOp!.onChainTxStatus, transactionHash: userOp!.transactionHash, transactionType: userOp!.transactionType || undefined, - gasLimit: userOp!.gasLimit || undefined, - maxFeePerGas: userOp!.maxFeePerGas || undefined, - maxPriorityFeePerGas: userOp!.maxPriorityFeePerGas || undefined, + gasLimit: userOp!.gasLimit, + maxFeePerGas: userOp!.maxFeePerGas, + maxPriorityFeePerGas: userOp!.maxPriorityFeePerGas, gasPrice: userOp!.gasPrice || undefined, }, }); @@ -103,25 +101,21 @@ export const updateMinedUserOps = async () => { queueId: userOp!.id, message: `Updated with receipt`, }); - sendWebhookForQueueIds.push({ - queueId: userOp!.id, - status: TransactionStatus.Mined, - }); reportUsageForQueueIds.push({ - input: { + data: { fromAddress: userOp!.fromAddress || undefined, toAddress: userOp!.toAddress || undefined, value: userOp!.value || undefined, - chainId: userOp!.chainId || undefined, + chainId: userOp!.chainId, userOpHash: userOp!.userOpHash || undefined, onChainTxStatus: userOp!.onChainTxStatus, functionName: userOp!.functionName || undefined, extension: userOp!.extension || undefined, - provider: userOp!.provider || undefined, + provider: userOp!.provider, msSinceSend: userOp!.minedAt.getTime() - userOp!.sentAt!.getTime(), }, - action: UsageEventTxActionEnum.MineTx, + action: UsageEventType.MineTx, }); }), ); @@ -131,7 +125,6 @@ export const updateMinedUserOps = async () => { }, ); - await sendWebhooks(sendWebhookForQueueIds); reportUsage(reportUsageForQueueIds); } catch (err) { logger({ diff --git a/src/worker/tasks/webhookWorker.ts b/src/worker/tasks/webhookWorker.ts new file mode 100644 index 000000000..8bf629ca6 --- /dev/null +++ b/src/worker/tasks/webhookWorker.ts @@ -0,0 +1,31 @@ +import { Job, Processor, Worker } from "bullmq"; +import superjson from "superjson"; +import { WebhooksEventTypes } from "../../schema/webhooks"; +import { toTransactionResponse } from "../../server/schemas/transaction"; +import { redis } from "../../utils/redis/redis"; +import { WebhookResponse, sendWebhookRequest } from "../../utils/webhook"; +import { WebhookJob } from "../queues/queues"; +import { logWorkerEvents } from "../queues/workers"; + +const handleWebhook: Processor = async ( + job: Job, +) => { + const { data, webhook } = superjson.parse(job.data); + + let resp: WebhookResponse | undefined; + if (data.type === WebhooksEventTypes.ALL_TRANSACTIONS) { + resp = await sendWebhookRequest(webhook, toTransactionResponse(data.tx)); + } else if (data.type === WebhooksEventTypes.BACKEND_WALLET_BALANCE) { + resp = await sendWebhookRequest(webhook, data); + } + + if (resp && !resp.ok) { + throw `Received status ${resp.status} from webhook ${webhook.url}.`; + } +}; + +const webhookWorker = new Worker("webhook", handleWebhook, { + concurrency: 1, + connection: redis, +}); +logWorkerEvents(webhookWorker); diff --git a/src/worker/utils/withdraw.ts b/src/worker/utils/withdraw.ts index 599600204..e1d11f7f8 100644 --- a/src/worker/utils/withdraw.ts +++ b/src/worker/utils/withdraw.ts @@ -1,8 +1,6 @@ import { defineChain, prepareTransaction } from "thirdweb"; -import { ethers5Adapter } from "thirdweb/adapters/ethers5"; import { estimateGasCost } from "thirdweb/transaction"; import { getWalletBalance } from "thirdweb/wallets"; -import { getWallet } from "../../utils/cache/getWallet"; import { thirdwebClient } from "../../utils/sdk"; interface GetWithdrawValueParams { @@ -19,11 +17,8 @@ export const getWithdrawValue = async ({ const chain = defineChain(chainId); // Get wallet balance. - const wallet = await getWallet({ chainId, walletAddress: fromAddress }); - const signer = await wallet.getSigner(); - const account = await ethers5Adapter.signer.fromEthers(signer); const { value: balanceWei } = await getWalletBalance({ - account, + address: fromAddress, client: thirdwebClient, chain, }); diff --git a/yarn.lock b/yarn.lock index e2b1dde6b..33f509bca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1065,7 +1065,22 @@ crc-32 "^1.2.0" ethereumjs-util "^7.1.5" -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": +"@ethersproject/abi@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.5.0.tgz#fb52820e22e50b854ff15ce1647cc508d6660613" + integrity sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w== + dependencies: + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -1080,7 +1095,20 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0": +"@ethersproject/abstract-provider@5.5.1": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz#2f1f6e8a3ab7d378d8ad0b5718460f85649710c5" + integrity sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/networks" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/web" "^5.5.0" + +"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.5.0", "@ethersproject/abstract-provider@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== @@ -1093,7 +1121,18 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/web" "^5.7.0" -"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": +"@ethersproject/abstract-signer@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz#590ff6693370c60ae376bf1c7ada59eb2a8dd08d" + integrity sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + +"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.5.0", "@ethersproject/abstract-signer@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== @@ -1104,7 +1143,18 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/address@5.7.0", "@ethersproject/address@^5.7.0": +"@ethersproject/address@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.5.0.tgz#bcc6f576a553f21f3dd7ba17248f81b473c9c78f" + integrity sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + +"@ethersproject/address@5.7.0", "@ethersproject/address@^5.5.0", "@ethersproject/address@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== @@ -1115,14 +1165,29 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/rlp" "^5.7.0" -"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0": +"@ethersproject/base64@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.5.0.tgz#881e8544e47ed976930836986e5eb8fab259c090" + integrity sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + +"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.5.0", "@ethersproject/base64@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== dependencies: "@ethersproject/bytes" "^5.7.0" -"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0": +"@ethersproject/basex@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.5.0.tgz#e40a53ae6d6b09ab4d977bd037010d4bed21b4d3" + integrity sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + +"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.5.0", "@ethersproject/basex@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== @@ -1130,7 +1195,16 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0": +"@ethersproject/bignumber@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.5.0.tgz#875b143f04a216f4f8b96245bde942d42d279527" + integrity sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + bn.js "^4.11.9" + +"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.5.0", "@ethersproject/bignumber@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== @@ -1139,20 +1213,50 @@ "@ethersproject/logger" "^5.7.0" bn.js "^5.2.1" -"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0": +"@ethersproject/bytes@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" + integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== + dependencies: + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.5.0", "@ethersproject/bytes@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0": +"@ethersproject/constants@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.5.0.tgz#d2a2cd7d94bd1d58377d1d66c4f53c9be4d0a45e" + integrity sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + +"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.5.0", "@ethersproject/constants@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== dependencies: "@ethersproject/bignumber" "^5.7.0" +"@ethersproject/contracts@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.5.0.tgz#b735260d4bd61283a670a82d5275e2a38892c197" + integrity sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg== + dependencies: + "@ethersproject/abi" "^5.5.0" + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/contracts@5.7.0", "@ethersproject/contracts@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" @@ -1169,7 +1273,21 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/transactions" "^5.7.0" -"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0": +"@ethersproject/hash@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.5.0.tgz#7cee76d08f88d1873574c849e0207dcb32380cc9" + integrity sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.5.0", "@ethersproject/hash@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== @@ -1184,7 +1302,25 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0": +"@ethersproject/hdnode@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.5.0.tgz#4a04e28f41c546f7c978528ea1575206a200ddf6" + integrity sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/basex" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/pbkdf2" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/wordlists" "^5.5.0" + +"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.5.0", "@ethersproject/hdnode@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf" integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg== @@ -1202,7 +1338,26 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/wordlists" "^5.7.0" -"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0": +"@ethersproject/json-wallets@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz#dd522d4297e15bccc8e1427d247ec8376b60e325" + integrity sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/hdnode" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/pbkdf2" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/random" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + +"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.5.0", "@ethersproject/json-wallets@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360" integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g== @@ -1221,7 +1376,15 @@ aes-js "3.0.0" scrypt-js "3.0.1" -"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": +"@ethersproject/keccak256@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.5.0.tgz#e4b1f9d7701da87c564ffe336f86dcee82983492" + integrity sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + js-sha3 "0.8.0" + +"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.5.0", "@ethersproject/keccak256@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== @@ -1229,19 +1392,39 @@ "@ethersproject/bytes" "^5.7.0" js-sha3 "0.8.0" -"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0": +"@ethersproject/logger@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" + integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== + +"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.5.0", "@ethersproject/logger@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== -"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0": +"@ethersproject/networks@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.0.tgz#babec47cab892c51f8dd652ce7f2e3e14283981a" + integrity sha512-KWfP3xOnJeF89Uf/FCJdV1a2aDJe5XTN2N52p4fcQ34QhDqQFkgQKZ39VGtiqUgHcLI8DfT0l9azC3KFTunqtA== + dependencies: + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.5.0", "@ethersproject/networks@^5.7.0": version "5.7.1" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0": +"@ethersproject/pbkdf2@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz#e25032cdf02f31505d47afbf9c3e000d95c4a050" + integrity sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + +"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.5.0", "@ethersproject/pbkdf2@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102" integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw== @@ -1249,13 +1432,45 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/sha2" "^5.7.0" -"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0": +"@ethersproject/properties@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.5.0.tgz#61f00f2bb83376d2071baab02245f92070c59995" + integrity sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA== + dependencies: + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.5.0", "@ethersproject/properties@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== dependencies: "@ethersproject/logger" "^5.7.0" +"@ethersproject/providers@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.5.0.tgz#bc2876a8fe5e0053ed9828b1f3767ae46e43758b" + integrity sha512-xqMbDnS/FPy+J/9mBLKddzyLLAQFjrVff5g00efqxPzcAwXiR+SiCGVy6eJ5iAIirBOATjx7QLhDNPGV+AEQsw== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/basex" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/networks" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/random" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/web" "^5.5.0" + bech32 "1.1.4" + ws "7.4.6" + "@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.0", "@ethersproject/providers@^5.7.2": version "5.7.2" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" @@ -1282,7 +1497,15 @@ bech32 "1.1.4" ws "7.4.6" -"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": +"@ethersproject/random@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.5.0.tgz#305ed9e033ca537735365ac12eed88580b0f81f9" + integrity sha512-egGYZwZ/YIFKMHcoBUo8t3a8Hb/TKYX8BCBoLjudVCZh892welR3jOxgOmb48xznc9bTcMm7Tpwc1gHC1PFNFQ== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/random@5.7.0", "@ethersproject/random@^5.5.0", "@ethersproject/random@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== @@ -1290,7 +1513,15 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": +"@ethersproject/rlp@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.5.0.tgz#530f4f608f9ca9d4f89c24ab95db58ab56ab99a0" + integrity sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.5.0", "@ethersproject/rlp@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== @@ -1298,7 +1529,16 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0": +"@ethersproject/sha2@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" + integrity sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + hash.js "1.1.7" + +"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.5.0", "@ethersproject/sha2@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== @@ -1307,7 +1547,19 @@ "@ethersproject/logger" "^5.7.0" hash.js "1.1.7" -"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": +"@ethersproject/signing-key@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.5.0.tgz#2aa37169ce7e01e3e80f2c14325f624c29cedbe0" + integrity sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.5.0", "@ethersproject/signing-key@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== @@ -1319,6 +1571,18 @@ elliptic "6.5.4" hash.js "1.1.7" +"@ethersproject/solidity@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.5.0.tgz#2662eb3e5da471b85a20531e420054278362f93f" + integrity sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/solidity@5.7.0", "@ethersproject/solidity@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" @@ -1331,7 +1595,16 @@ "@ethersproject/sha2" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": +"@ethersproject/strings@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.5.0.tgz#e6784d00ec6c57710755699003bc747e98c5d549" + integrity sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.5.0", "@ethersproject/strings@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== @@ -1340,7 +1613,22 @@ "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.6.2", "@ethersproject/transactions@^5.7.0": +"@ethersproject/transactions@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.5.0.tgz#7e9bf72e97bcdf69db34fe0d59e2f4203c7a2908" + integrity sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA== + dependencies: + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + +"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.5.0", "@ethersproject/transactions@^5.6.2", "@ethersproject/transactions@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== @@ -1355,6 +1643,15 @@ "@ethersproject/rlp" "^5.7.0" "@ethersproject/signing-key" "^5.7.0" +"@ethersproject/units@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.5.0.tgz#104d02db5b5dc42cc672cc4587bafb87a95ee45e" + integrity sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/units@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" @@ -1364,6 +1661,27 @@ "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" +"@ethersproject/wallet@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.5.0.tgz#322a10527a440ece593980dca6182f17d54eae75" + integrity sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/hdnode" "^5.5.0" + "@ethersproject/json-wallets" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/random" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/wordlists" "^5.5.0" + "@ethersproject/wallet@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d" @@ -1385,7 +1703,18 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/wordlists" "^5.7.0" -"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0", "@ethersproject/web@^5.7.1": +"@ethersproject/web@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.5.0.tgz#0e5bb21a2b58fb4960a705bfc6522a6acf461e28" + integrity sha512-BEgY0eL5oH4mAo37TNYVrFeHsIXLRxggCRG/ksRIxI2X5uj5IsjGmcNiRN/VirQOlBxcUhCgHhaDLG4m6XAVoA== + dependencies: + "@ethersproject/base64" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/web@5.7.1", "@ethersproject/web@^5.5.0", "@ethersproject/web@^5.7.0", "@ethersproject/web@^5.7.1": version "5.7.1" resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== @@ -1396,7 +1725,18 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0": +"@ethersproject/wordlists@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.5.0.tgz#aac74963aa43e643638e5172353d931b347d584f" + integrity sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.5.0", "@ethersproject/wordlists@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5" integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA== @@ -1567,6 +1907,11 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@ioredis/commands@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" + integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -1895,6 +2240,17 @@ resolved "https://registry.yarnpkg.com/@magic-sdk/types/-/types-11.6.2.tgz#1fb6205b516c3f0c5787e82aecd0e667d2de1cd2" integrity sha512-+Emd+9HeeVi2E0bktJ33YleA/ozEuKYCBfmSbGRxlntdyUvaojeC+WPf2jN1WH8FjUEiljAjrEJTTZyRGCL8SQ== +"@metamask/eth-sig-util@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.0.tgz#11553ba06de0d1352332c1bde28c8edd00e0dcf6" + integrity sha512-LczOjjxY4A7XYloxzyxJIHONELmUxVZncpOLoClpEcTiebiVdM46KRPYXGuULro9oNNR2xdVx3yoKiQjdfWmoA== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^6.2.1" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + "@metamask/eth-sig-util@^4.0.0": version "4.0.1" resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" @@ -1990,6 +2346,36 @@ "@motionone/dom" "^10.16.2" tslib "^2.3.1" +"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz#44d752c1a2dc113f15f781b7cc4f53a307e3fa38" + integrity sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ== + +"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz#f954f34355712212a8e06c465bc06c40852c6bb3" + integrity sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw== + +"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz#45c63037f045c2b15c44f80f0393fa24f9655367" + integrity sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg== + +"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz#35707efeafe6d22b3f373caf9e8775e8920d1399" + integrity sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA== + +"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz#091b1218b66c341f532611477ef89e83f25fae4f" + integrity sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA== + +"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz#0f164b726869f71da3c594171df5ebc1c4b0a407" + integrity sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ== + "@multiformats/base-x@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" @@ -3128,17 +3514,17 @@ resolved "https://registry.yarnpkg.com/@t3-oss/env-core/-/env-core-0.6.0.tgz#a2e6a6a2b166aad2df7ecc5e7a72400946a84fbe" integrity sha512-3FkPAba069WRZVVab/sB1m3eSGn/rZeypx5k+sWEu1d+k0OQdRDnvFS+7MtxYgqVrwaRk3b7yVnX2dgSPVmWPQ== -"@tanstack/query-core@5.28.4": - version "5.28.4" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.28.4.tgz#fa416532f8b33ca8608d40bb9728b60e2e1a38dc" - integrity sha512-uQZqOFqLWUvXNIQZ63XdKzg22NtHzgCBUfDmjDHi3BoF+nUYeBNvMi/xFPtFrMhqRzG2Ir4mYaGsWZzmiEjXpA== +"@tanstack/query-core@5.28.9": + version "5.28.9" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.28.9.tgz#170a7a8794ab73aeffbaf711ac62126479a5d026" + integrity sha512-hNlfCiqZevr3GRVPXS3MhaGW5hjcxvCsIQ4q6ff7EPlvFwYZaS+0d9EIIgofnegDaU2BbCDlyURoYfRl5rmzow== -"@tanstack/react-query@5.28.4": - version "5.28.4" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.28.4.tgz#32e56ca4fd08513a906fe6323908f0e38ffccbba" - integrity sha512-BErcoB/QQG6YwLSUKnaGxF+lSc270RH2w3kMBpG0i4YzDCsFs2pdxPX1WVknQvFk9bNgukMb158hc2Zb4SdwSA== +"@tanstack/react-query@5.28.9": + version "5.28.9" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.28.9.tgz#13c2049daa5db6c3137473e279b209f76d39708e" + integrity sha512-vwifBkGXsydsLxFOBMe3+f8kvtDoqDRDwUNjPHVDDt+FoBetCbOWAUHgZn4k+CVeZgLmy7bx6aKeDbe3e8koOQ== dependencies: - "@tanstack/query-core" "5.28.4" + "@tanstack/query-core" "5.28.9" "@thirdweb-dev/auth@^4.1.47": version "4.1.47" @@ -4597,7 +4983,7 @@ arrify@^2.0.0: resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== -asn1.js@5.4.1, asn1.js@^5.0.1: +asn1.js@5.4.1, asn1.js@^5.0.1, asn1.js@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== @@ -4660,6 +5046,22 @@ avvio@^8.2.0: debug "^4.0.0" fastq "^1.6.1" +aws-sdk@^2.922.0: + version "2.1589.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1589.0.tgz#b000aff456e2046c45c0d6a5d1e1281c14a7a7fe" + integrity sha512-Tt3UHH6hoUEAjbCscqvfEAoq9VSTN5iSQO9XSisiiH/QJo8sf+iLCYmfJHM4tVkd92bQH61/xxj9t2Mazwc/WQ== + dependencies: + buffer "4.9.2" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.16.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + util "^0.12.4" + uuid "8.0.0" + xml2js "0.6.2" + aws4fetch@^1.0.17: version "1.0.17" resolved "https://registry.yarnpkg.com/aws4fetch/-/aws4fetch-1.0.17.tgz#0e4f7170f5a7d85ef2981c80209136606eabfada" @@ -4840,25 +5242,7 @@ bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -body-parser@1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" - integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== - dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.11.0" - raw-body "2.5.1" - type-is "~1.6.18" - unpipe "1.0.0" - -body-parser@^1.20.2: +body-parser@1.20.2, body-parser@^1.20.2: version "1.20.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== @@ -5056,15 +5440,7 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== -buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -buffer@^4.3.0: +buffer@4.9.2, buffer@^4.3.0: version "4.9.2" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== @@ -5073,6 +5449,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + bufferutil@^4.0.1: version "4.0.7" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad" @@ -5090,6 +5474,19 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== +bullmq@^5.4.2: + version "5.4.6" + resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-5.4.6.tgz#94f2c6fcfe55eed846de6b8ebe6bfb623d415eca" + integrity sha512-LfxMT+nfbq/Z1cMo5SJH0/P9sp9K2joZsX6ErDB+0IV6aYMqLaiV4p7nKtyWjoE8GoKy2ZdQU6DZJDHVjEhCIw== + dependencies: + cron-parser "^4.6.0" + ioredis "^5.3.2" + msgpackr "^1.10.1" + node-abort-controller "^3.1.1" + semver "^7.5.4" + tslib "^2.0.0" + uuid "^9.0.0" + bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -5325,6 +5722,11 @@ clsx@^1.1.0: resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== +cluster-key-slot@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" + integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -5461,7 +5863,12 @@ cookie@0.4.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== -cookie@0.5.0, cookie@^0.5.0: +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + +cookie@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== @@ -5471,6 +5878,13 @@ cookiejar@^2.1.1: resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== +copy-anything@^3.0.2: + version "3.0.5" + resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-3.0.5.tgz#2d92dce8c498f790fa7ad16b01a1ae5a45b020a0" + integrity sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w== + dependencies: + is-what "^4.1.8" + copyfiles@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.4.1.tgz#d2dcff60aaad1015f09d0b66e7f0f1c5cd3c5da5" @@ -5554,7 +5968,7 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cron-parser@^4.9.0: +cron-parser@^4.6.0, cron-parser@^4.9.0: version "4.9.0" resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-4.9.0.tgz#0340694af3e46a0894978c6f52a6dbb5c0f11ad5" integrity sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q== @@ -5604,6 +6018,11 @@ crypto-js@^4.2.0: resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== +crypto@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037" + integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig== + csstype@^3.0.2: version "3.1.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" @@ -5715,6 +6134,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +denque@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" + integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== + depd@2.0.0, depd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -5806,6 +6230,11 @@ domain-browser@^1.1.1: resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== +dotenv@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" + integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== + dotenv@^16.0.3: version "16.0.3" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" @@ -6245,6 +6674,17 @@ ethereumjs-abi@^0.6.8: bn.js "^4.11.8" ethereumjs-util "^6.0.0" +ethereumjs-util@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.3.tgz#b55d7b64dde3e3e45749e4c41288238edec32d23" + integrity sha512-y+82tEbyASO0K0X1/SRhbJJoAlfcvq8JbrG4a5cjrOks7HS/36efU/0j2flxCPOUM++HFahk33kr/ZxyC4vNuw== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" @@ -6269,7 +6709,33 @@ ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.3, ethereumjs-util@^7.1.5: ethereum-cryptography "^0.1.3" rlp "^2.2.4" -ethers@5, ethers@5.7.2, ethers@^5.7.2: +ethers-aws-kms-signer@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/ethers-aws-kms-signer/-/ethers-aws-kms-signer-1.3.2.tgz#af4e3686f79feff82cca94cf39279fd4c45f7fbc" + integrity sha512-A/xsjCtUGXybpo7imThn4wWcOazHKQi9wnfs9ArPrgp4TmD7dH0bw0FsX71rr9QWsP6RCj0FAGsAxCKNWgEP/g== + dependencies: + asn1.js "^5.4.1" + aws-sdk "^2.922.0" + bn.js "^5.2.0" + debug "^4.3.1" + ethers "^5.4.1" + +ethers-gcp-kms-signer@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/ethers-gcp-kms-signer/-/ethers-gcp-kms-signer-1.1.6.tgz#7510f390d491dc5507b42a54a6d7362b3074f30b" + integrity sha512-mxnDcNM2lgd1IhcbVAQsATYDhJkyK5XbB0Pba3E+9ODNApzwmMzRf8fV58nl95IeRteDY3NTms/6QOapPp4hoQ== + dependencies: + "@google-cloud/kms" "3.0.1" + "@metamask/eth-sig-util" "4.0.0" + asn1.js "5.4.1" + bn.js "5.2.0" + crypto "1.0.1" + dotenv "^10.0.0" + ethereumjs-util "7.1.3" + ethers "5.5.1" + key-encoder "2.0.3" + +ethers@5, ethers@5.7.2, ethers@^5.4.1, ethers@^5.7.2: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -6305,6 +6771,42 @@ ethers@5, ethers@5.7.2, ethers@^5.7.2: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" +ethers@5.5.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.5.1.tgz#d3259a95a42557844aa543906c537106c0406fbf" + integrity sha512-RodEvUFZI+EmFcE6bwkuJqpCYHazdzeR1nMzg+YWQSmQEsNtfl1KHGfp/FWZYl48bI/g7cgBeP2IlPthjiVngw== + dependencies: + "@ethersproject/abi" "5.5.0" + "@ethersproject/abstract-provider" "5.5.1" + "@ethersproject/abstract-signer" "5.5.0" + "@ethersproject/address" "5.5.0" + "@ethersproject/base64" "5.5.0" + "@ethersproject/basex" "5.5.0" + "@ethersproject/bignumber" "5.5.0" + "@ethersproject/bytes" "5.5.0" + "@ethersproject/constants" "5.5.0" + "@ethersproject/contracts" "5.5.0" + "@ethersproject/hash" "5.5.0" + "@ethersproject/hdnode" "5.5.0" + "@ethersproject/json-wallets" "5.5.0" + "@ethersproject/keccak256" "5.5.0" + "@ethersproject/logger" "5.5.0" + "@ethersproject/networks" "5.5.0" + "@ethersproject/pbkdf2" "5.5.0" + "@ethersproject/properties" "5.5.0" + "@ethersproject/providers" "5.5.0" + "@ethersproject/random" "5.5.0" + "@ethersproject/rlp" "5.5.0" + "@ethersproject/sha2" "5.5.0" + "@ethersproject/signing-key" "5.5.0" + "@ethersproject/solidity" "5.5.0" + "@ethersproject/strings" "5.5.0" + "@ethersproject/transactions" "5.5.0" + "@ethersproject/units" "5.5.0" + "@ethersproject/wallet" "5.5.0" + "@ethersproject/web" "5.5.0" + "@ethersproject/wordlists" "5.5.0" + ethjs-unit@0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" @@ -6341,6 +6843,11 @@ eventemitter3@^5.0.1: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== +events@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== + events@3.3.0, events@^3.0.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -6406,16 +6913,16 @@ explain-error@^1.0.4: integrity sha512-/wSgNMxFusiYRy1rd19LT2SQlIXDppHpumpWo06wxjflD1OYxDLbl6rMVw+U3bxD5Nuhex4TKqv9Aem4D0lVzQ== express@^4.17.1: - version "4.18.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" - integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + version "4.19.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" + integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.1" + body-parser "1.20.2" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.5.0" + cookie "0.6.0" cookie-signature "1.0.6" debug "2.6.9" depd "2.0.0" @@ -6685,9 +7192,9 @@ flatted@^3.1.0: integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== follow-redirects@^1.14.0, follow-redirects@^1.14.9: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== for-each@^0.3.3: version "0.3.3" @@ -7248,6 +7755,11 @@ idb-keyval@^6.2.1: resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.2.1.tgz#94516d625346d16f56f3b33855da11bfded2db33" integrity sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg== +ieee754@1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + ieee754@^1.1.4, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -7319,6 +7831,21 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" +ioredis@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.3.2.tgz#9139f596f62fc9c72d873353ac5395bcf05709f7" + integrity sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA== + dependencies: + "@ioredis/commands" "^1.1.1" + cluster-key-slot "^1.1.0" + debug "^4.3.4" + denque "^2.1.0" + lodash.defaults "^4.2.0" + lodash.isarguments "^3.1.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + standard-as-callback "^2.1.0" + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -7456,6 +7983,11 @@ is-typedarray@^1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== +is-what@^4.1.8: + version "4.1.16" + resolved "https://registry.yarnpkg.com/is-what/-/is-what-4.1.16.tgz#1ad860a19da8b4895ad5495da3182ce2acdd7a6f" + integrity sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A== + is-wsl@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" @@ -7942,6 +8474,11 @@ jiti@^1.21.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== +jmespath@0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" + integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== + joycon@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" @@ -8313,6 +8850,16 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== + +lodash.isarguments@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== + lodash.isequal@4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" @@ -8572,12 +9119,10 @@ minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -mipd@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/mipd/-/mipd-0.0.5.tgz#367ee796531c23f0631f129038700b1406663aec" - integrity sha512-gbKA784D2WKb5H/GtqEv+Ofd1S9Zj+Z/PGDIl1u1QAbswkxD28BQ5bSXQxkeBzPBABg1iDSbiwGG1XqlOxRspA== - dependencies: - viem "^1.1.4" +mipd@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mipd/-/mipd-0.0.7.tgz#bb5559e21fa18dc3d9fe1c08902ef14b7ce32fd9" + integrity sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg== mkdirp@^1.0.4: version "1.0.4" @@ -8633,6 +9178,27 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +msgpackr-extract@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz#e05ec1bb4453ddf020551bcd5daaf0092a2c279d" + integrity sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A== + dependencies: + node-gyp-build-optional-packages "5.0.7" + optionalDependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" + +msgpackr@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.10.1.tgz#51953bb4ce4f3494f0c4af3f484f01cfbb306555" + integrity sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ== + optionalDependencies: + msgpackr-extract "^3.0.2" + multibase@^4.0.1, multibase@^4.0.2: version "4.0.6" resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.6.tgz#6e624341483d6123ca1ede956208cb821b440559" @@ -8692,6 +9258,11 @@ next-tick@^1.1.0: resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== +node-abort-controller@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" + integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== + node-addon-api@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" @@ -8733,6 +9304,11 @@ node-forge@^1.3.1: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== +node-gyp-build-optional-packages@5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz#5d2632bbde0ab2f6e22f1bbac2199b07244ae0b3" + integrity sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w== + node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: version "4.6.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" @@ -9514,6 +10090,11 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== + punycode@^1.2.4, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" @@ -9584,6 +10165,11 @@ querystring-es3@^0.2.0: resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -9619,16 +10205,6 @@ range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - raw-body@2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" @@ -9743,6 +10319,18 @@ rechoir@^0.8.0: dependencies: resolve "^1.20.0" +redis-errors@^1.0.0, redis-errors@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" + integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w== + +redis-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" + integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A== + dependencies: + redis-errors "^1.0.0" + regenerator-runtime@^0.13.11: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" @@ -9927,6 +10515,16 @@ safe-stable-stringify@^2.1.0, safe-stable-stringify@^2.3.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sax@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" + integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA== + +sax@>=0.6.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + scrypt-js@3.0.1, scrypt-js@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" @@ -10182,6 +10780,11 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +standard-as-callback@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" + integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -10314,6 +10917,13 @@ stylis@4.2.0: resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== +superjson@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/superjson/-/superjson-2.2.1.tgz#9377a7fa80fedb10c851c9dbffd942d4bcf79733" + integrity sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA== + dependencies: + copy-anything "^3.0.2" + superstruct@^0.14.2: version "0.14.2" resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b" @@ -10379,10 +10989,10 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -thirdweb@^5.0.0-beta-ca68bc77e74f594360b4da1e9e77793b66cfb12a-20240323045412: - version "5.0.0-beta-ca68bc77e74f594360b4da1e9e77793b66cfb12a-20240323045412" - resolved "https://registry.yarnpkg.com/thirdweb/-/thirdweb-5.0.0-beta-ca68bc77e74f594360b4da1e9e77793b66cfb12a-20240323045412.tgz#dd94a5f80b3735d76227dac17c7e44e608653140" - integrity sha512-CJnhdZq/I48V8iHS6P3gccrjv7VyDj4mAIfaCPwFpauz+l9p+PvuokS6e+BnnHWB1FE1+iRGnrGy4J4PGpDa5g== +thirdweb@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/thirdweb/-/thirdweb-5.1.0.tgz#1df31e2a5c3f39f2926b1a27c04fecbc34e21213" + integrity sha512-yZwFcfDimePsdJ1HmYT90BBeeCi5/N8oAqUDRKaf2tI12i0Y7ajgpbAQx4Dd/0LJ+hQ2ovOqXaPTiSEXshF5hg== dependencies: "@coinbase/wallet-sdk" "3.7.2" "@emotion/react" "11.11.4" @@ -10394,15 +11004,15 @@ thirdweb@^5.0.0-beta-ca68bc77e74f594360b4da1e9e77793b66cfb12a-20240323045412: "@radix-ui/react-focus-scope" "1.0.4" "@radix-ui/react-icons" "1.3.0" "@radix-ui/react-tooltip" "1.0.7" - "@tanstack/react-query" "5.28.4" + "@tanstack/react-query" "5.28.9" "@walletconnect/ethereum-provider" "2.11.3" abitype "1.0.0" fast-text-encoding "^1.0.6" fuse.js "7.0.0" - mipd "0.0.5" + mipd "0.0.7" node-libs-browser "2.2.1" uqr "0.1.2" - viem "2.8.14" + viem "2.9.6" thread-stream@^0.15.1: version "0.15.2" @@ -10768,6 +11378,14 @@ url-set-query@^1.0.0: resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" integrity sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg== +url@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" + integrity sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ== + dependencies: + punycode "1.3.2" + querystring "0.2.0" + url@^0.11.0: version "0.11.3" resolved "https://registry.yarnpkg.com/url/-/url-0.11.3.tgz#6f495f4b935de40ce4a0a52faee8954244f3d3ad" @@ -10843,6 +11461,11 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +uuid@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" + integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== + uuid@8.3.2, uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" @@ -10853,7 +11476,7 @@ uuid@9.0.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== -uuid@^9.0.1: +uuid@^9.0.0, uuid@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== @@ -10903,10 +11526,10 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -viem@2.8.14: - version "2.8.14" - resolved "https://registry.yarnpkg.com/viem/-/viem-2.8.14.tgz#25b7e9488f494b0b20124fdeefd96b65d851850a" - integrity sha512-K5u9OoyPQ7W8VPa6xY2m7oazuhemp0xuK9Ur8AkaXHtcusism9keTXDDaCw6WWFK3YR9HSojHJOtuVQqvRz0ug== +viem@2.9.6: + version "2.9.6" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.9.6.tgz#66819684e878b6ea659ad9c49a0a864512859190" + integrity sha512-VVFWjGQei2wnWTvAB/xrIf22m6flCwxeBr8LnwejXMTSSi1EORWEswrw2lfKTmw3TlRPSG4uSiQMa/d0l0DKRg== dependencies: "@adraffy/ens-normalize" "1.10.0" "@noble/curves" "1.2.0" @@ -10917,20 +11540,6 @@ viem@2.8.14: isows "1.0.3" ws "8.13.0" -viem@^1.1.4: - version "1.21.4" - resolved "https://registry.yarnpkg.com/viem/-/viem-1.21.4.tgz#883760e9222540a5a7e0339809202b45fe6a842d" - integrity sha512-BNVYdSaUjeS2zKQgPs+49e5JKocfo60Ib2yiXOWBT6LuVxY1I/6fFX3waEtpXvL1Xn4qu+BVitVtMh9lyThyhQ== - dependencies: - "@adraffy/ens-normalize" "1.10.0" - "@noble/curves" "1.2.0" - "@noble/hashes" "1.3.2" - "@scure/bip32" "1.3.2" - "@scure/bip39" "1.2.1" - abitype "0.9.8" - isows "1.0.3" - ws "8.13.0" - viem@^1.14.0: version "1.14.0" resolved "https://registry.yarnpkg.com/viem/-/viem-1.14.0.tgz#e4b305c4cce500e04a66b951c01856d7b04ab403" @@ -11320,6 +11929,19 @@ xhr@^2.0.4: parse-headers "^2.0.0" xtend "^4.0.0" +xml2js@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" + integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + xmlcreate@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.4.tgz#0c5ab0f99cdd02a81065fa9cd8f8ae87624889be"