@frak-labs/core-sdk 0.1.1 → 0.2.0-beta.7898df5b

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.
Files changed (130) hide show
  1. package/README.md +58 -0
  2. package/cdn/bundle.js +14 -0
  3. package/dist/actions.cjs +1 -1
  4. package/dist/actions.d.cts +3 -3
  5. package/dist/actions.d.ts +3 -3
  6. package/dist/actions.js +1 -1
  7. package/dist/bundle.cjs +1 -1
  8. package/dist/bundle.d.cts +4 -6
  9. package/dist/bundle.d.ts +4 -6
  10. package/dist/bundle.js +1 -1
  11. package/dist/computeLegacyProductId-CCAZvLa5.d.cts +537 -0
  12. package/dist/computeLegacyProductId-b5cUWdAm.d.ts +537 -0
  13. package/dist/index.cjs +1 -1
  14. package/dist/index.d.cts +3 -4
  15. package/dist/index.d.ts +3 -4
  16. package/dist/index.js +1 -1
  17. package/dist/{openSso-D--Airj6.d.cts → openSso-B0g7-807.d.cts} +173 -136
  18. package/dist/{openSso-DsKJ4y0j.d.ts → openSso-CMzwvaCa.d.ts} +173 -136
  19. package/dist/setupClient-BICl5fdX.js +13 -0
  20. package/dist/setupClient-nl8Dhh4V.cjs +13 -0
  21. package/dist/siweAuthenticate-BWmI2_TN.cjs +1 -0
  22. package/dist/{index-d8xS4ryI.d.ts → siweAuthenticate-CVigMOxz.d.cts} +113 -92
  23. package/dist/{index-C6FxkWPC.d.cts → siweAuthenticate-CnCZ7mok.d.ts} +113 -92
  24. package/dist/siweAuthenticate-zczqxm0a.js +1 -0
  25. package/dist/trackEvent-CeLFVzZn.js +1 -0
  26. package/dist/trackEvent-Ew5r5zfI.cjs +1 -0
  27. package/package.json +11 -22
  28. package/src/actions/displayEmbeddedWallet.ts +1 -0
  29. package/src/actions/displayModal.test.ts +12 -11
  30. package/src/actions/displayModal.ts +7 -18
  31. package/src/actions/ensureIdentity.ts +68 -0
  32. package/src/actions/{getProductInformation.test.ts → getMerchantInformation.test.ts} +33 -50
  33. package/src/actions/getMerchantInformation.ts +16 -0
  34. package/src/actions/index.ts +3 -2
  35. package/src/actions/openSso.ts +4 -2
  36. package/src/actions/referral/processReferral.test.ts +117 -242
  37. package/src/actions/referral/processReferral.ts +134 -204
  38. package/src/actions/referral/referralInteraction.test.ts +4 -12
  39. package/src/actions/referral/referralInteraction.ts +3 -13
  40. package/src/actions/sendInteraction.ts +46 -22
  41. package/src/actions/trackPurchaseStatus.test.ts +354 -141
  42. package/src/actions/trackPurchaseStatus.ts +48 -11
  43. package/src/actions/watchWalletStatus.ts +2 -3
  44. package/src/actions/wrapper/modalBuilder.test.ts +0 -14
  45. package/src/actions/wrapper/modalBuilder.ts +3 -12
  46. package/src/bundle.ts +0 -1
  47. package/src/clients/createIFrameFrakClient.ts +10 -5
  48. package/src/clients/transports/iframeLifecycleManager.test.ts +163 -4
  49. package/src/clients/transports/iframeLifecycleManager.ts +172 -33
  50. package/src/constants/interactionTypes.ts +12 -41
  51. package/src/index.ts +27 -16
  52. package/src/types/config.ts +6 -0
  53. package/src/types/context.ts +48 -6
  54. package/src/types/index.ts +15 -11
  55. package/src/types/lifecycle/client.ts +24 -1
  56. package/src/types/lifecycle/iframe.ts +6 -0
  57. package/src/types/rpc/displayModal.ts +2 -4
  58. package/src/types/rpc/embedded/index.ts +2 -2
  59. package/src/types/rpc/interaction.ts +31 -39
  60. package/src/types/rpc/merchantInformation.ts +77 -0
  61. package/src/types/rpc/modal/index.ts +0 -4
  62. package/src/types/rpc/modal/login.ts +5 -1
  63. package/src/types/rpc/walletStatus.ts +1 -7
  64. package/src/types/rpc.ts +22 -30
  65. package/src/types/tracking.ts +31 -0
  66. package/src/utils/FrakContext.test.ts +270 -186
  67. package/src/utils/FrakContext.ts +78 -56
  68. package/src/utils/backendUrl.test.ts +83 -0
  69. package/src/utils/backendUrl.ts +62 -0
  70. package/src/utils/clientId.test.ts +41 -0
  71. package/src/utils/clientId.ts +43 -0
  72. package/src/utils/compression/compress.test.ts +1 -1
  73. package/src/utils/compression/compress.ts +2 -2
  74. package/src/utils/compression/decompress.test.ts +8 -4
  75. package/src/utils/compression/decompress.ts +2 -2
  76. package/src/utils/{computeProductId.ts → computeLegacyProductId.ts} +2 -2
  77. package/src/utils/constants.ts +5 -0
  78. package/src/utils/deepLinkWithFallback.test.ts +243 -0
  79. package/src/utils/deepLinkWithFallback.ts +103 -0
  80. package/src/utils/formatAmount.ts +6 -0
  81. package/src/utils/iframeHelper.test.ts +18 -5
  82. package/src/utils/iframeHelper.ts +10 -3
  83. package/src/utils/index.ts +16 -1
  84. package/src/utils/merchantId.test.ts +653 -0
  85. package/src/utils/merchantId.ts +143 -0
  86. package/src/utils/sso.ts +18 -11
  87. package/src/utils/trackEvent.test.ts +23 -5
  88. package/src/utils/trackEvent.ts +13 -0
  89. package/cdn/bundle.iife.js +0 -14
  90. package/dist/actions-B5j-i1p0.cjs +0 -1
  91. package/dist/actions-q090Z0oR.js +0 -1
  92. package/dist/index-7OZ39x1U.d.ts +0 -195
  93. package/dist/index-CRsQWnTs.d.cts +0 -351
  94. package/dist/index-Ck1hudEi.d.ts +0 -351
  95. package/dist/index-zDq-VlKx.d.cts +0 -195
  96. package/dist/interaction-DMJ3ZfaF.d.cts +0 -45
  97. package/dist/interaction-KX1h9a7V.d.ts +0 -45
  98. package/dist/interactions-DnfM3oe0.js +0 -1
  99. package/dist/interactions-EIXhNLf6.cjs +0 -1
  100. package/dist/interactions.cjs +0 -1
  101. package/dist/interactions.d.cts +0 -2
  102. package/dist/interactions.d.ts +0 -2
  103. package/dist/interactions.js +0 -1
  104. package/dist/productTypes-BUkXJKZ7.cjs +0 -1
  105. package/dist/productTypes-CGb1MmBF.js +0 -1
  106. package/dist/src-1LQ4eLq5.js +0 -13
  107. package/dist/src-hW71KjPN.cjs +0 -13
  108. package/dist/trackEvent-CHnYa85W.js +0 -1
  109. package/dist/trackEvent-GuQm_1Nm.cjs +0 -1
  110. package/src/actions/getProductInformation.ts +0 -14
  111. package/src/actions/openSso.test.ts +0 -407
  112. package/src/actions/sendInteraction.test.ts +0 -219
  113. package/src/constants/interactionTypes.test.ts +0 -128
  114. package/src/constants/productTypes.test.ts +0 -130
  115. package/src/constants/productTypes.ts +0 -33
  116. package/src/interactions/index.ts +0 -5
  117. package/src/interactions/pressEncoder.test.ts +0 -215
  118. package/src/interactions/pressEncoder.ts +0 -53
  119. package/src/interactions/purchaseEncoder.test.ts +0 -291
  120. package/src/interactions/purchaseEncoder.ts +0 -99
  121. package/src/interactions/referralEncoder.test.ts +0 -170
  122. package/src/interactions/referralEncoder.ts +0 -47
  123. package/src/interactions/retailEncoder.test.ts +0 -107
  124. package/src/interactions/retailEncoder.ts +0 -37
  125. package/src/interactions/webshopEncoder.test.ts +0 -56
  126. package/src/interactions/webshopEncoder.ts +0 -30
  127. package/src/types/rpc/modal/openSession.ts +0 -25
  128. package/src/types/rpc/productInformation.ts +0 -59
  129. package/src/utils/computeProductId.test.ts +0 -80
  130. package/src/utils/sso.test.ts +0 -361
