@frak-labs/core-sdk
Version:
Core SDK of the Frak wallet, low level library to interact directly with the frak ecosystem.
1 lines • 50.7 kB
Source Map (JSON)
{"version":3,"file":"actions-DwDtd2rq.cjs","names":["getClientId","sdkConfigStore","getBackendUrl","withCache","withCache","withCache","getClientId","isV2Context","isV1Context","getClientId","areAddressesEqual","FrakContextManager","getClientId","sdkConfigStore","getBackendUrl","Deferred"],"sources":["../src/actions/displayEmbeddedWallet.ts","../src/actions/displayModal.ts","../src/actions/displaySharingPage.ts","../src/actions/ensureIdentity.ts","../src/actions/getMerchantInformation.ts","../src/actions/getMergeToken.ts","../src/actions/getUserReferralStatus.ts","../src/actions/prepareSso.ts","../src/actions/sendInteraction.ts","../src/actions/referral/processReferral.ts","../src/actions/referral/referralInteraction.ts","../src/actions/referral/setupReferral.ts","../src/actions/trackPurchaseStatus.ts","../src/actions/watchWalletStatus.ts","../src/actions/wrapper/modalBuilder.ts","../src/actions/wrapper/sendTransaction.ts","../src/actions/wrapper/siweAuthenticate.ts"],"sourcesContent":["import type {\n DisplayEmbeddedWalletParamsType,\n DisplayEmbeddedWalletResultType,\n FrakClient,\n} from \"../types\";\n\n/**\n * Function used to display the Frak embedded wallet popup\n * @param client - The current Frak Client\n * @param params - The parameter used to customise the embedded wallet\n * @param placement - Optional placement ID to associate with this display request\n * @returns The embedded wallet display result\n */\nexport async function displayEmbeddedWallet(\n client: FrakClient,\n params: DisplayEmbeddedWalletParamsType,\n placement?: string\n): Promise<DisplayEmbeddedWalletResultType> {\n return await client.request({\n method: \"frak_displayEmbeddedWallet\",\n params: placement\n ? [params, client.config.metadata, placement]\n : [params, client.config.metadata],\n });\n}\n","import type {\n DisplayModalParamsType,\n FrakClient,\n ModalRpcStepsResultType,\n ModalStepTypes,\n} from \"../types\";\n\n/**\n * Function used to display a modal\n * @param client - The current Frak Client\n * @param args\n * @param args.steps - The different steps of the modal\n * @param args.metadata - The metadata for the modal (customization, etc)\n * @param placement - Optional placement ID to associate with this modal display\n * @returns The result of each modal steps\n *\n * @description This function will display a modal to the user with the provided steps and metadata.\n *\n * @remarks\n * - The UI of the displayed modal can be configured with the `customCss` property in the `customizations.css` field of the top-level config.\n * - The `login` step will be automatically skipped if the user is already logged in. It's safe to include this step in all cases to ensure proper user state.\n * - Steps are automatically reordered in the following sequence:\n * 1. `login` (if needed)\n * 2. All other steps in the order specified\n * 3. `success` (if included, always last)\n *\n * @example\n * Simple sharing modal with steps:\n * 1. Login (Skipped if already logged in)\n * 2. Display a success message with sharing link option\n *\n * ```ts\n * const results = await displayModal(frakConfig, {\n * steps: {\n * // Simple login with no SSO, nor customization\n * login: { allowSso: false },\n * // Success message\n * final: {\n * action: { key: \"reward\" },\n * // Skip this step, it will be only displayed in the stepper within the modal\n * autoSkip: true,\n * },\n * },\n * });\n *\n * console.log(\"Login step - wallet\", results.login.wallet);\n * ```\n *\n * @example\n * A full modal example, with a few customization options, with the steps:\n * 1. Login (Skipped if already logged in)\n * 2. Authenticate via SIWE\n * 3. Send a transaction\n * 4. Display a success message with sharing link options\n *\n * ```ts\n * const results = await displayModal(frakConfig, {\n * steps: {\n * // Login step\n * login: {\n * allowSso: true,\n * ssoMetadata: {\n * logoUrl: \"https://my-app.com/logo.png\",\n * homepageLink: \"https://my-app.com\",\n * },\n * },\n * // Siwe authentication\n * siweAuthenticate: {\n * siwe: {\n * domain: \"my-app.com\",\n * uri: \"https://my-app.com/\",\n * nonce: generateSiweNonce(),\n * version: \"1\",\n * },\n * },\n * // Send batched transaction\n * sendTransaction: {\n * tx: [\n * { to: \"0xdeadbeef\", data: \"0xdeadbeef\" },\n * { to: \"0xdeadbeef\", data: \"0xdeadbeef\" },\n * ],\n * },\n * // Success message with sharing options\n * final: {\n * action: {\n * key: \"sharing\",\n * options: {\n * popupTitle: \"Share the app\",\n * text: \"Discover my super app website\",\n * link: \"https://my-app.com\",\n * },\n * },\n * dismissedMetadata: {\n * title: \"Dismiss\",\n * description: \"You won't be rewarded for this sharing action\",\n * },\n * },\n * },\n * metadata: {\n * // Header of desktop modals\n * header: {\n * title: \"My-App\",\n * icon: \"https://my-app.com/logo.png\",\n * },\n * // Context that will be present in every modal steps\n * context: \"My-app overkill flow\",\n * },\n * });\n * ```\n */\nexport async function displayModal<\n T extends ModalStepTypes[] = ModalStepTypes[],\n>(\n client: FrakClient,\n { steps, metadata }: DisplayModalParamsType<T>,\n placement?: string\n): Promise<ModalRpcStepsResultType<T>> {\n return (await client.request({\n method: \"frak_displayModal\",\n params: placement\n ? [steps, metadata, client.config.metadata, placement]\n : [steps, metadata, client.config.metadata],\n })) as ModalRpcStepsResultType<T>;\n}\n","import type {\n DisplaySharingPageParamsType,\n DisplaySharingPageResultType,\n FrakClient,\n} from \"../types\";\n\n/**\n * Function used to display a sharing page\n * @param client - The current Frak Client\n * @param params - The parameters to customize the sharing page (products, link override, metadata)\n * @param placement - Optional placement ID to associate with this display request\n * @returns The result indicating the user's action (shared, copied, or dismissed)\n *\n * @description This function will display a full-page sharing UI to the user,\n * showing product info, estimated rewards, sharing steps, FAQ, and share/copy buttons.\n * The sharing link is generated from the user's wallet context + merchant info.\n *\n * @remarks\n * - The promise resolves on the first user action (share or copy) but the page stays visible\n * - The user can continue to share/copy multiple times after the initial resolution\n * - Dismissing the page after a share/copy action is a no-op (promise already resolved)\n * - If the user dismisses without any action, the promise resolves with `{ action: \"dismissed\" }`\n *\n * @example\n * ```ts\n * const result = await displaySharingPage(frakClient, {\n * products: [\n * {\n * title: \"Babies camel cuir velours bout carré\",\n * imageUrl: \"https://example.com/product.jpg\",\n * },\n * ],\n * });\n *\n * console.log(\"User action:\", result.action); // \"shared\" | \"copied\" | \"dismissed\"\n * ```\n */\nexport async function displaySharingPage(\n client: FrakClient,\n params: DisplaySharingPageParamsType,\n placement?: string\n): Promise<DisplaySharingPageResultType> {\n return await client.request({\n method: \"frak_displaySharingPage\",\n params: placement\n ? [params, client.config.metadata, placement]\n : [params, client.config.metadata],\n });\n}\n","import { getBackendUrl } from \"../config/backendUrl\";\nimport { getClientId } from \"../config/clientId\";\nimport { sdkConfigStore } from \"../config/sdkConfigStore\";\n\nconst ENSURE_STORAGE_PREFIX = \"frak-identity-ensured-\";\n\n/**\n * Ensure the current wallet ↔ clientId link exists on the backend.\n *\n * Called automatically by {@link watchWalletStatus} when a connected wallet\n * status is received. Acts as a failsafe: if the primary merge (SSO, pairing,\n * login/register) missed or silently failed, this ensures the link is\n * eventually established.\n *\n * The call is:\n * - **Idempotent** — if already linked, backend returns immediately\n * - **Deduplicated** — only fires once per browser session per merchant\n * - **Fire-and-forget** — errors are logged but never thrown\n *\n * @param interactionToken - The SDK JWT from wallet status (x-wallet-sdk-auth)\n *\n * @example\n * ```ts\n * // Usually called automatically via watchWalletStatus side effect.\n * // Can also be called manually if needed:\n * await ensureIdentity(\"eyJhbGciOi...\");\n * ```\n */\nexport async function ensureIdentity(interactionToken: string): Promise<void> {\n if (typeof window === \"undefined\") {\n return;\n }\n\n const clientId = getClientId();\n if (!clientId) {\n return;\n }\n\n const merchantId = await sdkConfigStore.resolveMerchantId();\n if (!merchantId) {\n return;\n }\n\n const storageKey = `${ENSURE_STORAGE_PREFIX}${merchantId}`;\n if (window.sessionStorage.getItem(storageKey)) {\n return;\n }\n\n try {\n const backendUrl = getBackendUrl();\n const response = await fetch(`${backendUrl}/user/identity/ensure`, {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"x-wallet-sdk-auth\": interactionToken,\n \"x-frak-client-id\": clientId,\n },\n body: JSON.stringify({ merchantId }),\n });\n\n if (response.ok) {\n window.sessionStorage.setItem(storageKey, \"1\");\n }\n } catch {\n // Fire-and-forget — retry on next session\n }\n}\n","import type { FrakClient, GetMerchantInformationReturnType } from \"../types\";\nimport { withCache } from \"../utils/cache\";\n\n/**\n * Fetch the current merchant information (name, rewards, tiers) from the wallet iframe.\n *\n * Results are cached in memory for 30 seconds by default. Concurrent calls\n * while a request is in-flight are deduplicated automatically.\n *\n * @param client - The current Frak Client\n * @param options - Optional cache configuration\n * @param options.cacheTime - Time in ms to cache the result. Default: 30_000 (30s). Set to 0 to disable.\n * @returns The merchant information including available reward tiers\n *\n * @see {@link @frak-labs/core-sdk!index.GetMerchantInformationReturnType | `GetMerchantInformationReturnType`} for the return type shape\n */\nexport async function getMerchantInformation(\n client: FrakClient,\n options?: { cacheTime?: number }\n): Promise<GetMerchantInformationReturnType> {\n return withCache(\n () =>\n client.request({\n method: \"frak_getMerchantInformation\",\n }),\n {\n cacheKey: \"frak_getMerchantInformation\",\n cacheTime: options?.cacheTime,\n }\n );\n}\n","import type { FrakClient } from \"../types\";\nimport { withCache } from \"../utils/cache\";\n\n/**\n * Fetch a merge token for the current anonymous identity.\n *\n * Used by in-app browser redirect flows to preserve identity\n * when switching from a WebView to the system browser.\n * The token is appended as `?fmt=` to the redirect URL.\n *\n * Results are cached in memory for 30 seconds by default. Concurrent calls\n * while a request is in-flight are deduplicated automatically.\n *\n * @param client - The current Frak Client\n * @param options - Optional cache configuration\n * @param options.cacheTime - Time in ms to cache the result. Default: 30_000 (30s). Set to 0 to disable.\n * @returns The merge token string, or null if unavailable\n */\nexport async function getMergeToken(\n client: FrakClient,\n options?: { cacheTime?: number }\n): Promise<string | null> {\n return withCache(\n () =>\n client.request({\n method: \"frak_getMergeToken\",\n }),\n {\n cacheKey: \"frak_getMergeToken\",\n cacheTime: options?.cacheTime,\n }\n );\n}\n","import type { FrakClient, UserReferralStatusType } from \"../types\";\nimport { withCache } from \"../utils/cache\";\n\n/**\n * Fetch the current user's referral status on the current merchant.\n *\n * The listener resolves the user's identity (via clientId or wallet session)\n * and checks whether a referral link exists where the user is the referee.\n *\n * Results are cached in memory for 30 seconds by default. Concurrent calls\n * while a request is in-flight are deduplicated automatically.\n *\n * Returns `null` when the user's identity cannot be resolved.\n *\n * @param client - The current Frak Client\n * @param options - Optional cache configuration\n * @param options.cacheTime - Time in ms to cache the result. Default: 30_000 (30s). Set to 0 to disable.\n * @returns The user's referral status, or `null` if identity cannot be resolved\n *\n * @example\n * ```ts\n * const status = await getUserReferralStatus(client);\n * if (status?.isReferred) {\n * console.log(\"User was referred to this merchant\");\n * }\n * ```\n */\nexport async function getUserReferralStatus(\n client: FrakClient,\n options?: { cacheTime?: number }\n): Promise<UserReferralStatusType | null> {\n return withCache(\n () =>\n client.request({\n method: \"frak_getUserReferralStatus\",\n }),\n {\n cacheKey: \"frak_getUserReferralStatus\",\n cacheTime: options?.cacheTime,\n }\n );\n}\n","import type {\n FrakClient,\n PrepareSsoParamsType,\n PrepareSsoReturnType,\n} from \"../types\";\n\n/**\n * Generate SSO URL without opening popup\n *\n * This is a **synchronous**, client-side function that generates the SSO URL\n * without any RPC calls to the wallet iframe. Use this when you need:\n * - Custom URL modifications before opening popup\n * - Pre-generation for advanced popup strategies\n * - URL inspection/logging before SSO flow\n *\n * @param client - The current Frak Client\n * @param args - The SSO parameters\n * @returns Object containing the generated ssoUrl\n *\n * @example\n * ```ts\n * // Generate URL for inspection\n * const { ssoUrl } = prepareSso(client, {\n * metadata: { logoUrl: \"...\" },\n * directExit: true\n * });\n * console.log(\"Opening SSO:\", ssoUrl);\n *\n * // Add custom params\n * const customUrl = `${ssoUrl}&tracking=abc123`;\n * await openSso(client, { metadata, ssoPopupUrl: customUrl });\n * ```\n *\n * @remarks\n * For most use cases, just use `openSso()` which handles URL generation automatically.\n * Only use `prepareSso()` when you need explicit control over the URL.\n */\nexport async function prepareSso(\n client: FrakClient,\n args: PrepareSsoParamsType\n): Promise<PrepareSsoReturnType> {\n const { metadata, customizations } = client.config;\n\n return await client.request({\n method: \"frak_prepareSso\",\n params: [args, metadata.name, customizations?.css],\n });\n}\n","import { getClientId } from \"../config/clientId\";\nimport type { FrakClient } from \"../types\";\nimport type { SendInteractionParamsType } from \"../types/rpc/interaction\";\n\n/**\n * Send an interaction to the backend via the listener RPC.\n * Fire-and-forget: errors are caught and logged, not thrown.\n *\n * @param client - The Frak client instance\n * @param params - The interaction parameters\n *\n * @description Sends a user interaction event through the wallet iframe RPC. Supports three interaction types: arrival tracking, sharing events, and custom interactions.\n *\n * @example\n * Track a user arrival with referral attribution:\n * ```ts\n * await sendInteraction(client, {\n * type: \"arrival\",\n * referrerWallet: \"0x1234...abcd\",\n * landingUrl: window.location.href,\n * utmSource: \"twitter\",\n * utmMedium: \"social\",\n * utmCampaign: \"launch-2026\",\n * });\n * ```\n *\n * @example\n * Track a sharing event:\n * ```ts\n * await sendInteraction(client, { type: \"sharing\" });\n * ```\n *\n * @example\n * Send a custom interaction:\n * ```ts\n * await sendInteraction(client, {\n * type: \"custom\",\n * customType: \"newsletter_signup\",\n * data: { email: \"user@example.com\" },\n * });\n * ```\n */\nexport async function sendInteraction(\n client: FrakClient,\n params: SendInteractionParamsType\n): Promise<void> {\n try {\n await client.request({\n method: \"frak_sendInteraction\",\n params: [params, { clientId: getClientId() }],\n });\n } catch {\n // Silent failure - fire-and-forget\n console.warn(\"[Frak SDK] Failed to send interaction:\", params.type);\n }\n}\n","import { getClientId } from \"../../config/clientId\";\nimport { FrakContextManager } from \"../../context\";\nimport { areAddressesEqual } from \"../../context/address\";\nimport type {\n FrakClient,\n FrakContext,\n FrakContextV2,\n WalletStatusReturnType,\n} from \"../../types\";\nimport { isV1Context, isV2Context } from \"../../types\";\nimport { trackEvent } from \"../../utils\";\nimport { sendInteraction } from \"../sendInteraction\";\n\n/**\n * Options for the referral auto-interaction process.\n */\nexport type ProcessReferralOptions = {\n /**\n * If true, always replace the URL with the current user's referral context\n * so the next visitor gets referred by this user.\n * @defaultValue false\n */\n alwaysAppendUrl?: boolean;\n /**\n * Merchant ID for building the current user's referral context.\n * Required when `alwaysAppendUrl` is true and the incoming context is V1.\n * For V2 contexts, the merchantId is already embedded in the context.\n */\n merchantId?: string;\n};\n\n/**\n * The different states of the referral process.\n * @inline\n */\ntype ReferralState =\n | \"idle\"\n | \"processing\"\n | \"success\"\n | \"no-referrer\"\n | \"self-referral\";\n\n/**\n * Track an arrival event if the context version is recognized.\n * Sends both tracking analytics and the arrival interaction RPC.\n *\n * @returns true if the context was valid and tracked, false otherwise\n */\nfunction trackArrivalIfValid(\n client: FrakClient,\n frakContext: FrakContext,\n walletStatus?: WalletStatusReturnType\n): boolean {\n if (isV2Context(frakContext)) {\n trackEvent(client, \"user_referred_started\", {\n referrer_client_id: frakContext.c,\n referrer_wallet: frakContext.w,\n wallet_status: walletStatus?.key,\n });\n sendInteraction(client, {\n type: \"arrival\",\n referrerClientId: frakContext.c,\n referrerMerchantId: frakContext.m,\n referrerWallet: frakContext.w,\n referralTimestamp: frakContext.t,\n });\n return true;\n }\n\n if (isV1Context(frakContext)) {\n trackEvent(client, \"user_referred_started\", {\n referrer: frakContext.r,\n wallet_status: walletStatus?.key,\n });\n sendInteraction(client, {\n type: \"arrival\",\n referrerWallet: frakContext.r,\n });\n return true;\n }\n\n return false;\n}\n\n/**\n * Build a V2 context representing the current user for URL replacement.\n *\n * Emits both `c` (anonymous clientId) and `w` (wallet) when available — wallet\n * is the preferred identity signal and takes attribution precedence downstream.\n * Returns null when neither identifier is available.\n */\nfunction buildCurrentUserContext(\n merchantId: string,\n wallet?: WalletStatusReturnType[\"wallet\"]\n): FrakContextV2 | null {\n const clientId = getClientId();\n if (!clientId && !wallet) return null;\n return {\n v: 2,\n m: merchantId,\n t: Math.floor(Date.now() / 1000),\n ...(clientId ? { c: clientId } : {}),\n ...(wallet ? { w: wallet } : {}),\n };\n}\n\n/**\n * Client-side self-referral preflight check.\n * Prevents unnecessary backend round-trips for obvious self-referrals.\n */\nfunction isSelfReferral(\n frakContext: FrakContext,\n walletStatus?: WalletStatusReturnType\n): boolean {\n if (isV2Context(frakContext)) {\n // Wallet match takes precedence — it's the strongest signal we have.\n if (frakContext.w && walletStatus?.wallet) {\n return areAddressesEqual(frakContext.w, walletStatus.wallet);\n }\n if (frakContext.c) {\n return getClientId() === frakContext.c;\n }\n return false;\n }\n if (isV1Context(frakContext) && walletStatus?.wallet) {\n return areAddressesEqual(frakContext.r, walletStatus.wallet);\n }\n return false;\n}\n\n/**\n * Handle the full referral interaction flow:\n *\n * 1. Check if the user has been referred (if not, early exit)\n * 2. Preflight self-referral check (if yes, early exit)\n * 3. Track the arrival event\n * 4. Replace the current URL with the user's own referral context\n * 5. Return the resulting referral state\n *\n * @param client - The current Frak Client\n * @param args\n * @param args.walletStatus - The current user wallet status\n * @param args.frakContext - The referral context parsed from the URL\n * @param args.options - Options for URL replacement and merchant context\n * @returns The referral state\n *\n * @see {@link @frak-labs/core-sdk!ModalStepTypes} for modal step types\n */\nexport function processReferral(\n client: FrakClient,\n {\n walletStatus,\n frakContext,\n options,\n }: {\n walletStatus?: WalletStatusReturnType;\n frakContext?: FrakContext | null;\n options?: ProcessReferralOptions;\n }\n): ReferralState {\n if (!frakContext) {\n return \"no-referrer\";\n }\n\n if (isSelfReferral(frakContext, walletStatus)) {\n return \"self-referral\";\n }\n\n if (!trackArrivalIfValid(client, frakContext, walletStatus)) {\n return \"no-referrer\";\n }\n\n // V2 context embeds merchantId; V1 falls back to options\n const contextMerchantId = isV2Context(frakContext)\n ? frakContext.m\n : options?.merchantId;\n\n const replaceContext =\n options?.alwaysAppendUrl && contextMerchantId\n ? buildCurrentUserContext(contextMerchantId, walletStatus?.wallet)\n : null;\n\n FrakContextManager.replaceUrl({\n url: window.location?.href,\n context: replaceContext,\n });\n\n trackEvent(client, \"user_referred_completed\", {\n status: \"success\",\n });\n\n return \"success\";\n}\n","import { FrakContextManager } from \"../../context\";\nimport type { FrakClient } from \"../../types\";\nimport { watchWalletStatus } from \"../index\";\nimport {\n type ProcessReferralOptions,\n processReferral,\n} from \"./processReferral\";\n\n/**\n * Function used to handle referral interactions\n * @param client - The current Frak Client\n * @param args\n * @param args.options - Some options for the referral interaction\n *\n * @returns A promise with the resulting referral state, or undefined in case of an error\n *\n * @description This function will automatically handle the referral interaction process\n *\n * @see {@link processReferral} for more details on the automatic referral handling process\n */\nexport async function referralInteraction(\n client: FrakClient,\n {\n options,\n }: {\n options?: ProcessReferralOptions;\n } = {}\n) {\n // Get the current frak context\n const frakContext = FrakContextManager.parse({\n url: window.location.href,\n });\n\n // Get the current wallet status\n const currentWalletStatus = await watchWalletStatus(client);\n\n try {\n return processReferral(client, {\n walletStatus: currentWalletStatus,\n frakContext,\n options,\n });\n } catch (error) {\n console.warn(\"Error processing referral\", { error });\n }\n return;\n}\n","import type { FrakClient } from \"../../types\";\nimport { referralInteraction } from \"./referralInteraction\";\n\n/**\n * Custom event name dispatched on successful referral processing.\n *\n * Fired once per page load when a valid referral context is found in the URL\n * and successfully tracked. Consumers (e.g. `<frak-banner>`) listen for this\n * to display a referral success message.\n */\nexport const REFERRAL_SUCCESS_EVENT = \"frak:referral-success\";\n\n/**\n * Process referral context and emit a DOM event on success.\n *\n * - Calls {@link referralInteraction} to detect and track any referral in the URL\n * - On `\"success\"`, dispatches a bare {@link REFERRAL_SUCCESS_EVENT} on `window`\n * - Silently swallows errors (fire-and-forget during SDK init)\n *\n * @param client - The initialized Frak client\n */\nexport async function setupReferral(client: FrakClient): Promise<void> {\n try {\n const state = await referralInteraction(client);\n\n if (state === \"success\") {\n window.dispatchEvent(new Event(REFERRAL_SUCCESS_EVENT));\n }\n } catch (error) {\n console.warn(\"[Frak] Referral setup failed\", error);\n }\n}\n","import { getBackendUrl } from \"../config/backendUrl\";\nimport { getClientId } from \"../config/clientId\";\nimport { sdkConfigStore } from \"../config/sdkConfigStore\";\n\n/**\n * Function used to track the status of a purchase\n * when a purchase is tracked, the `purchaseCompleted` interactions will be automatically send for the user when we receive the purchase confirmation via webhook.\n *\n * @param args.customerId - The customer id that made the purchase (on your side)\n * @param args.orderId - The order id of the purchase (on your side)\n * @param args.token - The token of the purchase\n * @param args.merchantId - Optional explicit merchant id to use for the tracking request\n *\n * @description This function will send a request to the backend to listen for the purchase status.\n *\n * @example\n * async function trackPurchase(checkout) {\n * const payload = {\n * customerId: checkout.order.customer.id,\n * orderId: checkout.order.id,\n * token: checkout.token,\n * merchantId: \"your-merchant-id\",\n * };\n *\n * await trackPurchaseStatus(payload);\n * }\n *\n * @remarks\n * - Merchant id is resolved in this order: explicit `args.merchantId`, then `sdkConfigStore.resolveMerchantId()` (config store → sessionStorage → backend fetch).\n * - This function supports anonymous users and will use the `x-frak-client-id` header when available.\n * - At least one identity source must exist (`frak-wallet-interaction-token` or `x-frak-client-id`), otherwise the tracking request is skipped.\n * - This function will print a warning if used in a non-browser environment or if no identity / merchant id can be resolved.\n */\nexport async function trackPurchaseStatus(args: {\n customerId: string | number;\n orderId: string | number;\n token: string;\n merchantId?: string;\n}) {\n if (typeof window === \"undefined\") {\n console.warn(\"[Frak] No window found, can't track purchase\");\n return;\n }\n\n const interactionToken = window.sessionStorage.getItem(\n \"frak-wallet-interaction-token\"\n );\n\n const clientId = getClientId();\n if (!interactionToken && !clientId) {\n console.warn(\"[Frak] No identity found, skipping purchase check\");\n return;\n }\n\n const merchantId =\n args.merchantId ?? (await sdkConfigStore.resolveMerchantId());\n\n if (!merchantId) {\n console.warn(\"[Frak] No merchant id found, skipping purchase check\");\n return;\n }\n\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n };\n\n if (interactionToken) {\n headers[\"x-wallet-sdk-auth\"] = interactionToken;\n }\n\n if (clientId) {\n headers[\"x-frak-client-id\"] = clientId;\n }\n\n // Submit the listening request\n const backendUrl = getBackendUrl();\n await fetch(`${backendUrl}/user/track/purchase`, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n customerId: args.customerId,\n orderId: args.orderId,\n token: args.token,\n merchantId,\n }),\n });\n}\n","import { Deferred } from \"@frak-labs/frame-connector\";\nimport type { FrakClient } from \"../types/client\";\nimport type { WalletStatusReturnType } from \"../types/rpc/walletStatus\";\nimport { ensureIdentity } from \"./ensureIdentity\";\n\n/**\n * Function used to watch the current frak wallet status\n * @param client - The current Frak Client\n * @param callback - The callback that will receive any wallet status change\n * @returns A promise resolving with the initial wallet status\n *\n * @description This function will return the current wallet status, and will listen to any change in the wallet status.\n *\n * @example\n * await watchWalletStatus(frakConfig, (status: WalletStatusReturnType) => {\n * if (status.key === \"connected\") {\n * console.log(\"Wallet connected:\", status.wallet);\n * } else {\n * console.log(\"Wallet not connected\");\n * }\n * });\n */\nexport function watchWalletStatus(\n client: FrakClient,\n callback?: (status: WalletStatusReturnType) => void\n): Promise<WalletStatusReturnType> {\n // If no callback is provided, just do a request with deferred result\n if (!callback) {\n return client\n .request({ method: \"frak_listenToWalletStatus\" })\n .then((result) => {\n // Handle side effects of this request\n walletStatusSideEffect(client, result);\n\n // Return the result\n return result;\n });\n }\n\n // Otherwise, listen to the wallet status and return the first one received\n const firstResult = new Deferred<WalletStatusReturnType>();\n let hasResolved = false;\n\n // Start the listening request, and return the first result\n client.listenerRequest(\n {\n method: \"frak_listenToWalletStatus\",\n },\n (status) => {\n // Handle side effects of this request\n walletStatusSideEffect(client, status);\n\n // Transmit the status to the callback\n callback(status);\n\n // If the promise hasn't resolved yet, resolve it\n if (!hasResolved) {\n firstResult.resolve(status);\n hasResolved = true;\n }\n }\n );\n\n return firstResult.promise;\n}\n\n/**\n * Helper to save a potential interaction token\n * @param interactionToken\n */\nfunction walletStatusSideEffect(\n client: FrakClient,\n status: WalletStatusReturnType\n) {\n if (typeof window === \"undefined\") {\n return;\n }\n\n // Update the global properties\n client.openPanel?.setGlobalProperties({\n wallet: status.wallet ?? null,\n });\n\n if (status.interactionToken) {\n window.sessionStorage.setItem(\n \"frak-wallet-interaction-token\",\n status.interactionToken\n );\n ensureIdentity(status.interactionToken);\n } else {\n window.sessionStorage.removeItem(\"frak-wallet-interaction-token\");\n }\n}\n","import type {\n DisplayModalParamsType,\n FinalActionType,\n FinalModalStepType,\n FrakClient,\n LoginModalStepType,\n ModalRpcMetadata,\n ModalRpcStepsResultType,\n ModalStepTypes,\n SendTransactionModalStepType,\n} from \"../../types\";\nimport { displayModal } from \"../displayModal\";\n\n/**\n * Represent the type of the modal step builder\n */\nexport type ModalStepBuilder<\n Steps extends ModalStepTypes[] = ModalStepTypes[],\n> = {\n /**\n * The current modal params\n */\n params: DisplayModalParamsType<Steps>;\n /**\n * Add a send transaction step to the modal\n */\n sendTx: (\n options: SendTransactionModalStepType[\"params\"]\n ) => ModalStepBuilder<[...Steps, SendTransactionModalStepType]>;\n /**\n * Add a final step of type reward to the modal\n */\n reward: (\n options?: Omit<FinalModalStepType[\"params\"], \"action\">\n ) => ModalStepBuilder<[...Steps, FinalModalStepType]>;\n /**\n * Add a final step of type sharing to the modal\n */\n sharing: (\n sharingOptions?: Extract<\n FinalActionType,\n { key: \"sharing\" }\n >[\"options\"],\n options?: Omit<FinalModalStepType[\"params\"], \"action\">\n ) => ModalStepBuilder<[...Steps, FinalModalStepType]>;\n /**\n * Display the modal\n * @param metadataOverride - Function returning optional metadata to override the current modal metadata\n * @param placement - Optional placement ID to associate with this modal display\n */\n display: (\n metadataOverride?: (\n current?: ModalRpcMetadata\n ) => ModalRpcMetadata | undefined,\n placement?: string\n ) => Promise<ModalRpcStepsResultType<Steps>>;\n};\n\n/**\n * Represent the output type of the modal builder\n */\nexport type ModalBuilder = ModalStepBuilder<[LoginModalStepType]>;\n\n/**\n * Helper to craft Frak modal, and share a base initial config\n * @param client - The current Frak Client\n * @param args\n * @param args.metadata - Common modal metadata (customisation, language etc)\n * @param args.login - Login step parameters\n *\n * @description This function will create a modal builder with the provided metadata and login parameters.\n *\n * @example\n * Here is an example of how to use the `modalBuilder` to create and display a sharing modal:\n *\n * ```js\n * // Create the modal builder\n * const modalBuilder = window.FrakSDK.modalBuilder(frakClient, baseModalConfig);\n *\n * // Configure the information to be shared via the sharing link\n * const sharingConfig = {\n * popupTitle: \"Share this with your friends\",\n * text: \"Discover our product!\",\n * link: window.location.href,\n * };\n *\n * // Display the sharing modal\n * function modalShare() {\n * modalBuilder.sharing(sharingConfig).display();\n * }\n * ```\n *\n * @see {@link ModalStepTypes} for more info about each modal step types and their parameters\n * @see {@link ModalRpcMetadata} for more info about the metadata that can be passed to the modal\n * @see {@link ModalRpcStepsResultType} for more info about the result of each modal steps\n * @see {@link displayModal} for more info about how the modal is displayed\n */\nexport function modalBuilder(\n client: FrakClient,\n {\n metadata,\n login,\n }: {\n metadata?: ModalRpcMetadata;\n login?: LoginModalStepType[\"params\"];\n }\n): ModalBuilder {\n // Build the initial modal params\n const baseParams: DisplayModalParamsType<[LoginModalStepType]> = {\n steps: {\n login: login ?? {},\n },\n metadata,\n };\n\n // Return the step builder\n return modalStepsBuilder(client, baseParams);\n}\n\n/**\n * Modal step builder, allowing to add new steps to the modal, and to build and display it\n */\nfunction modalStepsBuilder<CurrentSteps extends ModalStepTypes[]>(\n client: FrakClient,\n params: DisplayModalParamsType<CurrentSteps>\n): ModalStepBuilder<CurrentSteps> {\n // Function add the send tx step\n function sendTx(options: SendTransactionModalStepType[\"params\"]) {\n return modalStepsBuilder<\n [...CurrentSteps, SendTransactionModalStepType]\n >(client, {\n ...params,\n steps: {\n ...params.steps,\n sendTransaction: options,\n },\n } as DisplayModalParamsType<\n [...CurrentSteps, SendTransactionModalStepType]\n >);\n }\n\n // Function to add a reward step at the end\n function reward(options?: Omit<FinalModalStepType[\"params\"], \"action\">) {\n return modalStepsBuilder<[...CurrentSteps, FinalModalStepType]>(\n client,\n {\n ...params,\n steps: {\n ...params.steps,\n final: {\n ...options,\n action: { key: \"reward\" },\n },\n },\n } as DisplayModalParamsType<[...CurrentSteps, FinalModalStepType]>\n );\n }\n\n // Function to add sharing step at the end\n function sharing(\n sharingOptions?: Extract<\n FinalActionType,\n { key: \"sharing\" }\n >[\"options\"],\n options?: Omit<FinalModalStepType[\"params\"], \"action\">\n ) {\n return modalStepsBuilder<[...CurrentSteps, FinalModalStepType]>(\n client,\n {\n ...params,\n steps: {\n ...params.steps,\n final: {\n ...options,\n action: { key: \"sharing\", options: sharingOptions },\n },\n },\n } as DisplayModalParamsType<[...CurrentSteps, FinalModalStepType]>\n );\n }\n\n async function display(\n metadataOverride?: (\n current?: ModalRpcMetadata\n ) => ModalRpcMetadata | undefined,\n placement?: string\n ) {\n if (metadataOverride) {\n params.metadata = metadataOverride(params.metadata ?? {});\n }\n return await displayModal(client, params, placement);\n }\n\n return {\n params,\n sendTx,\n reward,\n sharing,\n display,\n };\n}\n","import type {\n FrakClient,\n ModalRpcMetadata,\n SendTransactionModalStepType,\n SendTransactionReturnType,\n} from \"../../types\";\nimport { displayModal } from \"../displayModal\";\n\n/**\n * Parameters to directly show a modal used to send a transaction\n * @inline\n */\nexport type SendTransactionParams = {\n /**\n * The transaction to be sent (either a single tx or multiple ones)\n */\n tx: SendTransactionModalStepType[\"params\"][\"tx\"];\n /**\n * Custom metadata to be passed to the modal\n */\n metadata?: ModalRpcMetadata;\n};\n\n/**\n * Function used to send a user transaction, simple wrapper around the displayModal function to ease the send transaction process\n * @param client - The current Frak Client\n * @param args - The parameters\n * @returns The hash of the transaction that was sent in a promise\n *\n * @description This function will display a modal to the user with the provided transaction and metadata.\n *\n * @example\n * const { hash } = await sendTransaction(frakConfig, {\n * tx: {\n * to: \"0xdeadbeef\",\n * value: toHex(100n),\n * },\n * metadata: {\n * header: {\n * title: \"Sending eth\",\n * },\n * context: \"Send 100wei to 0xdeadbeef\",\n * },\n * });\n * console.log(\"Transaction hash:\", hash);\n */\nexport async function sendTransaction(\n client: FrakClient,\n { tx, metadata }: SendTransactionParams\n): Promise<SendTransactionReturnType> {\n // Trigger a modal with login options\n const result = await displayModal(client, {\n metadata,\n steps: {\n login: {},\n sendTransaction: { tx },\n },\n });\n\n // Return the tx result only\n return result.sendTransaction;\n}\n","import type {\n FrakClient,\n ModalRpcMetadata,\n SiweAuthenticateReturnType,\n SiweAuthenticationParams,\n} from \"../../types\";\nimport { displayModal } from \"../displayModal\";\n\n/**\n * Generate a random EIP-4361-compatible nonce.\n *\n * Matches viem/siwe's `generateSiweNonce` shape (96 alphanumeric chars) but\n * inlined here to avoid pulling viem's runtime utilities into the bundle.\n * Uses `crypto.getRandomValues` when available (browsers, modern Node) and\n * falls back to `Math.random` for the rare environment without WebCrypto.\n */\nfunction generateSiweNonce(length = 96): string {\n const byteCount = Math.ceil(length / 2);\n let hex = \"\";\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n const bytes = new Uint8Array(byteCount);\n crypto.getRandomValues(bytes);\n for (let i = 0; i < bytes.length; i++) {\n hex += bytes[i].toString(16).padStart(2, \"0\");\n }\n } else {\n for (let i = 0; i < byteCount; i++) {\n hex += ((Math.random() * 256) | 0).toString(16).padStart(2, \"0\");\n }\n }\n return hex.substring(0, length);\n}\n\n/**\n * Parameter used to directly show a modal used to authenticate with SIWE\n * @inline\n */\nexport type SiweAuthenticateModalParams = {\n /**\n * Partial SIWE params, since we can rebuild them from the SDK if they are empty\n *\n * If no parameters provider, some fields will be recomputed from the current configuration and environment.\n * - `statement` will be set to a default value\n * - `nonce` will be generated\n * - `uri` will be set to the current domain\n * - `version` will be set to \"1\"\n * - `domain` will be set to the current window domain\n *\n * @default {}\n */\n siwe?: Partial<SiweAuthenticationParams>;\n /**\n * Custom metadata to be passed to the modal\n */\n metadata?: ModalRpcMetadata;\n};\n\n/**\n * Function used to launch a siwe authentication\n * @param client - The current Frak Client\n * @param args - The parameters\n * @returns The SIWE authentication result (message + signature) in a promise\n *\n * @description This function will display a modal to the user with the provided SIWE parameters and metadata.\n *\n * @example\n * import { siweAuthenticate } from \"@frak-labs/core-sdk/actions\";\n * import { parseSiweMessage } from \"viem/siwe\";\n *\n * const { signature, message } = await siweAuthenticate(frakConfig, {\n * siwe: {\n * statement: \"Sign in to My App\",\n * domain: \"my-app.com\",\n * expirationTimeTimestamp: Date.now() + 1000 * 60 * 5,\n * },\n * metadata: {\n * header: {\n * title: \"Sign in\",\n * },\n * context: \"Sign in to My App\",\n * },\n * });\n * console.log(\"Parsed final message:\", parseSiweMessage(message));\n * console.log(\"Siwe signature:\", signature);\n */\nexport async function siweAuthenticate(\n client: FrakClient,\n { siwe, metadata }: SiweAuthenticateModalParams\n): Promise<SiweAuthenticateReturnType> {\n const effectiveDomain = client.config?.domain ?? window.location.host;\n const realStatement =\n siwe?.statement ??\n `I confirm that I want to use my Frak wallet on: ${client.config.metadata.name}`;\n\n // Fill up the siwe request params\n const builtSiwe: SiweAuthenticationParams = {\n ...siwe,\n statement: realStatement,\n nonce: siwe?.nonce ?? generateSiweNonce(),\n uri: siwe?.uri ?? `https://${effectiveDomain}`,\n version: siwe?.version ?? \"1\",\n domain: effectiveDomain,\n };\n\n // Trigger a modal with login options\n const result = await displayModal(client, {\n metadata,\n steps: {\n login: {},\n siweAuthenticate: {\n siwe: builtSiwe,\n },\n },\n });\n\n // Return the SIWE result only\n return result.siweAuthenticate;\n}\n"],"mappings":"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