@settlemint/sdk-portal 2.3.2 → 2.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,252 @@
1
+ import { AbstractSetupSchema, FragmentOf, ResultOf, VariablesOf, initGraphQLTada, readFragment } from "gql.tada";
2
+ import { GraphQLClient } from "graphql-request";
3
+ import { z } from "zod/v4";
4
+ import * as graphql_ws0 from "graphql-ws";
5
+ import { Address, Hex, TransactionReceipt as TransactionReceipt$1 } from "viem";
6
+
7
+ //#region src/utils/websocket-client.d.ts
8
+ /**
9
+ * Options for the GraphQL WebSocket client
10
+ */
11
+ /**
12
+ * Options for the GraphQL WebSocket client
13
+ */
14
+ interface WebsocketClientOptions {
15
+ /**
16
+ * The GraphQL endpoint URL for the Portal API
17
+ */
18
+ portalGraphqlEndpoint: string;
19
+ /**
20
+ * The access token for authentication with the Portal API
21
+ */
22
+ accessToken?: string;
23
+ }
24
+ /**
25
+ * Creates a GraphQL WebSocket client for the Portal API
26
+ *
27
+ * @param {WebsocketClientOptions} options - The options for the client
28
+ * @returns {Client} The GraphQL WebSocket client
29
+ * @example
30
+ * import { getWebsocketClient } from "@settlemint/sdk-portal";
31
+ *
32
+ * const client = getWebsocketClient({
33
+ * portalGraphqlEndpoint: "https://portal.settlemint.com/graphql",
34
+ * accessToken: "your-access-token",
35
+ * });
36
+ */
37
+ declare function getWebsocketClient({
38
+ portalGraphqlEndpoint,
39
+ accessToken
40
+ }: WebsocketClientOptions): graphql_ws0.Client;
41
+
42
+ //#endregion
43
+ //#region src/utils/wait-for-transaction-receipt.d.ts
44
+ /**
45
+ * Represents an event emitted during a transaction execution
46
+ */
47
+ interface TransactionEvent {
48
+ /** The name of the event that was emitted */
49
+ eventName: string;
50
+ /** The arguments emitted by the event */
51
+ args: Record<string, unknown>;
52
+ /** Indexed event parameters used for filtering and searching */
53
+ topics: Hex[];
54
+ }
55
+ /**
56
+ * Represents the structure of a blockchain transaction receipt
57
+ */
58
+ interface TransactionReceipt extends TransactionReceipt$1<string, number, "Success" | "Reverted"> {
59
+ /** The raw reason for transaction reversion, if applicable */
60
+ revertReason: string;
61
+ /** Human-readable version of the revert reason */
62
+ revertReasonDecoded: string;
63
+ /** Array of events emitted during the transaction */
64
+ events: TransactionEvent[];
65
+ /** The address of the contract deployed in the transaction */
66
+ contractAddress: Address;
67
+ }
68
+ /**
69
+ * Represents the structure of a blockchain transaction with its receipt
70
+ */
71
+ interface Transaction {
72
+ receipt: TransactionReceipt;
73
+ /** The hash of the transaction (duplicate of receipt.transactionHash) */
74
+ transactionHash: string;
75
+ /** The sender address (duplicate of receipt.from) */
76
+ from: string;
77
+ /** Timestamp when the transaction was created */
78
+ createdAt: string;
79
+ /** The contract address involved in the transaction */
80
+ address: string;
81
+ /** The name of the function called in the transaction */
82
+ functionName: string;
83
+ /** Whether the transaction is a contract deployment */
84
+ isContract: boolean;
85
+ }
86
+ /**
87
+ * Options for waiting for a transaction receipt
88
+ */
89
+ interface WaitForTransactionReceiptOptions extends WebsocketClientOptions {
90
+ /** Optional timeout in milliseconds before the operation fails */
91
+ timeout?: number;
92
+ }
93
+ /**
94
+ * Waits for a blockchain transaction receipt by subscribing to transaction updates via GraphQL.
95
+ * This function polls until the transaction is confirmed or the timeout is reached.
96
+ *
97
+ * @param transactionHash - The hash of the transaction to wait for
98
+ * @param options - Configuration options for the waiting process
99
+ * @returns The transaction details including receipt information when the transaction is confirmed
100
+ * @throws Error if the transaction receipt cannot be retrieved within the specified timeout
101
+ *
102
+ * @example
103
+ * import { waitForTransactionReceipt } from "@settlemint/sdk-portal";
104
+ *
105
+ * const transaction = await waitForTransactionReceipt("0x123...", {
106
+ * portalGraphqlEndpoint: "https://example.settlemint.com/graphql",
107
+ * accessToken: "your-access-token",
108
+ * timeout: 30000 // 30 seconds timeout
109
+ * });
110
+ */
111
+ declare function waitForTransactionReceipt(transactionHash: string, options: WaitForTransactionReceiptOptions): Promise<Transaction>;
112
+
113
+ //#endregion
114
+ //#region src/utils/wallet-verification-challenge.d.ts
115
+ /**
116
+ * Custom error class for challenge-related errors
117
+ */
118
+ declare class ChallengeError extends Error {
119
+ readonly code: string;
120
+ constructor(message: string, code: string);
121
+ }
122
+ /**
123
+ * Options for handling a wallet verification challenge
124
+ */
125
+ interface HandleWalletVerificationChallengeOptions<Setup extends AbstractSetupSchema> {
126
+ /** The portal client instance */
127
+ portalClient: GraphQLClient;
128
+ /** The GraphQL query builder */
129
+ portalGraphql: initGraphQLTada<Setup>;
130
+ /** The ID of the verification challenge */
131
+ verificationId: string;
132
+ /** The wallet address to verify */
133
+ userWalletAddress: Address;
134
+ /** The verification code provided by the user */
135
+ code: string | number;
136
+ /** The type of verification being performed */
137
+ verificationType: "otp" | "secret-code" | "pincode";
138
+ }
139
+ /**
140
+ * Handles a wallet verification challenge by generating an appropriate response
141
+ *
142
+ * @param options - The options for handling the wallet verification challenge
143
+ * @returns Promise resolving to an object containing the challenge response and optionally the verification ID
144
+ * @throws {ChallengeError} If the challenge cannot be created or is invalid
145
+ * @example
146
+ * import { createPortalClient } from "@settlemint/sdk-portal";
147
+ * import { handleWalletVerificationChallenge } from "@settlemint/sdk-portal";
148
+ *
149
+ * const { client, graphql } = createPortalClient({
150
+ * instance: "https://portal.example.com/graphql",
151
+ * accessToken: "your-access-token"
152
+ * });
153
+ *
154
+ * const result = await handleWalletVerificationChallenge({
155
+ * portalClient: client,
156
+ * portalGraphql: graphql,
157
+ * verificationId: "verification-123",
158
+ * userWalletAddress: "0x123...",
159
+ * code: "123456",
160
+ * verificationType: "otp"
161
+ * });
162
+ */
163
+ declare function handleWalletVerificationChallenge<const Setup extends AbstractSetupSchema>({
164
+ portalClient,
165
+ portalGraphql,
166
+ verificationId,
167
+ userWalletAddress,
168
+ code,
169
+ verificationType
170
+ }: HandleWalletVerificationChallengeOptions<Setup>): Promise<{
171
+ challengeResponse: string;
172
+ verificationId?: string;
173
+ }>;
174
+
175
+ //#endregion
176
+ //#region src/portal.d.ts
177
+ /**
178
+ * Configuration options for the GraphQL client, excluding 'url' and 'exchanges'.
179
+ */
180
+ type RequestConfig = ConstructorParameters<typeof GraphQLClient>[1];
181
+ /**
182
+ * Schema for validating Portal client configuration options.
183
+ */
184
+ declare const ClientOptionsSchema: z.ZodObject<{
185
+ instance: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
186
+ accessToken: z.ZodOptional<z.ZodString>;
187
+ cache: z.ZodOptional<z.ZodEnum<{
188
+ default: "default";
189
+ "force-cache": "force-cache";
190
+ "no-cache": "no-cache";
191
+ "no-store": "no-store";
192
+ "only-if-cached": "only-if-cached";
193
+ reload: "reload";
194
+ }>>;
195
+ }, z.core.$strip>;
196
+ /**
197
+ * Type representing the validated client options.
198
+ */
199
+ type ClientOptions = z.infer<typeof ClientOptionsSchema>;
200
+ /**
201
+ * Creates a Portal GraphQL client with the provided configuration.
202
+ *
203
+ * @param options - Configuration options for the Portal client
204
+ * @param clientOptions - Additional GraphQL client configuration options
205
+ * @returns An object containing the configured GraphQL client and graphql helper function
206
+ * @throws If the provided options fail validation
207
+ *
208
+ * @example
209
+ * import { createPortalClient } from "@settlemint/sdk-portal";
210
+ * import { loadEnv } from "@settlemint/sdk-utils/environment";
211
+ * import { createLogger, requestLogger } from "@settlemint/sdk-utils/logging";
212
+ * import type { introspection } from "@schemas/portal-env";
213
+ *
214
+ * const env = await loadEnv(false, false);
215
+ * const logger = createLogger();
216
+ *
217
+ * const { client: portalClient, graphql: portalGraphql } = createPortalClient<{
218
+ * introspection: introspection;
219
+ * disableMasking: true;
220
+ * scalars: {
221
+ * // Change unknown to the type you are using to store metadata
222
+ * JSON: unknown;
223
+ * };
224
+ * }>(
225
+ * {
226
+ * instance: env.SETTLEMINT_PORTAL_GRAPHQL_ENDPOINT!,
227
+ * accessToken: env.SETTLEMINT_ACCESS_TOKEN!,
228
+ * },
229
+ * {
230
+ * fetch: requestLogger(logger, "portal", fetch) as typeof fetch,
231
+ * },
232
+ * );
233
+ *
234
+ * // Making GraphQL queries
235
+ * const query = portalGraphql(`
236
+ * query GetPendingTransactions {
237
+ * getPendingTransactions {
238
+ * count
239
+ * }
240
+ * }
241
+ * `);
242
+ *
243
+ * const result = await portalClient.request(query);
244
+ */
245
+ declare function createPortalClient<const Setup extends AbstractSetupSchema>(options: ClientOptions, clientOptions?: RequestConfig): {
246
+ client: GraphQLClient;
247
+ graphql: initGraphQLTada<Setup>;
248
+ };
249
+
250
+ //#endregion
251
+ export { ClientOptions, ClientOptionsSchema, FragmentOf, HandleWalletVerificationChallengeOptions, RequestConfig, ResultOf, Transaction, TransactionEvent, TransactionReceipt, VariablesOf, WaitForTransactionReceiptOptions, WebsocketClientOptions, createPortalClient, getWebsocketClient, handleWalletVerificationChallenge, readFragment, waitForTransactionReceipt };
252
+ //# sourceMappingURL=portal.d.ts.map
@@ -0,0 +1,294 @@
1
+ import { appendHeaders } from "@settlemint/sdk-utils/http";
2
+ import { ensureServer } from "@settlemint/sdk-utils/runtime";
3
+ import { ApplicationAccessTokenSchema, UrlOrPathSchema, validate } from "@settlemint/sdk-utils/validation";
4
+ import { initGraphQLTada, readFragment } from "gql.tada";
5
+ import { GraphQLClient } from "graphql-request";
6
+ import { z } from "zod/v4";
7
+ import { createClient } from "graphql-ws";
8
+ import { createHash } from "node:crypto";
9
+
10
+ //#region src/utils/websocket-client.ts
11
+ /**
12
+ * Creates a GraphQL WebSocket client for the Portal API
13
+ *
14
+ * @param {WebsocketClientOptions} options - The options for the client
15
+ * @returns {Client} The GraphQL WebSocket client
16
+ * @example
17
+ * import { getWebsocketClient } from "@settlemint/sdk-portal";
18
+ *
19
+ * const client = getWebsocketClient({
20
+ * portalGraphqlEndpoint: "https://portal.settlemint.com/graphql",
21
+ * accessToken: "your-access-token",
22
+ * });
23
+ */
24
+ function getWebsocketClient({ portalGraphqlEndpoint, accessToken }) {
25
+ if (!portalGraphqlEndpoint) {
26
+ throw new Error("portalGraphqlEndpoint is required");
27
+ }
28
+ const graphqlEndpoint = setWsProtocol(new URL(portalGraphqlEndpoint));
29
+ return createClient({ url: `${graphqlEndpoint.protocol}//${graphqlEndpoint.host}/${accessToken}${graphqlEndpoint.pathname}${graphqlEndpoint.search}` });
30
+ }
31
+ function setWsProtocol(url) {
32
+ if (url.protocol === "ws:" || url.protocol === "wss:") {
33
+ return url;
34
+ }
35
+ if (url.protocol === "http:") {
36
+ url.protocol = "ws:";
37
+ } else {
38
+ url.protocol = "wss:";
39
+ }
40
+ return url;
41
+ }
42
+
43
+ //#endregion
44
+ //#region src/utils/wait-for-transaction-receipt.ts
45
+ /**
46
+ * Waits for a blockchain transaction receipt by subscribing to transaction updates via GraphQL.
47
+ * This function polls until the transaction is confirmed or the timeout is reached.
48
+ *
49
+ * @param transactionHash - The hash of the transaction to wait for
50
+ * @param options - Configuration options for the waiting process
51
+ * @returns The transaction details including receipt information when the transaction is confirmed
52
+ * @throws Error if the transaction receipt cannot be retrieved within the specified timeout
53
+ *
54
+ * @example
55
+ * import { waitForTransactionReceipt } from "@settlemint/sdk-portal";
56
+ *
57
+ * const transaction = await waitForTransactionReceipt("0x123...", {
58
+ * portalGraphqlEndpoint: "https://example.settlemint.com/graphql",
59
+ * accessToken: "your-access-token",
60
+ * timeout: 30000 // 30 seconds timeout
61
+ * });
62
+ */
63
+ async function waitForTransactionReceipt(transactionHash, options) {
64
+ const wsClient = getWebsocketClient(options);
65
+ const subscription = wsClient.iterate({
66
+ query: `subscription getTransaction($transactionHash: String!) {
67
+ getTransaction(transactionHash: $transactionHash) {
68
+ receipt {
69
+ transactionHash
70
+ to
71
+ status
72
+ from
73
+ type
74
+ revertReason
75
+ revertReasonDecoded
76
+ logs
77
+ events
78
+ contractAddress
79
+ }
80
+ transactionHash
81
+ from
82
+ createdAt
83
+ address
84
+ functionName
85
+ isContract
86
+ }
87
+ }`,
88
+ variables: { transactionHash }
89
+ });
90
+ const promises = [getTransactionFromSubscription(subscription)];
91
+ if (options.timeout) {
92
+ promises.push(createTimeoutPromise(options.timeout));
93
+ }
94
+ return Promise.race(promises);
95
+ }
96
+ function createTimeoutPromise(timeout) {
97
+ return new Promise((_, reject) => {
98
+ setTimeout(() => reject(new Error("Transaction receipt not found")), timeout);
99
+ });
100
+ }
101
+ async function getTransactionFromSubscription(subscription) {
102
+ for await (const result of subscription) {
103
+ if (result?.data?.getTransaction?.receipt) {
104
+ return result.data.getTransaction;
105
+ }
106
+ }
107
+ throw new Error("No transaction found");
108
+ }
109
+
110
+ //#endregion
111
+ //#region src/utils/wallet-verification-challenge.ts
112
+ /**
113
+ * Custom error class for challenge-related errors
114
+ */
115
+ var ChallengeError = class extends Error {
116
+ code;
117
+ constructor(message, code) {
118
+ super(message);
119
+ this.name = "ChallengeError";
120
+ this.code = code;
121
+ }
122
+ };
123
+ /**
124
+ * Hashes a pincode with a salt using SHA-256
125
+ * @param pincode - The pincode to hash
126
+ * @param salt - The salt to use in hashing
127
+ * @returns The hashed pincode as a hex string
128
+ */
129
+ function hashPincode(pincode, salt) {
130
+ return createHash("sha256").update(`${salt}${pincode}`).digest("hex");
131
+ }
132
+ /**
133
+ * Generates a challenge response by combining a hashed pincode with a challenge
134
+ * @param pincode - The user's pincode
135
+ * @param salt - The salt provided in the challenge
136
+ * @param challenge - The challenge secret
137
+ * @returns The challenge response as a hex string
138
+ */
139
+ function generateResponse(pincode, salt, challenge) {
140
+ const hashedPincode = hashPincode(pincode, salt);
141
+ return createHash("sha256").update(`${hashedPincode}_${challenge}`).digest("hex");
142
+ }
143
+ /**
144
+ * Handles a wallet verification challenge by generating an appropriate response
145
+ *
146
+ * @param options - The options for handling the wallet verification challenge
147
+ * @returns Promise resolving to an object containing the challenge response and optionally the verification ID
148
+ * @throws {ChallengeError} If the challenge cannot be created or is invalid
149
+ * @example
150
+ * import { createPortalClient } from "@settlemint/sdk-portal";
151
+ * import { handleWalletVerificationChallenge } from "@settlemint/sdk-portal";
152
+ *
153
+ * const { client, graphql } = createPortalClient({
154
+ * instance: "https://portal.example.com/graphql",
155
+ * accessToken: "your-access-token"
156
+ * });
157
+ *
158
+ * const result = await handleWalletVerificationChallenge({
159
+ * portalClient: client,
160
+ * portalGraphql: graphql,
161
+ * verificationId: "verification-123",
162
+ * userWalletAddress: "0x123...",
163
+ * code: "123456",
164
+ * verificationType: "otp"
165
+ * });
166
+ */
167
+ async function handleWalletVerificationChallenge({ portalClient, portalGraphql, verificationId, userWalletAddress, code, verificationType }) {
168
+ try {
169
+ if (verificationType === "otp") {
170
+ return {
171
+ challengeResponse: code.toString(),
172
+ verificationId
173
+ };
174
+ }
175
+ if (verificationType === "secret-code") {
176
+ const formattedCode = code.toString().replace(/(.{5})(?=.)/, "$1-");
177
+ return {
178
+ challengeResponse: formattedCode,
179
+ verificationId
180
+ };
181
+ }
182
+ const verificationChallenges = await portalClient.request(portalGraphql(`
183
+ mutation CreateWalletVerificationChallenges($userWalletAddress: String!, $verificationId: String!) {
184
+ createWalletVerificationChallenges(userWalletAddress: $userWalletAddress, verificationId: $verificationId) {
185
+ challenge
186
+ id
187
+ name
188
+ verificationType
189
+ }
190
+ }
191
+ `), {
192
+ userWalletAddress,
193
+ verificationId
194
+ });
195
+ if (!verificationChallenges.createWalletVerificationChallenges?.length) {
196
+ throw new ChallengeError("No verification challenges received", "NO_CHALLENGES");
197
+ }
198
+ const walletVerificationChallenge = verificationChallenges.createWalletVerificationChallenges.find((challenge) => challenge.id === verificationId);
199
+ if (!walletVerificationChallenge?.challenge?.secret || !walletVerificationChallenge?.challenge?.salt) {
200
+ throw new ChallengeError("Invalid challenge format", "INVALID_CHALLENGE");
201
+ }
202
+ const { secret, salt } = walletVerificationChallenge.challenge;
203
+ const challengeResponse = generateResponse(code.toString(), salt, secret);
204
+ return {
205
+ challengeResponse,
206
+ verificationId
207
+ };
208
+ } catch (error) {
209
+ if (error instanceof ChallengeError) {
210
+ throw error;
211
+ }
212
+ throw new ChallengeError("Failed to process wallet verification challenge", "CHALLENGE_PROCESSING_ERROR");
213
+ }
214
+ }
215
+
216
+ //#endregion
217
+ //#region src/portal.ts
218
+ /**
219
+ * Schema for validating Portal client configuration options.
220
+ */
221
+ const ClientOptionsSchema = z.object({
222
+ instance: UrlOrPathSchema,
223
+ accessToken: ApplicationAccessTokenSchema.optional(),
224
+ cache: z.enum([
225
+ "default",
226
+ "force-cache",
227
+ "no-cache",
228
+ "no-store",
229
+ "only-if-cached",
230
+ "reload"
231
+ ]).optional()
232
+ });
233
+ /**
234
+ * Creates a Portal GraphQL client with the provided configuration.
235
+ *
236
+ * @param options - Configuration options for the Portal client
237
+ * @param clientOptions - Additional GraphQL client configuration options
238
+ * @returns An object containing the configured GraphQL client and graphql helper function
239
+ * @throws If the provided options fail validation
240
+ *
241
+ * @example
242
+ * import { createPortalClient } from "@settlemint/sdk-portal";
243
+ * import { loadEnv } from "@settlemint/sdk-utils/environment";
244
+ * import { createLogger, requestLogger } from "@settlemint/sdk-utils/logging";
245
+ * import type { introspection } from "@schemas/portal-env";
246
+ *
247
+ * const env = await loadEnv(false, false);
248
+ * const logger = createLogger();
249
+ *
250
+ * const { client: portalClient, graphql: portalGraphql } = createPortalClient<{
251
+ * introspection: introspection;
252
+ * disableMasking: true;
253
+ * scalars: {
254
+ * // Change unknown to the type you are using to store metadata
255
+ * JSON: unknown;
256
+ * };
257
+ * }>(
258
+ * {
259
+ * instance: env.SETTLEMINT_PORTAL_GRAPHQL_ENDPOINT!,
260
+ * accessToken: env.SETTLEMINT_ACCESS_TOKEN!,
261
+ * },
262
+ * {
263
+ * fetch: requestLogger(logger, "portal", fetch) as typeof fetch,
264
+ * },
265
+ * );
266
+ *
267
+ * // Making GraphQL queries
268
+ * const query = portalGraphql(`
269
+ * query GetPendingTransactions {
270
+ * getPendingTransactions {
271
+ * count
272
+ * }
273
+ * }
274
+ * `);
275
+ *
276
+ * const result = await portalClient.request(query);
277
+ */
278
+ function createPortalClient(options, clientOptions) {
279
+ ensureServer();
280
+ const validatedOptions = validate(ClientOptionsSchema, options);
281
+ const graphql = initGraphQLTada();
282
+ const fullUrl = new URL(validatedOptions.instance).toString();
283
+ return {
284
+ client: new GraphQLClient(fullUrl, {
285
+ ...clientOptions,
286
+ headers: appendHeaders(clientOptions?.headers, { "x-auth-token": validatedOptions.accessToken })
287
+ }),
288
+ graphql
289
+ };
290
+ }
291
+
292
+ //#endregion
293
+ export { ClientOptionsSchema, createPortalClient, getWebsocketClient, handleWalletVerificationChallenge, readFragment, waitForTransactionReceipt };
294
+ //# sourceMappingURL=portal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal.js","names":["url: URL","transactionHash: string","options: WaitForTransactionReceiptOptions","timeout: number","subscription: AsyncIterableIterator<FormattedExecutionResult<GetTransactionResponse, unknown>>","message: string","code: string","pincode: string","salt: string","challenge: string","options: ClientOptions","clientOptions?: RequestConfig"],"sources":["../../src/utils/websocket-client.ts","../../src/utils/wait-for-transaction-receipt.ts","../../src/utils/wallet-verification-challenge.ts","../../src/portal.ts"],"sourcesContent":["import { createClient } from \"graphql-ws\";\n\n/**\n * Options for the GraphQL WebSocket client\n */\nexport interface WebsocketClientOptions {\n /**\n * The GraphQL endpoint URL for the Portal API\n */\n portalGraphqlEndpoint: string;\n /**\n * The access token for authentication with the Portal API\n */\n accessToken?: string;\n}\n\n/**\n * Creates a GraphQL WebSocket client for the Portal API\n *\n * @param {WebsocketClientOptions} options - The options for the client\n * @returns {Client} The GraphQL WebSocket client\n * @example\n * import { getWebsocketClient } from \"@settlemint/sdk-portal\";\n *\n * const client = getWebsocketClient({\n * portalGraphqlEndpoint: \"https://portal.settlemint.com/graphql\",\n * accessToken: \"your-access-token\",\n * });\n */\nexport function getWebsocketClient({ portalGraphqlEndpoint, accessToken }: WebsocketClientOptions) {\n if (!portalGraphqlEndpoint) {\n throw new Error(\"portalGraphqlEndpoint is required\");\n }\n const graphqlEndpoint = setWsProtocol(new URL(portalGraphqlEndpoint));\n return createClient({\n url: `${graphqlEndpoint.protocol}//${graphqlEndpoint.host}/${accessToken}${graphqlEndpoint.pathname}${graphqlEndpoint.search}`,\n });\n}\n\nfunction setWsProtocol(url: URL) {\n if (url.protocol === \"ws:\" || url.protocol === \"wss:\") {\n return url;\n }\n if (url.protocol === \"http:\") {\n url.protocol = \"ws:\";\n } else {\n url.protocol = \"wss:\";\n }\n return url;\n}\n","import type { FormattedExecutionResult } from \"graphql-ws\";\nimport type { Address, Hex, TransactionReceipt as TransactionReceiptViem } from \"viem\";\nimport { type WebsocketClientOptions, getWebsocketClient } from \"./websocket-client.js\";\n\n/**\n * Represents an event emitted during a transaction execution\n */\nexport interface TransactionEvent {\n /** The name of the event that was emitted */\n eventName: string;\n /** The arguments emitted by the event */\n args: Record<string, unknown>;\n /** Indexed event parameters used for filtering and searching */\n topics: Hex[];\n}\n\n/**\n * Represents the structure of a blockchain transaction receipt\n */\nexport interface TransactionReceipt extends TransactionReceiptViem<string, number, \"Success\" | \"Reverted\"> {\n /** The raw reason for transaction reversion, if applicable */\n revertReason: string;\n /** Human-readable version of the revert reason */\n revertReasonDecoded: string;\n /** Array of events emitted during the transaction */\n events: TransactionEvent[];\n /** The address of the contract deployed in the transaction */\n contractAddress: Address;\n}\n\n/**\n * Represents the structure of a blockchain transaction with its receipt\n */\nexport interface Transaction {\n receipt: TransactionReceipt;\n /** The hash of the transaction (duplicate of receipt.transactionHash) */\n transactionHash: string;\n /** The sender address (duplicate of receipt.from) */\n from: string;\n /** Timestamp when the transaction was created */\n createdAt: string;\n /** The contract address involved in the transaction */\n address: string;\n /** The name of the function called in the transaction */\n functionName: string;\n /** Whether the transaction is a contract deployment */\n isContract: boolean;\n}\n\ninterface GetTransactionResponse {\n getTransaction: Transaction;\n}\n\n/**\n * Options for waiting for a transaction receipt\n */\nexport interface WaitForTransactionReceiptOptions extends WebsocketClientOptions {\n /** Optional timeout in milliseconds before the operation fails */\n timeout?: number;\n}\n\n/**\n * Waits for a blockchain transaction receipt by subscribing to transaction updates via GraphQL.\n * This function polls until the transaction is confirmed or the timeout is reached.\n *\n * @param transactionHash - The hash of the transaction to wait for\n * @param options - Configuration options for the waiting process\n * @returns The transaction details including receipt information when the transaction is confirmed\n * @throws Error if the transaction receipt cannot be retrieved within the specified timeout\n *\n * @example\n * import { waitForTransactionReceipt } from \"@settlemint/sdk-portal\";\n *\n * const transaction = await waitForTransactionReceipt(\"0x123...\", {\n * portalGraphqlEndpoint: \"https://example.settlemint.com/graphql\",\n * accessToken: \"your-access-token\",\n * timeout: 30000 // 30 seconds timeout\n * });\n */\nexport async function waitForTransactionReceipt(transactionHash: string, options: WaitForTransactionReceiptOptions) {\n const wsClient = getWebsocketClient(options);\n const subscription = wsClient.iterate<GetTransactionResponse>({\n query: `subscription getTransaction($transactionHash: String!) {\n getTransaction(transactionHash: $transactionHash) {\n receipt {\n transactionHash\n to\n status\n from\n type\n revertReason\n revertReasonDecoded\n logs\n events\n contractAddress\n }\n transactionHash\n from\n createdAt\n address\n functionName\n isContract\n }\n }`,\n variables: { transactionHash },\n });\n const promises = [getTransactionFromSubscription(subscription)];\n if (options.timeout) {\n promises.push(createTimeoutPromise(options.timeout));\n }\n\n return Promise.race(promises);\n}\n\nfunction createTimeoutPromise(timeout: number): Promise<never> {\n return new Promise((_, reject) => {\n setTimeout(() => reject(new Error(\"Transaction receipt not found\")), timeout);\n });\n}\n\nasync function getTransactionFromSubscription(\n subscription: AsyncIterableIterator<FormattedExecutionResult<GetTransactionResponse, unknown>>,\n): Promise<Transaction> {\n for await (const result of subscription) {\n if (result?.data?.getTransaction?.receipt) {\n return result.data.getTransaction;\n }\n }\n throw new Error(\"No transaction found\");\n}\n","import { createHash } from \"node:crypto\";\nimport type { AbstractSetupSchema, initGraphQLTada } from \"gql.tada\";\nimport type { GraphQLClient } from \"graphql-request\";\nimport type { Address } from \"viem\";\n\n/**\n * Custom error class for challenge-related errors\n */\nexport class ChallengeError extends Error {\n readonly code: string;\n\n constructor(message: string, code: string) {\n super(message);\n this.name = \"ChallengeError\";\n this.code = code;\n }\n}\n\n/**\n * Represents the structure of a wallet verification challenge\n */\ninterface WalletVerificationChallenge {\n challenge: {\n secret: string;\n salt: string;\n };\n id: string;\n name: string;\n verificationType: string;\n}\n\n/**\n * Response type for the CreateWalletVerificationChallenges mutation\n */\ninterface CreateWalletVerificationChallengesResponse {\n createWalletVerificationChallenges: WalletVerificationChallenge[];\n}\n\n/**\n * Hashes a pincode with a salt using SHA-256\n * @param pincode - The pincode to hash\n * @param salt - The salt to use in hashing\n * @returns The hashed pincode as a hex string\n */\nfunction hashPincode(pincode: string, salt: string): string {\n return createHash(\"sha256\").update(`${salt}${pincode}`).digest(\"hex\");\n}\n\n/**\n * Generates a challenge response by combining a hashed pincode with a challenge\n * @param pincode - The user's pincode\n * @param salt - The salt provided in the challenge\n * @param challenge - The challenge secret\n * @returns The challenge response as a hex string\n */\nfunction generateResponse(pincode: string, salt: string, challenge: string): string {\n const hashedPincode = hashPincode(pincode, salt);\n return createHash(\"sha256\").update(`${hashedPincode}_${challenge}`).digest(\"hex\");\n}\n\n/**\n * Options for handling a wallet verification challenge\n */\nexport interface HandleWalletVerificationChallengeOptions<Setup extends AbstractSetupSchema> {\n /** The portal client instance */\n portalClient: GraphQLClient;\n /** The GraphQL query builder */\n portalGraphql: initGraphQLTada<Setup>;\n /** The ID of the verification challenge */\n verificationId: string;\n /** The wallet address to verify */\n userWalletAddress: Address;\n /** The verification code provided by the user */\n code: string | number;\n /** The type of verification being performed */\n verificationType: \"otp\" | \"secret-code\" | \"pincode\";\n}\n\n/**\n * Handles a wallet verification challenge by generating an appropriate response\n *\n * @param options - The options for handling the wallet verification challenge\n * @returns Promise resolving to an object containing the challenge response and optionally the verification ID\n * @throws {ChallengeError} If the challenge cannot be created or is invalid\n * @example\n * import { createPortalClient } from \"@settlemint/sdk-portal\";\n * import { handleWalletVerificationChallenge } from \"@settlemint/sdk-portal\";\n *\n * const { client, graphql } = createPortalClient({\n * instance: \"https://portal.example.com/graphql\",\n * accessToken: \"your-access-token\"\n * });\n *\n * const result = await handleWalletVerificationChallenge({\n * portalClient: client,\n * portalGraphql: graphql,\n * verificationId: \"verification-123\",\n * userWalletAddress: \"0x123...\",\n * code: \"123456\",\n * verificationType: \"otp\"\n * });\n */\nexport async function handleWalletVerificationChallenge<const Setup extends AbstractSetupSchema>({\n portalClient,\n portalGraphql,\n verificationId,\n userWalletAddress,\n code,\n verificationType,\n}: HandleWalletVerificationChallengeOptions<Setup>): Promise<{\n challengeResponse: string;\n verificationId?: string;\n}> {\n try {\n if (verificationType === \"otp\") {\n return {\n challengeResponse: code.toString(),\n verificationId,\n };\n }\n\n if (verificationType === \"secret-code\") {\n // Add - separator to the code\n const formattedCode = code.toString().replace(/(.{5})(?=.)/, \"$1-\");\n return {\n challengeResponse: formattedCode,\n verificationId,\n };\n }\n\n const verificationChallenges = await portalClient.request<CreateWalletVerificationChallengesResponse>(\n portalGraphql(`\n mutation CreateWalletVerificationChallenges($userWalletAddress: String!, $verificationId: String!) {\n createWalletVerificationChallenges(userWalletAddress: $userWalletAddress, verificationId: $verificationId) {\n challenge\n id\n name\n verificationType\n }\n }\n `),\n {\n userWalletAddress,\n verificationId,\n },\n );\n\n if (!verificationChallenges.createWalletVerificationChallenges?.length) {\n throw new ChallengeError(\"No verification challenges received\", \"NO_CHALLENGES\");\n }\n\n const walletVerificationChallenge = verificationChallenges.createWalletVerificationChallenges.find(\n (challenge) => challenge.id === verificationId,\n );\n\n if (!walletVerificationChallenge?.challenge?.secret || !walletVerificationChallenge?.challenge?.salt) {\n throw new ChallengeError(\"Invalid challenge format\", \"INVALID_CHALLENGE\");\n }\n\n const { secret, salt } = walletVerificationChallenge.challenge;\n const challengeResponse = generateResponse(code.toString(), salt, secret);\n return {\n challengeResponse,\n verificationId,\n };\n } catch (error) {\n if (error instanceof ChallengeError) {\n throw error;\n }\n throw new ChallengeError(\"Failed to process wallet verification challenge\", \"CHALLENGE_PROCESSING_ERROR\");\n }\n}\n","import { appendHeaders } from \"@settlemint/sdk-utils/http\";\nimport { ensureServer } from \"@settlemint/sdk-utils/runtime\";\nimport { ApplicationAccessTokenSchema, UrlOrPathSchema, validate } from \"@settlemint/sdk-utils/validation\";\nimport { type AbstractSetupSchema, initGraphQLTada } from \"gql.tada\";\nimport { GraphQLClient } from \"graphql-request\";\nimport { z } from \"zod/v4\";\n\n/**\n * Configuration options for the GraphQL client, excluding 'url' and 'exchanges'.\n */\nexport type RequestConfig = ConstructorParameters<typeof GraphQLClient>[1];\n\n/**\n * Schema for validating Portal client configuration options.\n */\nexport const ClientOptionsSchema = z.object({\n instance: UrlOrPathSchema,\n accessToken: ApplicationAccessTokenSchema.optional(),\n cache: z.enum([\"default\", \"force-cache\", \"no-cache\", \"no-store\", \"only-if-cached\", \"reload\"]).optional(),\n});\n\n/**\n * Type representing the validated client options.\n */\nexport type ClientOptions = z.infer<typeof ClientOptionsSchema>;\n\n/**\n * Creates a Portal GraphQL client with the provided configuration.\n *\n * @param options - Configuration options for the Portal client\n * @param clientOptions - Additional GraphQL client configuration options\n * @returns An object containing the configured GraphQL client and graphql helper function\n * @throws If the provided options fail validation\n *\n * @example\n * import { createPortalClient } from \"@settlemint/sdk-portal\";\n * import { loadEnv } from \"@settlemint/sdk-utils/environment\";\n * import { createLogger, requestLogger } from \"@settlemint/sdk-utils/logging\";\n * import type { introspection } from \"@schemas/portal-env\";\n *\n * const env = await loadEnv(false, false);\n * const logger = createLogger();\n *\n * const { client: portalClient, graphql: portalGraphql } = createPortalClient<{\n * introspection: introspection;\n * disableMasking: true;\n * scalars: {\n * // Change unknown to the type you are using to store metadata\n * JSON: unknown;\n * };\n * }>(\n * {\n * instance: env.SETTLEMINT_PORTAL_GRAPHQL_ENDPOINT!,\n * accessToken: env.SETTLEMINT_ACCESS_TOKEN!,\n * },\n * {\n * fetch: requestLogger(logger, \"portal\", fetch) as typeof fetch,\n * },\n * );\n *\n * // Making GraphQL queries\n * const query = portalGraphql(`\n * query GetPendingTransactions {\n * getPendingTransactions {\n * count\n * }\n * }\n * `);\n *\n * const result = await portalClient.request(query);\n */\nexport function createPortalClient<const Setup extends AbstractSetupSchema>(\n options: ClientOptions,\n clientOptions?: RequestConfig,\n): {\n client: GraphQLClient;\n graphql: initGraphQLTada<Setup>;\n} {\n ensureServer();\n const validatedOptions = validate(ClientOptionsSchema, options);\n const graphql = initGraphQLTada<Setup>();\n const fullUrl = new URL(validatedOptions.instance).toString();\n\n return {\n client: new GraphQLClient(fullUrl, {\n ...clientOptions,\n headers: appendHeaders(clientOptions?.headers, { \"x-auth-token\": validatedOptions.accessToken }),\n }),\n graphql,\n };\n}\n\nexport { readFragment } from \"gql.tada\";\nexport type { FragmentOf, ResultOf, VariablesOf } from \"gql.tada\";\nexport {\n waitForTransactionReceipt,\n type Transaction,\n type TransactionEvent,\n type TransactionReceipt,\n type WaitForTransactionReceiptOptions,\n} from \"./utils/wait-for-transaction-receipt.js\";\nexport {\n handleWalletVerificationChallenge,\n type HandleWalletVerificationChallengeOptions,\n} from \"./utils/wallet-verification-challenge.js\";\nexport { getWebsocketClient, type WebsocketClientOptions } from \"./utils/websocket-client.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,mBAAmB,EAAE,uBAAuB,aAAqC,EAAE;AACjG,MAAK,uBAAuB;AAC1B,QAAM,IAAI,MAAM;CACjB;CACD,MAAM,kBAAkB,cAAc,IAAI,IAAI,uBAAuB;AACrE,QAAO,aAAa,EAClB,MAAM,EAAE,gBAAgB,SAAS,IAAI,gBAAgB,KAAK,GAAG,YAAY,EAAE,gBAAgB,SAAS,EAAE,gBAAgB,OAAO,EAC9H,EAAC;AACH;AAED,SAAS,cAAcA,KAAU;AAC/B,KAAI,IAAI,aAAa,SAAS,IAAI,aAAa,QAAQ;AACrD,SAAO;CACR;AACD,KAAI,IAAI,aAAa,SAAS;AAC5B,MAAI,WAAW;CAChB,OAAM;AACL,MAAI,WAAW;CAChB;AACD,QAAO;AACR;;;;;;;;;;;;;;;;;;;;;;AC8BD,eAAsB,0BAA0BC,iBAAyBC,SAA2C;CAClH,MAAM,WAAW,mBAAmB,QAAQ;CAC5C,MAAM,eAAe,SAAS,QAAgC;EAC5D,QAAQ;;;;;;;;;;;;;;;;;;;;;;EAsBR,WAAW,EAAE,gBAAiB;CAC/B,EAAC;CACF,MAAM,WAAW,CAAC,+BAA+B,aAAa,AAAC;AAC/D,KAAI,QAAQ,SAAS;AACnB,WAAS,KAAK,qBAAqB,QAAQ,QAAQ,CAAC;CACrD;AAED,QAAO,QAAQ,KAAK,SAAS;AAC9B;AAED,SAAS,qBAAqBC,SAAiC;AAC7D,QAAO,IAAI,QAAQ,CAAC,GAAG,WAAW;AAChC,aAAW,MAAM,OAAO,IAAI,MAAM,iCAAiC,EAAE,QAAQ;CAC9E;AACF;AAED,eAAe,+BACbC,cACsB;AACtB,YAAW,MAAM,UAAU,cAAc;AACvC,MAAI,QAAQ,MAAM,gBAAgB,SAAS;AACzC,UAAO,OAAO,KAAK;EACpB;CACF;AACD,OAAM,IAAI,MAAM;AACjB;;;;;;;ACzHD,IAAa,iBAAb,cAAoC,MAAM;CACxC,AAAS;CAET,YAAYC,SAAiBC,MAAc;AACzC,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,OAAO;CACb;AACF;;;;;;;AA4BD,SAAS,YAAYC,SAAiBC,MAAsB;AAC1D,QAAO,WAAW,SAAS,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,OAAO,MAAM;AACtE;;;;;;;;AASD,SAAS,iBAAiBD,SAAiBC,MAAcC,WAA2B;CAClF,MAAM,gBAAgB,YAAY,SAAS,KAAK;AAChD,QAAO,WAAW,SAAS,CAAC,QAAQ,EAAE,cAAc,GAAG,UAAU,EAAE,CAAC,OAAO,MAAM;AAClF;;;;;;;;;;;;;;;;;;;;;;;;;AA4CD,eAAsB,kCAA2E,EAC/F,cACA,eACA,gBACA,mBACA,MACA,kBACgD,EAG/C;AACD,KAAI;AACF,MAAI,qBAAqB,OAAO;AAC9B,UAAO;IACL,mBAAmB,KAAK,UAAU;IAClC;GACD;EACF;AAED,MAAI,qBAAqB,eAAe;GAEtC,MAAM,gBAAgB,KAAK,UAAU,CAAC,QAAQ,eAAe,MAAM;AACnE,UAAO;IACL,mBAAmB;IACnB;GACD;EACF;EAED,MAAM,yBAAyB,MAAM,aAAa,QAChD,eAAe;;;;;;;;;QASb,EACF;GACE;GACA;EACD,EACF;AAED,OAAK,uBAAuB,oCAAoC,QAAQ;AACtE,SAAM,IAAI,eAAe,uCAAuC;EACjE;EAED,MAAM,8BAA8B,uBAAuB,mCAAmC,KAC5F,CAAC,cAAc,UAAU,OAAO,eACjC;AAED,OAAK,6BAA6B,WAAW,WAAW,6BAA6B,WAAW,MAAM;AACpG,SAAM,IAAI,eAAe,4BAA4B;EACtD;EAED,MAAM,EAAE,QAAQ,MAAM,GAAG,4BAA4B;EACrD,MAAM,oBAAoB,iBAAiB,KAAK,UAAU,EAAE,MAAM,OAAO;AACzE,SAAO;GACL;GACA;EACD;CACF,SAAQ,OAAO;AACd,MAAI,iBAAiB,gBAAgB;AACnC,SAAM;EACP;AACD,QAAM,IAAI,eAAe,mDAAmD;CAC7E;AACF;;;;;;;AC5JD,MAAa,sBAAsB,EAAE,OAAO;CAC1C,UAAU;CACV,aAAa,6BAA6B,UAAU;CACpD,OAAO,EAAE,KAAK;EAAC;EAAW;EAAe;EAAY;EAAY;EAAkB;CAAS,EAAC,CAAC,UAAU;AACzG,EAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDF,SAAgB,mBACdC,SACAC,eAIA;AACA,eAAc;CACd,MAAM,mBAAmB,SAAS,qBAAqB,QAAQ;CAC/D,MAAM,UAAU,iBAAwB;CACxC,MAAM,UAAU,IAAI,IAAI,iBAAiB,UAAU,UAAU;AAE7D,QAAO;EACL,QAAQ,IAAI,cAAc,SAAS;GACjC,GAAG;GACH,SAAS,cAAc,eAAe,SAAS,EAAE,gBAAgB,iBAAiB,YAAa,EAAC;EACjG;EACD;CACD;AACF"}