@settlemint/sdk-portal 2.3.2-pref75df5f → 2.3.2-prf63bb3f0

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.
package/dist/portal.cjs CHANGED
@@ -28,6 +28,7 @@ __export(portal_exports, {
28
28
  waitForTransactionReceipt: () => waitForTransactionReceipt
29
29
  });
30
30
  module.exports = __toCommonJS(portal_exports);
31
+ var import_http = require("@settlemint/sdk-utils/http");
31
32
  var import_runtime = require("@settlemint/sdk-utils/runtime");
32
33
  var import_validation = require("@settlemint/sdk-utils/validation");
33
34
  var import_gql = require("gql.tada");
@@ -190,7 +191,7 @@ async function getTransactionFromSubscription(subscription) {
190
191
  var import_gql2 = require("gql.tada");
191
192
  var ClientOptionsSchema = import_v4.z.object({
192
193
  instance: import_validation.UrlOrPathSchema,
193
- accessToken: import_validation.ApplicationAccessTokenSchema,
194
+ accessToken: import_validation.ApplicationAccessTokenSchema.optional(),
194
195
  cache: import_v4.z.enum(["default", "force-cache", "no-cache", "no-store", "only-if-cached", "reload"]).optional()
195
196
  });
196
197
  function createPortalClient(options, clientOptions) {
@@ -201,7 +202,7 @@ function createPortalClient(options, clientOptions) {
201
202
  return {
202
203
  client: new import_graphql_request.GraphQLClient(fullUrl, {
203
204
  ...clientOptions,
204
- headers: { ...clientOptions?.headers ?? {}, "x-auth-token": validatedOptions.accessToken }
205
+ headers: (0, import_http.appendHeaders)(clientOptions?.headers, { "x-auth-token": validatedOptions.accessToken })
205
206
  }),
206
207
  graphql
207
208
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/portal.ts","../src/utils/wallet-verification-challenge.ts","../src/utils/websocket-client.ts","../src/utils/wait-for-transaction-receipt.ts"],"sourcesContent":["import { 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,\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: { ...(clientOptions?.headers ?? {}), \"x-auth-token\": validatedOptions.accessToken },\n }),\n graphql,\n };\n}\n\nexport {\n handleWalletVerificationChallenge,\n type HandleWalletVerificationChallengeOptions,\n} from \"./utils/wallet-verification-challenge.js\";\nexport {\n waitForTransactionReceipt,\n type Transaction,\n type WaitForTransactionReceiptOptions,\n} from \"./utils/wait-for-transaction-receipt.js\";\nexport { getWebsocketClient, type WebsocketClientOptions } from \"./utils/websocket-client.js\";\nexport { readFragment } from \"gql.tada\";\nexport type { FragmentOf, ResultOf, VariablesOf } from \"gql.tada\";\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 *\n * @typedef {Object} HandleWalletVerificationChallengeOptions\n * @template {AbstractSetupSchema} Setup - The GraphQL schema setup type\n * @property {GraphQLClient} portalClient - The portal client instance\n * @property {initGraphQLTada<Setup>} portalGraphql - The GraphQL query builder\n * @property {string} verificationId - The ID of the verification challenge\n * @property {Address} userWalletAddress - The wallet address to verify\n * @property {string | number} code - The verification code provided by the user\n * @property {\"otp\" | \"secret-code\" | \"pincode\"} verificationType - The type of verification being performed\n */\nexport interface HandleWalletVerificationChallengeOptions<Setup extends AbstractSetupSchema> {\n portalClient: GraphQLClient;\n portalGraphql: initGraphQLTada<Setup>;\n verificationId: string;\n userWalletAddress: Address;\n code: string | number;\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 { createClient } from \"graphql-ws\";\n\n/**\n * Options for the GraphQL WebSocket client\n *\n * @typedef {Object} WebsocketClientOptions\n * @property {string} portalGraphqlEndpoint - The GraphQL endpoint URL for the Portal API\n * @property {string} accessToken - The access token for authentication with the Portal API\n */\nexport interface WebsocketClientOptions {\n portalGraphqlEndpoint: string;\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 * @param {string} options.portalGraphqlEndpoint - The GraphQL endpoint URL for the Portal API\n * @param {string} options.accessToken - The access token for authentication with the Portal API\n * @returns {Client} The GraphQL WebSocket client\n */\nexport function getWebsocketClient({ portalGraphqlEndpoint, accessToken }: WebsocketClientOptions) {\n if (!portalGraphqlEndpoint) {\n throw new Error(\"portalGraphqlEndpoint is required\");\n }\n if (!accessToken) {\n throw new Error(\"accessToken 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 WebsocketClientOptions, getWebsocketClient } from \"./websocket-client.js\";\n\n/**\n * Represents the structure of a blockchain transaction with its receipt\n *\n * @typedef {Object} Transaction\n * @property {Object} receipt - The transaction receipt details\n * @property {string} receipt.transactionHash - The hash of the transaction\n * @property {string} receipt.to - The recipient address of the transaction\n * @property {string} receipt.status - The status of the transaction (success/failure)\n * @property {string} receipt.from - The sender address of the transaction\n * @property {string} receipt.type - The type of the transaction\n * @property {string} receipt.revertReason - The reason for transaction reversion, if applicable\n * @property {string} receipt.revertReasonDecoded - Human-readable version of the revert reason\n * @property {string} receipt.contractAddress - The address of the contract deployed in the transaction\n * @property {string[]} receipt.logs - Array of log entries generated by the transaction\n * @property {string[]} receipt.events - Array of events emitted during the transaction\n * @property {string} transactionHash - The hash of the transaction (duplicate of receipt.transactionHash)\n * @property {string} from - The sender address (duplicate of receipt.from)\n * @property {string} createdAt - Timestamp when the transaction was created\n * @property {string} address - The contract address involved in the transaction\n * @property {string} functionName - The name of the function called in the transaction\n * @property {boolean} isContract - Whether the transaction is a contract deployment\n */\nexport interface Transaction {\n receipt: {\n transactionHash: string;\n to: string;\n status: string;\n from: string;\n type: string;\n revertReason: string;\n revertReasonDecoded: string;\n logs: string[];\n events: string[];\n contractAddress: string;\n };\n transactionHash: string;\n from: string;\n createdAt: string;\n address: string;\n functionName: string;\n isContract: boolean;\n}\n\ninterface GetTransactionResponse {\n getTransaction: Transaction;\n}\n\n/**\n * Options for waiting for a transaction receipt\n *\n * @typedef {Object} WaitForTransactionReceiptOptions\n * @property {number} [timeout] - Optional timeout in milliseconds before the operation fails\n */\nexport interface WaitForTransactionReceiptOptions extends WebsocketClientOptions {\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"],"mappings":"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","names":["import_gql"]}
1
+ {"version":3,"sources":["../src/portal.ts","../src/utils/wallet-verification-challenge.ts","../src/utils/websocket-client.ts","../src/utils/wait-for-transaction-receipt.ts"],"sourcesContent":["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 {\n handleWalletVerificationChallenge,\n type HandleWalletVerificationChallengeOptions,\n} from \"./utils/wallet-verification-challenge.js\";\nexport {\n waitForTransactionReceipt,\n type Transaction,\n type WaitForTransactionReceiptOptions,\n} from \"./utils/wait-for-transaction-receipt.js\";\nexport { getWebsocketClient, type WebsocketClientOptions } from \"./utils/websocket-client.js\";\nexport { readFragment } from \"gql.tada\";\nexport type { FragmentOf, ResultOf, VariablesOf } from \"gql.tada\";\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 *\n * @typedef {Object} HandleWalletVerificationChallengeOptions\n * @template {AbstractSetupSchema} Setup - The GraphQL schema setup type\n * @property {GraphQLClient} portalClient - The portal client instance\n * @property {initGraphQLTada<Setup>} portalGraphql - The GraphQL query builder\n * @property {string} verificationId - The ID of the verification challenge\n * @property {Address} userWalletAddress - The wallet address to verify\n * @property {string | number} code - The verification code provided by the user\n * @property {\"otp\" | \"secret-code\" | \"pincode\"} verificationType - The type of verification being performed\n */\nexport interface HandleWalletVerificationChallengeOptions<Setup extends AbstractSetupSchema> {\n portalClient: GraphQLClient;\n portalGraphql: initGraphQLTada<Setup>;\n verificationId: string;\n userWalletAddress: Address;\n code: string | number;\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 { createClient } from \"graphql-ws\";\n\n/**\n * Options for the GraphQL WebSocket client\n *\n * @typedef {Object} WebsocketClientOptions\n * @property {string} portalGraphqlEndpoint - The GraphQL endpoint URL for the Portal API\n * @property {string} accessToken - The access token for authentication with the Portal API\n */\nexport interface WebsocketClientOptions {\n portalGraphqlEndpoint: string;\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 * @param {string} options.portalGraphqlEndpoint - The GraphQL endpoint URL for the Portal API\n * @param {string} options.accessToken - The access token for authentication with the Portal API\n * @returns {Client} The GraphQL WebSocket client\n */\nexport function getWebsocketClient({ portalGraphqlEndpoint, accessToken }: WebsocketClientOptions) {\n if (!portalGraphqlEndpoint) {\n throw new Error(\"portalGraphqlEndpoint is required\");\n }\n if (!accessToken) {\n throw new Error(\"accessToken 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 WebsocketClientOptions, getWebsocketClient } from \"./websocket-client.js\";\n\n/**\n * Represents the structure of a blockchain transaction with its receipt\n *\n * @typedef {Object} Transaction\n * @property {Object} receipt - The transaction receipt details\n * @property {string} receipt.transactionHash - The hash of the transaction\n * @property {string} receipt.to - The recipient address of the transaction\n * @property {string} receipt.status - The status of the transaction (success/failure)\n * @property {string} receipt.from - The sender address of the transaction\n * @property {string} receipt.type - The type of the transaction\n * @property {string} receipt.revertReason - The reason for transaction reversion, if applicable\n * @property {string} receipt.revertReasonDecoded - Human-readable version of the revert reason\n * @property {string} receipt.contractAddress - The address of the contract deployed in the transaction\n * @property {string[]} receipt.logs - Array of log entries generated by the transaction\n * @property {string[]} receipt.events - Array of events emitted during the transaction\n * @property {string} transactionHash - The hash of the transaction (duplicate of receipt.transactionHash)\n * @property {string} from - The sender address (duplicate of receipt.from)\n * @property {string} createdAt - Timestamp when the transaction was created\n * @property {string} address - The contract address involved in the transaction\n * @property {string} functionName - The name of the function called in the transaction\n * @property {boolean} isContract - Whether the transaction is a contract deployment\n */\nexport interface Transaction {\n receipt: {\n transactionHash: string;\n to: string;\n status: string;\n from: string;\n type: string;\n revertReason: string;\n revertReasonDecoded: string;\n logs: string[];\n events: string[];\n contractAddress: string;\n };\n transactionHash: string;\n from: string;\n createdAt: string;\n address: string;\n functionName: string;\n isContract: boolean;\n}\n\ninterface GetTransactionResponse {\n getTransaction: Transaction;\n}\n\n/**\n * Options for waiting for a transaction receipt\n *\n * @typedef {Object} WaitForTransactionReceiptOptions\n * @property {number} [timeout] - Optional timeout in milliseconds before the operation fails\n */\nexport interface WaitForTransactionReceiptOptions extends WebsocketClientOptions {\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"],"mappings":"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","names":["import_gql"]}
package/dist/portal.d.cts CHANGED
@@ -155,7 +155,7 @@ type RequestConfig = ConstructorParameters<typeof GraphQLClient>[1];
155
155
  */
156
156
  declare const ClientOptionsSchema: z.ZodObject<{
157
157
  instance: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
158
- accessToken: z.ZodString;
158
+ accessToken: z.ZodOptional<z.ZodString>;
159
159
  cache: z.ZodOptional<z.ZodEnum<{
160
160
  default: "default";
161
161
  "force-cache": "force-cache";
package/dist/portal.d.ts CHANGED
@@ -155,7 +155,7 @@ type RequestConfig = ConstructorParameters<typeof GraphQLClient>[1];
155
155
  */
156
156
  declare const ClientOptionsSchema: z.ZodObject<{
157
157
  instance: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
158
- accessToken: z.ZodString;
158
+ accessToken: z.ZodOptional<z.ZodString>;
159
159
  cache: z.ZodOptional<z.ZodEnum<{
160
160
  default: "default";
161
161
  "force-cache": "force-cache";
package/dist/portal.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  // src/portal.ts
2
+ import { appendHeaders } from "@settlemint/sdk-utils/http";
2
3
  import { ensureServer } from "@settlemint/sdk-utils/runtime";
3
4
  import { ApplicationAccessTokenSchema, UrlOrPathSchema, validate } from "@settlemint/sdk-utils/validation";
4
5
  import { initGraphQLTada } from "gql.tada";
@@ -161,7 +162,7 @@ async function getTransactionFromSubscription(subscription) {
161
162
  import { readFragment } from "gql.tada";
162
163
  var ClientOptionsSchema = z.object({
163
164
  instance: UrlOrPathSchema,
164
- accessToken: ApplicationAccessTokenSchema,
165
+ accessToken: ApplicationAccessTokenSchema.optional(),
165
166
  cache: z.enum(["default", "force-cache", "no-cache", "no-store", "only-if-cached", "reload"]).optional()
166
167
  });
167
168
  function createPortalClient(options, clientOptions) {
@@ -172,7 +173,7 @@ function createPortalClient(options, clientOptions) {
172
173
  return {
173
174
  client: new GraphQLClient(fullUrl, {
174
175
  ...clientOptions,
175
- headers: { ...clientOptions?.headers ?? {}, "x-auth-token": validatedOptions.accessToken }
176
+ headers: appendHeaders(clientOptions?.headers, { "x-auth-token": validatedOptions.accessToken })
176
177
  }),
177
178
  graphql
178
179
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/portal.ts","../src/utils/wallet-verification-challenge.ts","../src/utils/websocket-client.ts","../src/utils/wait-for-transaction-receipt.ts"],"sourcesContent":["import { 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,\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: { ...(clientOptions?.headers ?? {}), \"x-auth-token\": validatedOptions.accessToken },\n }),\n graphql,\n };\n}\n\nexport {\n handleWalletVerificationChallenge,\n type HandleWalletVerificationChallengeOptions,\n} from \"./utils/wallet-verification-challenge.js\";\nexport {\n waitForTransactionReceipt,\n type Transaction,\n type WaitForTransactionReceiptOptions,\n} from \"./utils/wait-for-transaction-receipt.js\";\nexport { getWebsocketClient, type WebsocketClientOptions } from \"./utils/websocket-client.js\";\nexport { readFragment } from \"gql.tada\";\nexport type { FragmentOf, ResultOf, VariablesOf } from \"gql.tada\";\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 *\n * @typedef {Object} HandleWalletVerificationChallengeOptions\n * @template {AbstractSetupSchema} Setup - The GraphQL schema setup type\n * @property {GraphQLClient} portalClient - The portal client instance\n * @property {initGraphQLTada<Setup>} portalGraphql - The GraphQL query builder\n * @property {string} verificationId - The ID of the verification challenge\n * @property {Address} userWalletAddress - The wallet address to verify\n * @property {string | number} code - The verification code provided by the user\n * @property {\"otp\" | \"secret-code\" | \"pincode\"} verificationType - The type of verification being performed\n */\nexport interface HandleWalletVerificationChallengeOptions<Setup extends AbstractSetupSchema> {\n portalClient: GraphQLClient;\n portalGraphql: initGraphQLTada<Setup>;\n verificationId: string;\n userWalletAddress: Address;\n code: string | number;\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 { createClient } from \"graphql-ws\";\n\n/**\n * Options for the GraphQL WebSocket client\n *\n * @typedef {Object} WebsocketClientOptions\n * @property {string} portalGraphqlEndpoint - The GraphQL endpoint URL for the Portal API\n * @property {string} accessToken - The access token for authentication with the Portal API\n */\nexport interface WebsocketClientOptions {\n portalGraphqlEndpoint: string;\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 * @param {string} options.portalGraphqlEndpoint - The GraphQL endpoint URL for the Portal API\n * @param {string} options.accessToken - The access token for authentication with the Portal API\n * @returns {Client} The GraphQL WebSocket client\n */\nexport function getWebsocketClient({ portalGraphqlEndpoint, accessToken }: WebsocketClientOptions) {\n if (!portalGraphqlEndpoint) {\n throw new Error(\"portalGraphqlEndpoint is required\");\n }\n if (!accessToken) {\n throw new Error(\"accessToken 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 WebsocketClientOptions, getWebsocketClient } from \"./websocket-client.js\";\n\n/**\n * Represents the structure of a blockchain transaction with its receipt\n *\n * @typedef {Object} Transaction\n * @property {Object} receipt - The transaction receipt details\n * @property {string} receipt.transactionHash - The hash of the transaction\n * @property {string} receipt.to - The recipient address of the transaction\n * @property {string} receipt.status - The status of the transaction (success/failure)\n * @property {string} receipt.from - The sender address of the transaction\n * @property {string} receipt.type - The type of the transaction\n * @property {string} receipt.revertReason - The reason for transaction reversion, if applicable\n * @property {string} receipt.revertReasonDecoded - Human-readable version of the revert reason\n * @property {string} receipt.contractAddress - The address of the contract deployed in the transaction\n * @property {string[]} receipt.logs - Array of log entries generated by the transaction\n * @property {string[]} receipt.events - Array of events emitted during the transaction\n * @property {string} transactionHash - The hash of the transaction (duplicate of receipt.transactionHash)\n * @property {string} from - The sender address (duplicate of receipt.from)\n * @property {string} createdAt - Timestamp when the transaction was created\n * @property {string} address - The contract address involved in the transaction\n * @property {string} functionName - The name of the function called in the transaction\n * @property {boolean} isContract - Whether the transaction is a contract deployment\n */\nexport interface Transaction {\n receipt: {\n transactionHash: string;\n to: string;\n status: string;\n from: string;\n type: string;\n revertReason: string;\n revertReasonDecoded: string;\n logs: string[];\n events: string[];\n contractAddress: string;\n };\n transactionHash: string;\n from: string;\n createdAt: string;\n address: string;\n functionName: string;\n isContract: boolean;\n}\n\ninterface GetTransactionResponse {\n getTransaction: Transaction;\n}\n\n/**\n * Options for waiting for a transaction receipt\n *\n * @typedef {Object} WaitForTransactionReceiptOptions\n * @property {number} [timeout] - Optional timeout in milliseconds before the operation fails\n */\nexport interface WaitForTransactionReceiptOptions extends WebsocketClientOptions {\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"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/portal.ts","../src/utils/wallet-verification-challenge.ts","../src/utils/websocket-client.ts","../src/utils/wait-for-transaction-receipt.ts"],"sourcesContent":["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 {\n handleWalletVerificationChallenge,\n type HandleWalletVerificationChallengeOptions,\n} from \"./utils/wallet-verification-challenge.js\";\nexport {\n waitForTransactionReceipt,\n type Transaction,\n type WaitForTransactionReceiptOptions,\n} from \"./utils/wait-for-transaction-receipt.js\";\nexport { getWebsocketClient, type WebsocketClientOptions } from \"./utils/websocket-client.js\";\nexport { readFragment } from \"gql.tada\";\nexport type { FragmentOf, ResultOf, VariablesOf } from \"gql.tada\";\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 *\n * @typedef {Object} HandleWalletVerificationChallengeOptions\n * @template {AbstractSetupSchema} Setup - The GraphQL schema setup type\n * @property {GraphQLClient} portalClient - The portal client instance\n * @property {initGraphQLTada<Setup>} portalGraphql - The GraphQL query builder\n * @property {string} verificationId - The ID of the verification challenge\n * @property {Address} userWalletAddress - The wallet address to verify\n * @property {string | number} code - The verification code provided by the user\n * @property {\"otp\" | \"secret-code\" | \"pincode\"} verificationType - The type of verification being performed\n */\nexport interface HandleWalletVerificationChallengeOptions<Setup extends AbstractSetupSchema> {\n portalClient: GraphQLClient;\n portalGraphql: initGraphQLTada<Setup>;\n verificationId: string;\n userWalletAddress: Address;\n code: string | number;\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 { createClient } from \"graphql-ws\";\n\n/**\n * Options for the GraphQL WebSocket client\n *\n * @typedef {Object} WebsocketClientOptions\n * @property {string} portalGraphqlEndpoint - The GraphQL endpoint URL for the Portal API\n * @property {string} accessToken - The access token for authentication with the Portal API\n */\nexport interface WebsocketClientOptions {\n portalGraphqlEndpoint: string;\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 * @param {string} options.portalGraphqlEndpoint - The GraphQL endpoint URL for the Portal API\n * @param {string} options.accessToken - The access token for authentication with the Portal API\n * @returns {Client} The GraphQL WebSocket client\n */\nexport function getWebsocketClient({ portalGraphqlEndpoint, accessToken }: WebsocketClientOptions) {\n if (!portalGraphqlEndpoint) {\n throw new Error(\"portalGraphqlEndpoint is required\");\n }\n if (!accessToken) {\n throw new Error(\"accessToken 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 WebsocketClientOptions, getWebsocketClient } from \"./websocket-client.js\";\n\n/**\n * Represents the structure of a blockchain transaction with its receipt\n *\n * @typedef {Object} Transaction\n * @property {Object} receipt - The transaction receipt details\n * @property {string} receipt.transactionHash - The hash of the transaction\n * @property {string} receipt.to - The recipient address of the transaction\n * @property {string} receipt.status - The status of the transaction (success/failure)\n * @property {string} receipt.from - The sender address of the transaction\n * @property {string} receipt.type - The type of the transaction\n * @property {string} receipt.revertReason - The reason for transaction reversion, if applicable\n * @property {string} receipt.revertReasonDecoded - Human-readable version of the revert reason\n * @property {string} receipt.contractAddress - The address of the contract deployed in the transaction\n * @property {string[]} receipt.logs - Array of log entries generated by the transaction\n * @property {string[]} receipt.events - Array of events emitted during the transaction\n * @property {string} transactionHash - The hash of the transaction (duplicate of receipt.transactionHash)\n * @property {string} from - The sender address (duplicate of receipt.from)\n * @property {string} createdAt - Timestamp when the transaction was created\n * @property {string} address - The contract address involved in the transaction\n * @property {string} functionName - The name of the function called in the transaction\n * @property {boolean} isContract - Whether the transaction is a contract deployment\n */\nexport interface Transaction {\n receipt: {\n transactionHash: string;\n to: string;\n status: string;\n from: string;\n type: string;\n revertReason: string;\n revertReasonDecoded: string;\n logs: string[];\n events: string[];\n contractAddress: string;\n };\n transactionHash: string;\n from: string;\n createdAt: string;\n address: string;\n functionName: string;\n isContract: boolean;\n}\n\ninterface GetTransactionResponse {\n getTransaction: Transaction;\n}\n\n/**\n * Options for waiting for a transaction receipt\n *\n * @typedef {Object} WaitForTransactionReceiptOptions\n * @property {number} [timeout] - Optional timeout in milliseconds before the operation fails\n */\nexport interface WaitForTransactionReceiptOptions extends WebsocketClientOptions {\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"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@settlemint/sdk-portal",
3
3
  "description": "Portal API client module for SettleMint SDK, providing access to smart contract portal services and APIs",
4
- "version": "2.3.2-pref75df5f",
4
+ "version": "2.3.2-prf63bb3f0",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "license": "FSL-1.1-MIT",
@@ -56,7 +56,7 @@
56
56
  "dependencies": {
57
57
  "gql.tada": "^1",
58
58
  "graphql-ws": "^6",
59
- "@settlemint/sdk-utils": "2.3.2-pref75df5f",
59
+ "@settlemint/sdk-utils": "2.3.2-prf63bb3f0",
60
60
  "graphql-request": "^7",
61
61
  "zod": "^3.25.0"
62
62
  },