@@ -3,6 +3,7 @@ import type { FastifyInstance } from "fastify";
3
3
import { StatusCodes } from "http-status-codes" ;
4
4
import { createBalanceSubscription } from "../../../shared/db/balance-subscriptions/create-balance-subscription" ;
5
5
import { insertWebhook } from "../../../shared/db/webhooks/create-webhook" ;
6
+ import { getWebhook } from "../../../shared/db/webhooks/get-webhook" ;
6
7
import { balanceSubscriptionConfigSchema } from "../../../shared/schemas/balance-subscription-config" ;
7
8
import { WebhooksEventTypes } from "../../../shared/schemas/webhooks" ;
8
9
import { createCustomError } from "../../middleware/error" ;
@@ -13,19 +14,36 @@ import { getChainIdFromChain } from "../../utils/chain";
13
14
import { isValidWebhookUrl } from "../../utils/validator" ;
14
15
import { balanceSubscriptionSchema , toBalanceSubscriptionSchema } from "../../schemas/balance-subscription" ;
15
16
16
- const requestBodySchema = Type . Object ( {
17
- chain : chainIdOrSlugSchema ,
18
- contractAddress : Type . Optional ( AddressSchema ) ,
19
- walletAddress : AddressSchema ,
20
- config : balanceSubscriptionConfigSchema ,
21
- webhookUrl : Type . Optional (
17
+ const webhookUrlSchema = Type . Object ( {
18
+ webhookUrl : Type . String ( {
19
+ description : "Webhook URL to create a new webhook" ,
20
+ examples : [ "https://example.com/webhook" ] ,
21
+ } ) ,
22
+ webhookLabel : Type . Optional (
22
23
Type . String ( {
23
- description : "Webhook URL" ,
24
- examples : [ "https://example.com/webhook" ] ,
24
+ description : "Optional label for the webhook when creating a new one" ,
25
+ examples : [ "My Balance Subscription Webhook" ] ,
26
+ minLength : 3 ,
25
27
} ) ,
26
28
) ,
27
29
} ) ;
28
30
31
+ const webhookIdSchema = Type . Object ( {
32
+ webhookId : Type . Integer ( {
33
+ description : "ID of an existing webhook to use" ,
34
+ } ) ,
35
+ } ) ;
36
+
37
+ const requestBodySchema = Type . Intersect ( [
38
+ Type . Object ( {
39
+ chain : chainIdOrSlugSchema ,
40
+ contractAddress : Type . Optional ( AddressSchema ) ,
41
+ walletAddress : AddressSchema ,
42
+ config : balanceSubscriptionConfigSchema ,
43
+ } ) ,
44
+ Type . Union ( [ webhookUrlSchema , webhookIdSchema ] ) ,
45
+ ] ) ;
46
+
29
47
const responseSchema = Type . Object ( {
30
48
result : balanceSubscriptionSchema ,
31
49
} ) ;
@@ -49,13 +67,15 @@ export async function addBalanceSubscriptionRoute(fastify: FastifyInstance) {
49
67
} ,
50
68
} ,
51
69
handler : async ( request , reply ) => {
52
- const { chain, contractAddress, walletAddress, config, webhookUrl } = request . body ;
53
-
70
+ const { chain, contractAddress, walletAddress, config } = request . body ;
54
71
const chainId = await getChainIdFromChain ( chain ) ;
55
72
56
- // Create the webhook (if provided).
57
- let webhookId : number | undefined ;
58
- if ( webhookUrl ) {
73
+ let finalWebhookId : number | undefined ;
74
+
75
+ // Handle webhook creation or validation
76
+ if ( "webhookUrl" in request . body ) {
77
+ // Create new webhook
78
+ const { webhookUrl, webhookLabel } = request . body ;
59
79
if ( ! isValidWebhookUrl ( webhookUrl ) ) {
60
80
throw createCustomError (
61
81
"Invalid webhook URL. Make sure it starts with 'https://'." ,
@@ -66,19 +86,45 @@ export async function addBalanceSubscriptionRoute(fastify: FastifyInstance) {
66
86
67
87
const webhook = await insertWebhook ( {
68
88
eventType : WebhooksEventTypes . BALANCE_SUBSCRIPTION ,
69
- name : "(Auto-generated)" ,
89
+ name : webhookLabel || "(Auto-generated)" ,
70
90
url : webhookUrl ,
71
91
} ) ;
72
- webhookId = webhook . id ;
92
+ finalWebhookId = webhook . id ;
93
+ } else {
94
+ // Validate existing webhook
95
+ const { webhookId } = request . body ;
96
+ const webhook = await getWebhook ( webhookId ) ;
97
+ if ( ! webhook ) {
98
+ throw createCustomError (
99
+ `Webhook with ID ${ webhookId } not found.` ,
100
+ StatusCodes . NOT_FOUND ,
101
+ "NOT_FOUND" ,
102
+ ) ;
103
+ }
104
+ if ( webhook . eventType !== WebhooksEventTypes . BALANCE_SUBSCRIPTION ) {
105
+ throw createCustomError (
106
+ `Webhook with ID ${ webhookId } has incorrect event type. Expected '${ WebhooksEventTypes . BALANCE_SUBSCRIPTION } ' but got '${ webhook . eventType } '.` ,
107
+ StatusCodes . BAD_REQUEST ,
108
+ "BAD_REQUEST" ,
109
+ ) ;
110
+ }
111
+ if ( webhook . revokedAt ) {
112
+ throw createCustomError (
113
+ `Webhook with ID ${ webhookId } has been revoked.` ,
114
+ StatusCodes . BAD_REQUEST ,
115
+ "BAD_REQUEST" ,
116
+ ) ;
117
+ }
118
+ finalWebhookId = webhookId ;
73
119
}
74
120
75
- // Create the balance subscription.
121
+ // Create the balance subscription
76
122
const balanceSubscription = await createBalanceSubscription ( {
77
123
chainId : chainId . toString ( ) ,
78
124
contractAddress : contractAddress ?. toLowerCase ( ) ,
79
125
walletAddress : walletAddress . toLowerCase ( ) ,
80
126
config,
81
- webhookId,
127
+ webhookId : finalWebhookId ,
82
128
} ) ;
83
129
84
130
reply . status ( StatusCodes . OK ) . send ( {
0 commit comments