@@ -1,256 +1,186 @@
1
- import { FrakRpcError, RpcErrorCodes } from "@frak-labs/frame-connector";
2
- import { type Address, type Hex, isAddressEqual } from "viem";
3
- import { ReferralInteractionEncoder } from "../../interactions";
1
+ import { isAddressEqual } from "viem";
4
2
  import type {
5
- DisplayEmbeddedWalletParamsType,
6
3
  FrakClient,
7
4
  FrakContext,
5
+ FrakContextV2,
8
6
  WalletStatusReturnType,
9
7
  } from "../../types";
10
- import { FrakContextManager, trackEvent } from "../../utils";
11
- import { displayEmbeddedWallet, sendInteraction } from "../index";
8
+ import { isV1Context, isV2Context } from "../../types";
9
+ import { FrakContextManager, getClientId, trackEvent } from "../../utils";
10
+ import { sendInteraction } from "../sendInteraction";
12
11
 
13
12
  /**
14
- * The different states of the referral process
13
+ * Options for the referral auto-interaction process.
14
+ */
15
+ export type ProcessReferralOptions = {
16
+ /**
17
+ * If true, always replace the URL with the current user's referral context
18
+ * so the next visitor gets referred by this user.
19
+ * @defaultValue false
20
+ */
21
+ alwaysAppendUrl?: boolean;
22
+ /**
23
+ * Merchant ID for building the current user's referral context.
24
+ * Required when `alwaysAppendUrl` is true and the incoming context is V1.
25
+ * For V2 contexts, the merchantId is already embedded in the context.
26
+ */
27
+ merchantId?: string;
28
+ };
29
+
30
+ /**
31
+ * The different states of the referral process.
15
32
  * @inline
16
33
  */
