From d25a5ed9022f93316863da3bd8459d7c78a40bca Mon Sep 17 00:00:00 2001 From: Phillip Ho Date: Thu, 6 Mar 2025 10:07:22 +0800 Subject: [PATCH 1/3] chore: Create contract subscription for existing webhook --- .../add-contract-subscription.ts | 55 +++++++++++-------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/server/routes/contract/subscriptions/add-contract-subscription.ts b/src/server/routes/contract/subscriptions/add-contract-subscription.ts index 57545eda0..7d486d5dd 100644 --- a/src/server/routes/contract/subscriptions/add-contract-subscription.ts +++ b/src/server/routes/contract/subscriptions/add-contract-subscription.ts @@ -2,7 +2,7 @@ import { type Static, Type } from "@sinclair/typebox"; import type { FastifyInstance } from "fastify"; import { StatusCodes } from "http-status-codes"; import { getContract } from "thirdweb"; -import { isContractDeployed } from "thirdweb/utils"; +import { isContractDeployed, shortenAddress } from "thirdweb/utils"; import { upsertChainIndexer } from "../../../../shared/db/chain-indexers/upsert-chain-indexer"; import { createContractSubscription } from "../../../../shared/db/contract-subscriptions/create-contract-subscription"; import { getContractSubscriptionsUniqueChainIds } from "../../../../shared/db/contract-subscriptions/get-contract-subscriptions"; @@ -21,6 +21,8 @@ import { import { standardResponseSchema } from "../../../schemas/shared-api-schemas"; import { getChainIdFromChain } from "../../../utils/chain"; import { isValidWebhookUrl } from "../../../utils/validator"; +import { getWebhook } from "../../../../shared/db/webhooks/get-webhook"; +import { Webhooks } from "@prisma/client"; const bodySchema = Type.Object({ chain: chainIdOrSlugSchema, @@ -28,9 +30,17 @@ const bodySchema = Type.Object({ ...AddressSchema, description: "The address for the contract.", }, + webhookId: Type.Optional( + Type.Number({ + description: + "The ID of an existing webhook to use for this contract subscription. Either `webhookId` or `webhookUrl` must be provided.", + examples: [1], + }), + ), webhookUrl: Type.Optional( Type.String({ - description: "Webhook URL", + description: + "Creates a new webhook to call when new onchain data is detected. Either `webhookId` or `webhookUrl` must be provided.", examples: ["https://example.com/webhook"], }), ), @@ -91,6 +101,7 @@ export async function addContractSubscription(fastify: FastifyInstance) { const { chain, contractAddress, + webhookId, webhookUrl, processEventLogs, filterEvents = [], @@ -124,6 +135,25 @@ export async function addContractSubscription(fastify: FastifyInstance) { ); } + // Get an existing webhook or create a new one. + let webhook: Webhooks | null = null; + if (webhookId) { + webhook = await getWebhook(webhookId); + } else if (webhookUrl && isValidWebhookUrl(webhookUrl)) { + webhook = await insertWebhook({ + eventType: WebhooksEventTypes.CONTRACT_SUBSCRIPTION, + name: `(Generated) Subscription for ${shortenAddress(contractAddress)}`, + url: webhookUrl, + }); + } + if (!webhook) { + throw createCustomError( + 'Failed to get or create webhook for contract subscription. Make sure you provide an valid "webhookId" or "webhookUrl".', + StatusCodes.INTERNAL_SERVER_ERROR, + "INTERNAL_SERVER_ERROR", + ); + } + // If not currently indexed, upsert the latest block number. const subscribedChainIds = await getContractSubscriptionsUniqueChainIds(); if (!subscribedChainIds.includes(chainId)) { @@ -137,30 +167,11 @@ export async function addContractSubscription(fastify: FastifyInstance) { } } - // Create the webhook (if provided). - let webhookId: number | undefined; - if (webhookUrl) { - if (!isValidWebhookUrl(webhookUrl)) { - throw createCustomError( - "Invalid webhook URL. Make sure it starts with 'https://'.", - StatusCodes.BAD_REQUEST, - "BAD_REQUEST", - ); - } - - const webhook = await insertWebhook({ - eventType: WebhooksEventTypes.CONTRACT_SUBSCRIPTION, - name: "(Auto-generated)", - url: webhookUrl, - }); - webhookId = webhook.id; - } - // Create the contract subscription. const contractSubscription = await createContractSubscription({ chainId, contractAddress: contractAddress.toLowerCase(), - webhookId, + webhookId: webhook.id, processEventLogs, filterEvents, processTransactionReceipts, From 057fbdd66b89f27eef835ef7f9e97179573c25b5 Mon Sep 17 00:00:00 2001 From: Phillip Ho Date: Thu, 6 Mar 2025 10:08:42 +0800 Subject: [PATCH 2/3] make 400 --- .../contract/subscriptions/add-contract-subscription.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/routes/contract/subscriptions/add-contract-subscription.ts b/src/server/routes/contract/subscriptions/add-contract-subscription.ts index 7d486d5dd..57be966f9 100644 --- a/src/server/routes/contract/subscriptions/add-contract-subscription.ts +++ b/src/server/routes/contract/subscriptions/add-contract-subscription.ts @@ -149,8 +149,8 @@ export async function addContractSubscription(fastify: FastifyInstance) { if (!webhook) { throw createCustomError( 'Failed to get or create webhook for contract subscription. Make sure you provide an valid "webhookId" or "webhookUrl".', - StatusCodes.INTERNAL_SERVER_ERROR, - "INTERNAL_SERVER_ERROR", + StatusCodes.BAD_REQUEST, + "INVALID_WEBHOOK", ); } From a439d9d05aed248e7d957ffc38652b041b6d44c8 Mon Sep 17 00:00:00 2001 From: Phillip Ho Date: Thu, 6 Mar 2025 10:27:23 +0800 Subject: [PATCH 3/3] linter --- .../routes/contract/subscriptions/add-contract-subscription.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/routes/contract/subscriptions/add-contract-subscription.ts b/src/server/routes/contract/subscriptions/add-contract-subscription.ts index 57be966f9..e46c5c429 100644 --- a/src/server/routes/contract/subscriptions/add-contract-subscription.ts +++ b/src/server/routes/contract/subscriptions/add-contract-subscription.ts @@ -22,7 +22,7 @@ import { standardResponseSchema } from "../../../schemas/shared-api-schemas"; import { getChainIdFromChain } from "../../../utils/chain"; import { isValidWebhookUrl } from "../../../utils/validator"; import { getWebhook } from "../../../../shared/db/webhooks/get-webhook"; -import { Webhooks } from "@prisma/client"; +import type { Webhooks } from "@prisma/client"; const bodySchema = Type.Object({ chain: chainIdOrSlugSchema,