17
34
  type ReferralState =
18
35
  | "idle"
19
36
  | "processing"
20
37
  | "success"
21
- | "no-wallet"
22
- | "no-session"
23
- | "error"
24
38
  | "no-referrer"
25
39
  | "self-referral";
26
40
 
27
41
  /**
28
- * Options for the referral auto-interaction process
29
- */
30
- export type ProcessReferralOptions = {
31
- /**
32
- * If we want to always append the url with the frak context or not
33
- * @defaultValue false
34
- */
35
- alwaysAppendUrl?: boolean;
36
- };
37
-
38
- /**
39
- * This function handle all the heavy lifting of the referral interaction process
40
- * 1. Check if the user has been referred or not (if not, early exit)
41
- * 2. Then check if the user is logged in or not
42
- * 2.1 If not logged in, try a soft login, if it fail, display a modal for the user to login
43
- * 3. Check if that's not a self-referral (if yes, early exit)
44
- * 4. Check if the user has an interaction session or not
45
- * 4.1 If not, display a modal for the user to open a session
46
- * 5. Push the referred interaction
47
- * 6. Update the current url with the right data
48
- * 7. Return the resulting referral state
42
+ * Track an arrival event if the context version is recognized.
43
+ * Sends both tracking analytics and the arrival interaction RPC.
49
44
  *
50
- * If any error occurs during the process, the function will catch it and return an error state
51
- *
52
- * @param client - The current Frak Client
53
- * @param args
54
- * @param args.walletStatus - The current user wallet status
55
- * @param args.frakContext - The current frak context
56
- * @param args.modalConfig - The modal configuration to display if the user is not logged in
57
- * @param args.productId - The product id to interact with (if not specified will be recomputed from the current domain)
58
- * @param args.options - Some options for the referral interaction
59
- * @returns A promise with the resulting referral state
60
- *
61
- * @see {@link displayModal} for more details about the displayed modal
62
- * @see {@link sendInteraction} for more details on the interaction submission part
63
- * @see {@link ReferralInteractionEncoder} for more details about the referred interaction
64
- * @see {@link @frak-labs/core-sdk!ModalStepTypes} for more details on each modal steps types
45
+ * @returns true if the context was valid and tracked, false otherwise
65
46
  */
66
- export async function processReferral(
47
+ function trackArrivalIfValid(
67
48
  client: FrakClient,
68
- {
69
- walletStatus,
70
- frakContext,
71
- modalConfig,
72
- productId,
73
- options,
74
- }: {
75
- walletStatus?: WalletStatusReturnType;
76
- frakContext?: Partial<FrakContext> | null;
77
- modalConfig?: DisplayEmbeddedWalletParamsType;
78
- productId?: Hex;
79
- options?: ProcessReferralOptions;
80
- }
81
- ) {
82
- // Early exit if we don't have any referral informations
83
- if (!frakContext?.r) {
84
- return "no-referrer";
85
- }
86
-
87
- // If we got a context, log an event
88
- trackEvent(client, "user_referred_started", {
89
- properties: {
90
- referrer: frakContext?.r,
91
- walletStatus: walletStatus?.key,
92
- },
93
- });
94
-
95
- // Helper to fetch a fresh wallet status
96
- let walletRequest = false;
97
- async function getFreshWalletStatus() {
98
- if (walletRequest) {
99
- return;
100
- }
101
- walletRequest = true;
102
- return ensureWalletConnected(client, {
103
- modalConfig: {
104
- ...modalConfig,
105
- loggedIn: {
106
- action: {
107
- key: "referred",
108
- },
109
- },
49
+ frakContext: FrakContext,
50
+ walletStatus?: WalletStatusReturnType
51
+ ): boolean {
52
+ const landingUrl =
53
+ typeof window !== "undefined" ? window.location.href : undefined;
54
+
55
+ if (isV2Context(frakContext)) {
56
+ trackEvent(client, "user_referred_started", {
57
+ properties: {
58
+ referrerClientId: frakContext.c,
59
+ walletStatus: walletStatus?.key,
110
60
  },
111
- walletStatus,
112
61
  });
113
- }
114
-
115
- // Helper function to push the interaction
116
- async function pushReferralInteraction(referrer: Address) {
117
- const interaction = ReferralInteractionEncoder.referred({
118
- referrer,
62
+ sendInteraction(client, {
63
+ type: "arrival",
64
+ referrerClientId: frakContext.c,
65
+ referrerMerchantId: frakContext.m,
66
+ referralTimestamp: frakContext.t,
67
+ landingUrl,
119
68
  });
120
- await sendInteraction(client, { productId, interaction });
69
+ return true;
121
70
  }
122
71
 
123
- try {
124
- // Do the core processing logic
125
- const { status, currentWallet } = await processReferralLogic({
126
- initialWalletStatus: walletStatus,
127
- getFreshWalletStatus,
128
- pushReferralInteraction,
129
- // We can enforce this type cause of the condition at the start
130
- frakContext: frakContext as Pick<FrakContext, "r">,
131
- });
132
-
133
- // Update the current url with the right data
134
- FrakContextManager.replaceUrl({
135
- url: window.location?.href,
136
- context: options?.alwaysAppendUrl ? { r: currentWallet } : null,
137
- });
138
-
139
- // Track the event
140
- trackEvent(client, "user_referred_completed", {
72
+ if (isV1Context(frakContext)) {
73
+ trackEvent(client, "user_referred_started", {
141
74
  properties: {
142
- status,
143
- referrer: frakContext?.r,
144
- wallet: currentWallet,
75
+ referrer: frakContext.r,
76
+ walletStatus: walletStatus?.key,
145
77
  },
146
78
  });
147
-
148
- return status;
149
- } catch (error) {
150
- console.log("Error processing referral", { error });
151
-
152
- // Track the error event
153
- trackEvent(client, "user_referred_error", {
154
- properties: {
155
- referrer: frakContext?.r,
156
- error:
157
- error instanceof FrakRpcError
158
- ? `[${error.code}] ${error.name} - ${error.message}`
159
- : error instanceof Error
160
- ? error.message
161
- : "undefined",
162
- },
79
+ sendInteraction(client, {
80
+ type: "arrival",
81
+ referrerWallet: frakContext.r,
82
+ landingUrl,
163
83
  });
164
-
165
- // Update the current url with the right data
166
- FrakContextManager.replaceUrl({
167
- url: window.location?.href,
168
- context: options?.alwaysAppendUrl
169
- ? { r: walletStatus?.wallet }
170
- : null,
171
- });
172
-
173
- // And map the error a state
174
- return mapErrorToState(error);
84
+ return true;
175
85
  }
86
+
87
+ return false;
176
88
  }
177
89
 
178
90
  /**
179
- * Automatically submit a referral interaction when detected
180
- * -> And automatically set the referral context in the url
181
- * @param walletStatus
182
- * @param frakContext
91
+ * Build a V2 context representing the current user for URL replacement.
92
+ * @returns A V2 context, or null if clientId or merchantId is unavailable
183
93
  */
184
- async function processReferralLogic({
185
- initialWalletStatus,
186
- getFreshWalletStatus,
187
- pushReferralInteraction,
188
- frakContext,
189
- }: {
190
- initialWalletStatus?: WalletStatusReturnType;
191
- getFreshWalletStatus: () => Promise<Address | undefined>;
192
- pushReferralInteraction: (referrer: Address) => Promise<void>;
193
- frakContext: Pick<FrakContext, "r">;
194
- }) {
195
- // Get the current wallet, without auto displaying the modal
196
- let currentWallet = initialWalletStatus?.wallet;
94
+ function buildCurrentUserContext(merchantId: string): FrakContextV2 | null {
95
+ const clientId = getClientId();
96
+ if (!clientId) return null;
97
+ return {
98
+ v: 2,
99
+ c: clientId,
100
+ m: merchantId,
101
+ t: Math.floor(Date.now() / 1000),
102
+ };
103
+ }
197
104
 
198
- // If we don't have a current wallet, display the modal
199
- if (!currentWallet) {
200
- // Track the event
201
- currentWallet = await getFreshWalletStatus();
105
+ /**
106
+ * Client-side self-referral preflight check.
107
+ * Prevents unnecessary backend round-trips for obvious self-referrals.
108
+ */
109
+ function isSelfReferral(
110
+ frakContext: FrakContext,
111
+ walletStatus?: WalletStatusReturnType
112
+ ): boolean {
113
+ if (isV2Context(frakContext)) {
114
+ return getClientId() === frakContext.c;
202
115
  }
203
-
204
- if (currentWallet && isAddressEqual(frakContext.r, currentWallet)) {
205
- return { status: "self-referral", currentWallet } as const;
116
+ if (isV1Context(frakContext) && walletStatus?.wallet) {
117
+ return isAddressEqual(frakContext.r, walletStatus.wallet);
206
118
  }
207
-
208
- // If the current wallet doesn't have an interaction session, display the modal
209
- if (!initialWalletStatus?.interactionSession) {
210
- currentWallet = await getFreshWalletStatus();
211
- }
212
-
213
- // Push the referred interaction
214
- await pushReferralInteraction(frakContext.r);
215
- return { status: "success", currentWallet } as const;
119
+ return false;
216
120
  }
217
121
 
218
122
  /**
219
- * Helper to ensure a wallet is connected, and display a modal if we got everything needed
123
+ * Handle the full referral interaction flow:
124
+ *
125
+ * 1. Check if the user has been referred (if not, early exit)
126
+ * 2. Preflight self-referral check (if yes, early exit)
127
+ * 3. Track the arrival event
128
+ * 4. Replace the current URL with the user's own referral context
129
+ * 5. Return the resulting referral state
130
+ *
131
+ * @param client - The current Frak Client
132
+ * @param args
133
+ * @param args.walletStatus - The current user wallet status
134
+ * @param args.frakContext - The referral context parsed from the URL
135
+ * @param args.options - Options for URL replacement and merchant context
136
+ * @returns The referral state
137
+ *
138
+ * @see {@link @frak-labs/core-sdk!ModalStepTypes} for modal step types
220
139
  */
221
- async function ensureWalletConnected(
140
+ export function processReferral(
222
141
  client: FrakClient,
223
142
  {
224
- modalConfig,
225
143
  walletStatus,
144
+ frakContext,
145
+ options,
226
146
  }: {
227
- modalConfig?: DisplayEmbeddedWalletParamsType;
228
147
  walletStatus?: WalletStatusReturnType;
148
+ frakContext?: FrakContext | null;
149
+ options?: ProcessReferralOptions;
229
150
  }
230
- ) {
231
- // If wallet not connected, or no interaction session
232
- if (!walletStatus?.interactionSession) {
233
- const result = await displayEmbeddedWallet(client, modalConfig ?? {});
234
- return result?.wallet ?? undefined;
151
+ ): ReferralState {
152
+ if (!frakContext) {
153
+ return "no-referrer";
235
154
  }
236
155
 
237
- return walletStatus.wallet ?? undefined;
238
- }
156
+ if (isSelfReferral(frakContext, walletStatus)) {
157
+ return "self-referral";
158
+ }
239
159
 
240
- /**
241
- * Helper to map an error to a state
242
- * @param error
243
- */
244
- function mapErrorToState(error: unknown): ReferralState {
245
- if (error instanceof FrakRpcError) {
246
- switch (error.code) {
247
- case RpcErrorCodes.walletNotConnected:
248
- return "no-wallet";
249
- case RpcErrorCodes.serverErrorForInteractionDelegation:
250
- return "no-session";
251
- default:
252
- return "error";
253
- }
160
+ if (!trackArrivalIfValid(client, frakContext, walletStatus)) {
161
+ return "no-referrer";
254
162
  }
255
- return "error";
163
+
164
+ // V2 context embeds merchantId; V1 falls back to options
165
+ const contextMerchantId = isV2Context(frakContext)
166
+ ? frakContext.m
167
+ : options?.merchantId;
168
+
169
+ const replaceContext =
170
+ options?.alwaysAppendUrl && contextMerchantId
171
+ ? buildCurrentUserContext(contextMerchantId)
172
+ : null;
173
+
174
+ FrakContextManager.replaceUrl({
175
+ url: window.location?.href,
176
+ context: replaceContext,
177
+ });
178
+
179
+ trackEvent(client, "user_referred_completed", {
180
+ properties: {
181
+ status: "success",
182
+ },
183
+ });
184
+
185
+ return "success";
256
186
  }
@@ -21,9 +21,6 @@ describe("referralInteraction", () => {
21
21
  request: vi.fn(),
22
22
  } as any;
23
23
 
24
- const mockProductId =
25
- "0x0000000000000000000000000000000000000000000000000000000000000002" as Hex;
26
-
27
24
  beforeEach(() => {
28
25
  vi.clearAllMocks();
29
26
  Object.defineProperty(global, "window", {
@@ -55,8 +52,8 @@ describe("referralInteraction", () => {
55
52
 
56
53
  vi.mocked(FrakContextManager.parse).mockReturnValue({} as any);
57
54
  vi.mocked(watchWalletStatus).mockResolvedValue({
55
+ key: "connected",
58
56
  wallet: "0x123" as Hex,
59
- interactionSession: true,
60
57
  } as any);
61
58
  vi.mocked(processReferral).mockResolvedValue("success");
62
59
 
@@ -72,7 +69,6 @@ describe("referralInteraction", () => {
72
69
 
73
70
  const mockContext = { r: "0xreferrer" as Hex };
74
71
  const mockWalletStatus = { wallet: "0x123" as Hex };
75
- const mockModalConfig = { type: "login" };
76
72
  const mockOptions = { alwaysAppendUrl: true };
77
73
 
78
74
  vi.mocked(FrakContextManager.parse).mockReturnValue(mockContext as any);
@@ -80,16 +76,12 @@ describe("referralInteraction", () => {
80
76
  vi.mocked(processReferral).mockResolvedValue("success");
81
77
 
82
78
  await referralInteraction(mockClient, {
83
- productId: mockProductId,
84
- modalConfig: mockModalConfig as any,
85
79
  options: mockOptions,
86
80
  });
87
81
 
88
82
  expect(processReferral).toHaveBeenCalledWith(mockClient, {
89
83
  walletStatus: mockWalletStatus,
90
84
  frakContext: mockContext,
91
- modalConfig: mockModalConfig,
92
- productId: mockProductId,
93
85
  options: mockOptions,
94
86
  });
95
87
  });
@@ -115,7 +107,9 @@ describe("referralInteraction", () => {
115
107
 
116
108
  vi.mocked(FrakContextManager.parse).mockReturnValue({} as any);
117
109
  vi.mocked(watchWalletStatus).mockResolvedValue(null as any);
118
- vi.mocked(processReferral).mockRejectedValue(new Error("Test error"));
110
+ vi.mocked(processReferral).mockImplementation(() => {
111
+ throw new Error("Test error");
112
+ });
119
113
 
120
114
  const consoleSpy = vi
121
115
  .spyOn(console, "warn")
@@ -144,8 +138,6 @@ describe("referralInteraction", () => {
144
138
  expect(processReferral).toHaveBeenCalledWith(
145
139
  mockClient,
146
140
  expect.objectContaining({
147
- modalConfig: undefined,
148
- productId: undefined,
149
141
  options: undefined,
150
142
  })
151
143
  );
@@ -1,5 +1,4 @@
1
- import type { Hex } from "viem";
2
- import type { DisplayEmbeddedWalletParamsType, FrakClient } from "../../types";
1
+ import type { FrakClient } from "../../types";
3
2
  import { FrakContextManager } from "../../utils";
4
3
  import { watchWalletStatus } from "../index";
5
4
  import {
@@ -8,11 +7,9 @@ import {
8
7
  } from "./processReferral";
9
8
 
10
9
  /**
11
- * Function used to display a modal
10
+ * Function used to handle referral interactions
12
11
  * @param client - The current Frak Client
13
12
  * @param args
14
- * @param args.productId - The product id to interact with (if not specified will be recomputed from the current domain)
15
- * @param args.modalConfig - The modal configuration to display if the user is not logged in
16
13
  * @param args.options - Some options for the referral interaction
17
14
  *
18
15
  * @returns A promise with the resulting referral state, or undefined in case of an error
@@ -20,17 +17,12 @@ import {
20
17
  * @description This function will automatically handle the referral interaction process
21
18
  *
22
19
  * @see {@link processReferral} for more details on the automatic referral handling process
23
- * @see {@link @frak-labs/core-sdk!ModalStepTypes} for more details on each modal steps types
24
20
  */
25
21
  export async function referralInteraction(
26
22
  client: FrakClient,
27
23
  {
28
- productId,
29
- modalConfig,
30
24
  options,
31
25
  }: {
32
- productId?: Hex;
33
- modalConfig?: DisplayEmbeddedWalletParamsType;
34
26
  options?: ProcessReferralOptions;
35
27
  } = {}
36
28
  ) {
@@ -43,11 +35,9 @@ export async function referralInteraction(
43
35
  const currentWalletStatus = await watchWalletStatus(client);
44
36
 
45
37
  try {
46
- return await processReferral(client, {
38
+ return processReferral(client, {
47
39
  walletStatus: currentWalletStatus,
48
40
  frakContext,
49
- modalConfig,
50
- productId,
51
41
  options,
52
42
  });
53
43
  } catch (error) {
@@ -1,32 +1,56 @@
1
- import type {
2
- FrakClient,
3
- SendInteractionParamsType,
4
- SendInteractionReturnType,
5
- } from "../types";
6
- import { computeProductId } from "../utils/computeProductId";
1
+ import type { FrakClient } from "../types";
2
+ import type { SendInteractionParamsType } from "../types/rpc/interaction";
3
+ import { getClientId } from "../utils/clientId";
7
4
 
8
5
  /**
9
- * Function used to send an interaction
10
- * @param client - The current Frak Client
11
- * @param args
6
+ * Send an interaction to the backend via the listener RPC.
7
+ * Fire-and-forget: errors are caught and logged, not thrown.
8
+ *
9
+ * @param client - The Frak client instance
10
+ * @param params - The interaction parameters
11
+ *
12
+ * @description Sends a user interaction event through the wallet iframe RPC. Supports three interaction types: arrival tracking, sharing events, and custom interactions.
12
13
  *
13
14
  * @example
14
- * const interaction = PressInteractionEncoder.openArticle({
15
- * articleId: keccak256(toHex("article-slug")),
15
+ * Track a user arrival with referral attribution:
16
+ * ```ts
17
+ * await sendInteraction(client, {
18
+ * type: "arrival",
19
+ * referrerWallet: "0x1234...abcd",
20
+ * landingUrl: window.location.href,
21
+ * utmSource: "twitter",
22
+ * utmMedium: "social",
23
+ * utmCampaign: "launch-2026",
16
24
  * });
17
- * const { delegationId } = await sendInteraction(frakConfig, {
18
- * interaction,
25
+ * ```
26
+ *
27
+ * @example
28
+ * Track a sharing event:
29
+ * ```ts
30
+ * await sendInteraction(client, { type: "sharing" });
31
+ * ```
32
+ *
33
+ * @example
34
+ * Send a custom interaction:
35
+ * ```ts
36
+ * await sendInteraction(client, {
37
+ * type: "custom",
38
+ * customType: "newsletter_signup",
39
+ * data: { email: "user@example.com" },
19
40
  * });
20
- * console.log("Delegated interaction id", delegationId);
41
+ * ```
21
42
  */
22
43
  export async function sendInteraction(
23
44
  client: FrakClient,
24
- { productId, interaction, validation }: SendInteractionParamsType
25
- ): Promise<SendInteractionReturnType> {
26
- const pId = productId ?? computeProductId(client.config);
27
-
28
- return await client.request({
29
- method: "frak_sendInteraction",
30
- params: [pId, interaction, validation],
31
- });
45
+ params: SendInteractionParamsType
46
+ ): Promise<void> {
47
+ try {
48
+ await client.request({
49
+ method: "frak_sendInteraction",
50
+ params: [params, { clientId: getClientId() }],
51
+ });
52
+ } catch {
53
+ // Silent failure - fire-and-forget
54
+ console.warn("[Frak SDK] Failed to send interaction:", params.type);
55
+ }
32
56
  }