@frak-labs/core-sdk 0.0.19 → 0.1.0-beta.278e9605

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 (116) hide show
  1. package/package.json +26 -17
  2. package/src/actions/displayEmbeddedWallet.test.ts +194 -0
  3. package/src/actions/displayEmbeddedWallet.ts +20 -0
  4. package/src/actions/displayModal.test.ts +387 -0
  5. package/src/actions/displayModal.ts +131 -0
  6. package/src/actions/getProductInformation.test.ts +133 -0
  7. package/src/actions/getProductInformation.ts +14 -0
  8. package/src/actions/index.ts +29 -0
  9. package/src/actions/openSso.test.ts +407 -0
  10. package/src/actions/openSso.ts +116 -0
  11. package/src/actions/prepareSso.test.ts +223 -0
  12. package/src/actions/prepareSso.ts +48 -0
  13. package/src/actions/referral/processReferral.ts +230 -0
  14. package/src/actions/referral/referralInteraction.ts +57 -0
  15. package/src/actions/sendInteraction.test.ts +219 -0
  16. package/src/actions/sendInteraction.ts +32 -0
  17. package/src/actions/trackPurchaseStatus.test.ts +287 -0
  18. package/src/actions/trackPurchaseStatus.ts +53 -0
  19. package/src/actions/watchWalletStatus.test.ts +372 -0
  20. package/src/actions/watchWalletStatus.ts +94 -0
  21. package/src/actions/wrapper/modalBuilder.ts +212 -0
  22. package/src/actions/wrapper/sendTransaction.ts +62 -0
  23. package/src/actions/wrapper/siweAuthenticate.ts +94 -0
  24. package/src/bundle.ts +3 -0
  25. package/src/clients/DebugInfo.ts +182 -0
  26. package/src/clients/createIFrameFrakClient.ts +287 -0
  27. package/src/clients/index.ts +3 -0
  28. package/src/clients/setupClient.ts +73 -0
  29. package/src/clients/transports/iframeLifecycleManager.ts +90 -0
  30. package/src/constants/interactionTypes.ts +44 -0
  31. package/src/constants/locales.ts +14 -0
  32. package/src/constants/productTypes.ts +33 -0
  33. package/src/index.ts +101 -0
  34. package/src/interactions/index.ts +5 -0
  35. package/src/interactions/pressEncoder.test.ts +215 -0
  36. package/src/interactions/pressEncoder.ts +53 -0
  37. package/src/interactions/purchaseEncoder.test.ts +291 -0
  38. package/src/interactions/purchaseEncoder.ts +99 -0
  39. package/src/interactions/referralEncoder.test.ts +170 -0
  40. package/src/interactions/referralEncoder.ts +47 -0
  41. package/src/interactions/retailEncoder.test.ts +107 -0
  42. package/src/interactions/retailEncoder.ts +37 -0
  43. package/src/interactions/webshopEncoder.test.ts +56 -0
  44. package/src/interactions/webshopEncoder.ts +30 -0
  45. package/src/types/client.ts +14 -0
  46. package/src/types/compression.ts +22 -0
  47. package/src/types/config.ts +111 -0
  48. package/src/types/context.ts +13 -0
  49. package/src/types/index.ts +71 -0
  50. package/src/types/lifecycle/client.ts +46 -0
  51. package/src/types/lifecycle/iframe.ts +35 -0
  52. package/src/types/lifecycle/index.ts +2 -0
  53. package/src/types/rpc/displayModal.ts +84 -0
  54. package/src/types/rpc/embedded/index.ts +68 -0
  55. package/src/types/rpc/embedded/loggedIn.ts +55 -0
  56. package/src/types/rpc/embedded/loggedOut.ts +28 -0
  57. package/src/types/rpc/interaction.ts +43 -0
  58. package/src/types/rpc/modal/final.ts +46 -0
  59. package/src/types/rpc/modal/generic.ts +46 -0
  60. package/src/types/rpc/modal/index.ts +20 -0
  61. package/src/types/rpc/modal/login.ts +32 -0
  62. package/src/types/rpc/modal/openSession.ts +25 -0
  63. package/src/types/rpc/modal/siweAuthenticate.ts +37 -0
  64. package/src/types/rpc/modal/transaction.ts +33 -0
  65. package/src/types/rpc/productInformation.ts +59 -0
  66. package/src/types/rpc/sso.ts +80 -0
  67. package/src/types/rpc/walletStatus.ts +35 -0
  68. package/src/types/rpc.ts +158 -0
  69. package/src/types/transport.ts +34 -0
  70. package/src/utils/FrakContext.test.ts +338 -0
  71. package/src/utils/FrakContext.ts +158 -0
  72. package/src/utils/compression/b64.test.ts +181 -0
  73. package/src/utils/compression/b64.ts +29 -0
  74. package/src/utils/compression/compress.test.ts +123 -0
  75. package/src/utils/compression/compress.ts +11 -0
  76. package/src/utils/compression/decompress.test.ts +145 -0
  77. package/src/utils/compression/decompress.ts +11 -0
  78. package/src/utils/compression/index.ts +3 -0
  79. package/src/utils/computeProductId.test.ts +80 -0
  80. package/src/utils/computeProductId.ts +11 -0
  81. package/src/utils/constants.test.ts +23 -0
  82. package/src/utils/constants.ts +4 -0
  83. package/src/utils/formatAmount.test.ts +113 -0
  84. package/src/utils/formatAmount.ts +18 -0
  85. package/src/utils/getCurrencyAmountKey.test.ts +44 -0
  86. package/src/utils/getCurrencyAmountKey.ts +15 -0
  87. package/src/utils/getSupportedCurrency.test.ts +51 -0
  88. package/src/utils/getSupportedCurrency.ts +14 -0
  89. package/src/utils/getSupportedLocale.test.ts +64 -0
  90. package/src/utils/getSupportedLocale.ts +16 -0
  91. package/src/utils/iframeHelper.test.ts +450 -0
  92. package/src/utils/iframeHelper.ts +143 -0
  93. package/src/utils/index.ts +21 -0
  94. package/src/utils/sso.test.ts +361 -0
  95. package/src/utils/sso.ts +119 -0
  96. package/src/utils/ssoUrlListener.ts +60 -0
  97. package/src/utils/trackEvent.test.ts +162 -0
  98. package/src/utils/trackEvent.ts +26 -0
  99. package/cdn/bundle.js +0 -19
  100. package/cdn/bundle.js.LICENSE.txt +0 -10
  101. package/dist/actions.cjs +0 -1
  102. package/dist/actions.d.cts +0 -1400
  103. package/dist/actions.d.ts +0 -1400
  104. package/dist/actions.js +0 -1
  105. package/dist/bundle.cjs +0 -13
  106. package/dist/bundle.d.cts +0 -2022
  107. package/dist/bundle.d.ts +0 -2022
  108. package/dist/bundle.js +0 -13
  109. package/dist/index.cjs +0 -13
  110. package/dist/index.d.cts +0 -1373
  111. package/dist/index.d.ts +0 -1373
  112. package/dist/index.js +0 -13
  113. package/dist/interactions.cjs +0 -1
  114. package/dist/interactions.d.cts +0 -182
  115. package/dist/interactions.d.ts +0 -182
  116. package/dist/interactions.js +0 -1
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "url": "https://twitter.com/QNivelais"
12
12
  }
13
13
  ],
14
- "version": "0.0.19",
14
+ "version": "0.1.0-beta.278e9605",
15
15
  "description": "Core SDK of the Frak wallet, low level library to interact directly with the frak ecosystem.",
16
16
  "repository": {
17
17
  "url": "https://github.com/frak-id/wallet",
@@ -34,11 +34,13 @@
34
34
  "type": "module",
35
35
  "files": [
36
36
  "/dist",
37
- "/cdn"
37
+ "/cdn",
38
+ "/src"
38
39
  ],
39
40
  "browser": "./cdn/bundle.js",
40
41
  "exports": {
41
42
  ".": {
43
+ "development": "./src/index.ts",
42
44
  "import": {
43
45
  "types": "./dist/index.d.ts",
44
46
  "default": "./dist/index.js"
@@ -49,6 +51,7 @@
49
51
  }
50
52
  },
51
53
  "./actions": {
54
+ "development": "./src/actions/index.ts",
52
55
  "import": {
53
56
  "types": "./dist/actions.d.ts",
54
57
  "default": "./dist/actions.js"
@@ -59,6 +62,7 @@
59
62
  }
60
63
  },
61
64
  "./interactions": {
65
+ "development": "./src/interactions/index.ts",
62
66
  "import": {
63
67
  "types": "./dist/interactions.d.ts",
64
68
  "default": "./dist/interactions.js"
@@ -83,32 +87,37 @@
83
87
  "lint": "biome lint .",
84
88
  "format:check": "biome check .",
85
89
  "format": "biome check --write .",
86
- "clean": "rimraf dist",
87
- "build": "rslib build",
88
- "build:watch": "rslib build --watch",
90
+ "clean": "rimraf dist cdn",
91
+ "build": "tsdown",
92
+ "build:watch": "tsdown --watch",
89
93
  "check-exports": "attw --pack --profile node16 .",
90
94
  "typecheck": "tsc --noEmit",
95
+ "test": "vitest",
96
+ "test:ui": "vitest --ui",
97
+ "test:coverage": "vitest --coverage",
91
98
  "prepublish": "bun run lint && bun run build",
92
99
  "publish": "echo 'Publishing core...'"
93
100
  },
94
101
  "peerDependencies": {
95
- "viem": "^2.23.14"
102
+ "viem": "^2.x"
96
103
  },
97
104
  "dependencies": {
98
- "@jsonjoy.com/json-pack": "^1.2.0",
105
+ "@frak-labs/frame-connector": "0.1.0",
106
+ "@jsonjoy.com/json-pack": "^1.21.0",
99
107
  "@openpanel/web": "^1.0.1"
100
108
  },
101
109
  "devDependencies": {
102
110
  "@arethetypeswrong/cli": "^0.18.2",
103
111
  "@frak-labs/dev-tooling": "0.0.0",
104
- "@microsoft/api-extractor": "^7.52.8",
105
- "@rsbuild/plugin-node-polyfill": "^1.3.0",
106
- "@rslib/core": "^0.9.2",
107
- "@types/node": "^24.0.1",
108
- "dotenv": "^17.0.0",
109
- "typescript": "^5.8.3"
110
- },
111
- "browserslist": [
112
- "extends @frak-labs/browserslist-config"
113
- ]
112
+ "@rolldown/plugin-node-polyfills": "^1.0.0",
113
+ "@types/jsdom": "^27.0.0",
114
+ "@types/node": "^24.10.0",
115
+ "@vitest/coverage-v8": "^4.0.8",
116
+ "@vitest/ui": "^4.0.8",
117
+ "jsdom": "^27.1.0",
118
+ "tsdown": "^0.16.1",
119
+ "typescript": "^5.9.3",
120
+ "viem": "^2.38.6",
121
+ "vitest": "^4.0.8"
122
+ }
114
123
  }
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Tests for displayEmbeddedWallet action
3
+ * Tests displaying embedded wallet via RPC
4
+ */
5
+
6
+ import type { Address } from "viem";
7
+ import { describe, expect, it, vi } from "../../tests/vitest-fixtures";
8
+ import type {
9
+ DisplayEmbeddedWalletParamsType,
10
+ DisplayEmbeddedWalletResultType,
11
+ FrakClient,
12
+ } from "../types";
13
+ import { displayEmbeddedWallet } from "./displayEmbeddedWallet";
14
+
15
+ describe("displayEmbeddedWallet", () => {
16
+ describe("success cases", () => {
17
+ it("should call client.request with correct method and params", async () => {
18
+ const mockParams: DisplayEmbeddedWalletParamsType = {
19
+ loggedIn: {
20
+ action: {
21
+ key: "sharing",
22
+ },
23
+ },
24
+ };
25
+
26
+ const mockResponse: DisplayEmbeddedWalletResultType = {
27
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
28
+ };
29
+
30
+ const mockClient = {
31
+ config: {
32
+ metadata: {
33
+ name: "Test App",
34
+ },
35
+ },
36
+ request: vi.fn().mockResolvedValue(mockResponse),
37
+ } as unknown as FrakClient;
38
+
39
+ await displayEmbeddedWallet(mockClient, mockParams);
40
+
41
+ expect(mockClient.request).toHaveBeenCalledWith({
42
+ method: "frak_displayEmbeddedWallet",
43
+ params: [mockParams, { name: "Test App" }],
44
+ });
45
+ });
46
+
47
+ it("should return wallet address", async () => {
48
+ const mockParams: DisplayEmbeddedWalletParamsType = {
49
+ loggedIn: {
50
+ action: {
51
+ key: "sharing",
52
+ },
53
+ },
54
+ };
55
+
56
+ const mockResponse: DisplayEmbeddedWalletResultType = {
57
+ wallet: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as Address,
58
+ };
59
+
60
+ const mockClient = {
61
+ config: {
62
+ metadata: {
63
+ name: "Test App",
64
+ },
65
+ },
66
+ request: vi.fn().mockResolvedValue(mockResponse),
67
+ } as unknown as FrakClient;
68
+
69
+ const result = await displayEmbeddedWallet(mockClient, mockParams);
70
+
71
+ expect(result).toEqual(mockResponse);
72
+ expect(result.wallet).toBe(
73
+ "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
74
+ );
75
+ });
76
+
77
+ it("should handle loggedOut view params", async () => {
78
+ const mockParams: DisplayEmbeddedWalletParamsType = {
79
+ loggedOut: {
80
+ metadata: {
81
+ text: "Login to get rewards",
82
+ },
83
+ },
84
+ };
85
+
86
+ const mockResponse: DisplayEmbeddedWalletResultType = {
87
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
88
+ };
89
+
90
+ const mockClient = {
91
+ config: {
92
+ metadata: {
93
+ name: "Test App",
94
+ logoUrl: "https://example.com/logo.png",
95
+ },
96
+ },
97
+ request: vi.fn().mockResolvedValue(mockResponse),
98
+ } as unknown as FrakClient;
99
+
100
+ await displayEmbeddedWallet(mockClient, mockParams);
101
+
102
+ expect(mockClient.request).toHaveBeenCalledWith({
103
+ method: "frak_displayEmbeddedWallet",
104
+ params: [
105
+ mockParams,
106
+ {
107
+ name: "Test App",
108
+ logoUrl: "https://example.com/logo.png",
109
+ },
110
+ ],
111
+ });
112
+ });
113
+
114
+ it("should handle referred action", async () => {
115
+ const mockParams: DisplayEmbeddedWalletParamsType = {
116
+ loggedIn: {
117
+ action: {
118
+ key: "referred",
119
+ },
120
+ },
121
+ };
122
+
123
+ const mockResponse: DisplayEmbeddedWalletResultType = {
124
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
125
+ };
126
+
127
+ const mockClient = {
128
+ config: {
129
+ metadata: {
130
+ name: "Test App",
131
+ },
132
+ },
133
+ request: vi.fn().mockResolvedValue(mockResponse),
134
+ } as unknown as FrakClient;
135
+
136
+ const result = await displayEmbeddedWallet(mockClient, mockParams);
137
+
138
+ expect(result).toEqual(mockResponse);
139
+ });
140
+
141
+ it("should pass client metadata to request", async () => {
142
+ const mockParams: DisplayEmbeddedWalletParamsType = {};
143
+
144
+ const mockMetadata = {
145
+ name: "My App",
146
+ logoUrl: "https://example.com/logo.png",
147
+ css: "body { color: red; }",
148
+ lang: "fr" as const,
149
+ };
150
+
151
+ const mockClient = {
152
+ config: {
153
+ metadata: mockMetadata,
154
+ },
155
+ request: vi.fn().mockResolvedValue({
156
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
157
+ }),
158
+ } as unknown as FrakClient;
159
+
160
+ await displayEmbeddedWallet(mockClient, mockParams);
161
+
162
+ expect(mockClient.request).toHaveBeenCalledWith({
163
+ method: "frak_displayEmbeddedWallet",
164
+ params: [mockParams, mockMetadata],
165
+ });
166
+ });
167
+ });
168
+
169
+ describe("error handling", () => {
170
+ it("should propagate errors from client.request", async () => {
171
+ const mockParams: DisplayEmbeddedWalletParamsType = {
172
+ loggedIn: {
173
+ action: {
174
+ key: "sharing",
175
+ },
176
+ },
177
+ };
178
+
179
+ const error = new Error("Display failed");
180
+ const mockClient = {
181
+ config: {
182
+ metadata: {
183
+ name: "Test App",
184
+ },
185
+ },
186
+ request: vi.fn().mockRejectedValue(error),
187
+ } as unknown as FrakClient;
188
+
189
+ await expect(
190
+ displayEmbeddedWallet(mockClient, mockParams)
191
+ ).rejects.toThrow("Display failed");
192
+ });
193
+ });
194
+ });
@@ -0,0 +1,20 @@
1
+ import type {
2
+ DisplayEmbeddedWalletParamsType,
3
+ DisplayEmbeddedWalletResultType,
4
+ FrakClient,
5
+ } from "../types";
6
+
7
+ /**
8
+ * Function used to display the Frak embedded wallet popup
9
+ * @param client - The current Frak Client
10
+ * @param params - The parameter used to customise the embedded wallet
11
+ */
12
+ export async function displayEmbeddedWallet(
13
+ client: FrakClient,
14
+ params: DisplayEmbeddedWalletParamsType
15
+ ): Promise<DisplayEmbeddedWalletResultType> {
16
+ return await client.request({
17
+ method: "frak_displayEmbeddedWallet",
18
+ params: [params, client.config.metadata],
19
+ });
20
+ }
@@ -0,0 +1,387 @@
1
+ /**
2
+ * Tests for displayModal action
3
+ * Tests modal display via RPC with various step configurations
4
+ */
5
+
6
+ import type { Address } from "viem";
7
+ import { describe, expect, it, vi } from "../../tests/vitest-fixtures";
8
+ import type {
9
+ DisplayModalParamsType,
10
+ FrakClient,
11
+ ModalStepTypes,
12
+ } from "../types";
13
+ import { displayModal } from "./displayModal";
14
+
15
+ describe("displayModal", () => {
16
+ describe("basic modal display", () => {
17
+ it("should call client.request with correct method and params", async () => {
18
+ const mockResponse = {
19
+ login: {
20
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
21
+ },
22
+ };
23
+
24
+ const mockClient = {
25
+ config: {
26
+ metadata: {
27
+ name: "Test App",
28
+ },
29
+ },
30
+ request: vi.fn().mockResolvedValue(mockResponse),
31
+ } as unknown as FrakClient;
32
+
33
+ const params: DisplayModalParamsType<ModalStepTypes[]> = {
34
+ steps: {
35
+ login: { allowSso: true },
36
+ },
37
+ };
38
+
39
+ await displayModal(mockClient, params);
40
+
41
+ expect(mockClient.request).toHaveBeenCalledWith({
42
+ method: "frak_displayModal",
43
+ params: [params.steps, params.metadata, { name: "Test App" }],
44
+ });
45
+ });
46
+
47
+ it("should return modal results", async () => {
48
+ const mockResponse = {
49
+ login: {
50
+ wallet: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as Address,
51
+ },
52
+ };
53
+
54
+ const mockClient = {
55
+ config: {
56
+ metadata: {
57
+ name: "Test App",
58
+ },
59
+ },
60
+ request: vi.fn().mockResolvedValue(mockResponse),
61
+ } as unknown as FrakClient;
62
+
63
+ const params: DisplayModalParamsType<ModalStepTypes[]> = {
64
+ steps: {
65
+ login: { allowSso: false },
66
+ },
67
+ };
68
+
69
+ const result = await displayModal(mockClient, params);
70
+
71
+ expect(result).toEqual(mockResponse);
72
+ });
73
+ });
74
+
75
+ describe("modal with multiple steps", () => {
76
+ it("should handle login + openSession steps", async () => {
77
+ const mockResponse = {
78
+ login: {
79
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
80
+ },
81
+ openSession: {
82
+ startTimestamp: 1234567890,
83
+ endTimestamp: 1234567900,
84
+ },
85
+ };
86
+
87
+ const mockClient = {
88
+ config: {
89
+ metadata: {
90
+ name: "Test App",
91
+ },
92
+ },
93
+ request: vi.fn().mockResolvedValue(mockResponse),
94
+ } as unknown as FrakClient;
95
+
96
+ const params: DisplayModalParamsType<ModalStepTypes[]> = {
97
+ steps: {
98
+ login: { allowSso: true },
99
+ openSession: {},
100
+ },
101
+ };
102
+
103
+ const result = await displayModal(mockClient, params);
104
+
105
+ expect(result).toEqual(mockResponse);
106
+ });
107
+
108
+ it("should handle full modal flow", async () => {
109
+ const mockResponse = {
110
+ login: {
111
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
112
+ },
113
+ openSession: {
114
+ startTimestamp: 1234567890,
115
+ endTimestamp: 1234567900,
116
+ },
117
+ sendTransaction: {
118
+ hash: "0xtxhash" as `0x${string}`,
119
+ },
120
+ final: {},
121
+ };
122
+
123
+ const mockClient = {
124
+ config: {
125
+ metadata: {
126
+ name: "Test App",
127
+ logoUrl: "https://example.com/logo.png",
128
+ },
129
+ },
130
+ request: vi.fn().mockResolvedValue(mockResponse),
131
+ } as unknown as FrakClient;
132
+
133
+ const params: DisplayModalParamsType<ModalStepTypes[]> = {
134
+ steps: {
135
+ login: { allowSso: true },
136
+ openSession: {},
137
+ sendTransaction: {
138
+ tx: [
139
+ {
140
+ to: "0xdeadbeef" as Address,
141
+ data: "0xdata" as `0x${string}`,
142
+ },
143
+ ],
144
+ },
145
+ final: {
146
+ action: { key: "reward" },
147
+ },
148
+ },
149
+ };
150
+
151
+ const result = await displayModal(mockClient, params);
152
+
153
+ expect(result).toEqual(mockResponse);
154
+ });
155
+ });
156
+
157
+ describe("modal with metadata", () => {
158
+ it("should pass metadata to request", async () => {
159
+ const mockResponse = {
160
+ login: {
161
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
162
+ },
163
+ };
164
+
165
+ const mockClient = {
166
+ config: {
167
+ metadata: {
168
+ name: "Test App",
169
+ },
170
+ },
171
+ request: vi.fn().mockResolvedValue(mockResponse),
172
+ } as unknown as FrakClient;
173
+
174
+ const params: DisplayModalParamsType<ModalStepTypes[]> = {
175
+ steps: {
176
+ login: { allowSso: true },
177
+ },
178
+ metadata: {
179
+ header: {
180
+ title: "My Custom Title",
181
+ icon: "https://example.com/icon.png",
182
+ },
183
+ },
184
+ };
185
+
186
+ await displayModal(mockClient, params);
187
+
188
+ expect(mockClient.request).toHaveBeenCalledWith({
189
+ method: "frak_displayModal",
190
+ params: [
191
+ params.steps,
192
+ {
193
+ header: {
194
+ title: "My Custom Title",
195
+ icon: "https://example.com/icon.png",
196
+ },
197
+ },
198
+ { name: "Test App" },
199
+ ],
200
+ });
201
+ });
202
+
203
+ it("should work without metadata", async () => {
204
+ const mockResponse = {
205
+ login: {
206
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
207
+ },
208
+ };
209
+
210
+ const mockClient = {
211
+ config: {
212
+ metadata: {
213
+ name: "Test App",
214
+ },
215
+ },
216
+ request: vi.fn().mockResolvedValue(mockResponse),
217
+ } as unknown as FrakClient;
218
+
219
+ const params: DisplayModalParamsType<ModalStepTypes[]> = {
220
+ steps: {
221
+ login: { allowSso: true },
222
+ },
223
+ };
224
+
225
+ await displayModal(mockClient, params);
226
+
227
+ expect(mockClient.request).toHaveBeenCalledWith({
228
+ method: "frak_displayModal",
229
+ params: [params.steps, undefined, { name: "Test App" }],
230
+ });
231
+ });
232
+ });
233
+
234
+ describe("modal with SSO metadata", () => {
235
+ it("should handle SSO metadata in login step", async () => {
236
+ const mockResponse = {
237
+ login: {
238
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
239
+ },
240
+ };
241
+
242
+ const mockClient = {
243
+ config: {
244
+ metadata: {
245
+ name: "Test App",
246
+ },
247
+ },
248
+ request: vi.fn().mockResolvedValue(mockResponse),
249
+ } as unknown as FrakClient;
250
+
251
+ const params: DisplayModalParamsType<ModalStepTypes[]> = {
252
+ steps: {
253
+ login: {
254
+ allowSso: true,
255
+ ssoMetadata: {
256
+ logoUrl: "https://example.com/logo.png",
257
+ homepageLink: "https://example.com",
258
+ },
259
+ },
260
+ },
261
+ };
262
+
263
+ await displayModal(mockClient, params);
264
+
265
+ expect(mockClient.request).toHaveBeenCalledWith({
266
+ method: "frak_displayModal",
267
+ params: [params.steps, undefined, { name: "Test App" }],
268
+ });
269
+ });
270
+ });
271
+
272
+ describe("modal with sharing action", () => {
273
+ it("should handle sharing final action", async () => {
274
+ const mockResponse = {
275
+ final: {},
276
+ };
277
+
278
+ const mockClient = {
279
+ config: {
280
+ metadata: {
281
+ name: "Test App",
282
+ },
283
+ },
284
+ request: vi.fn().mockResolvedValue(mockResponse),
285
+ } as unknown as FrakClient;
286
+
287
+ const params: DisplayModalParamsType<ModalStepTypes[]> = {
288
+ steps: {
289
+ final: {
290
+ action: {
291
+ key: "sharing",
292
+ options: {
293
+ popupTitle: "Share the app",
294
+ text: "Discover my app",
295
+ link: "https://example.com",
296
+ },
297
+ },
298
+ },
299
+ },
300
+ };
301
+
302
+ await displayModal(mockClient, params);
303
+
304
+ expect(mockClient.request).toHaveBeenCalledWith({
305
+ method: "frak_displayModal",
306
+ params: [params.steps, undefined, { name: "Test App" }],
307
+ });
308
+ });
309
+
310
+ it("should handle reward final action", async () => {
311
+ const mockResponse = {
312
+ final: {},
313
+ };
314
+
315
+ const mockClient = {
316
+ config: {
317
+ metadata: {
318
+ name: "Test App",
319
+ },
320
+ },
321
+ request: vi.fn().mockResolvedValue(mockResponse),
322
+ } as unknown as FrakClient;
323
+
324
+ const params: DisplayModalParamsType<ModalStepTypes[]> = {
325
+ steps: {
326
+ final: {
327
+ action: { key: "reward" },
328
+ autoSkip: true,
329
+ },
330
+ },
331
+ };
332
+
333
+ await displayModal(mockClient, params);
334
+
335
+ expect(mockClient.request).toHaveBeenCalledWith({
336
+ method: "frak_displayModal",
337
+ params: [params.steps, undefined, { name: "Test App" }],
338
+ });
339
+ });
340
+ });
341
+
342
+ describe("error handling", () => {
343
+ it("should propagate errors from client.request", async () => {
344
+ const error = new Error("Modal display failed");
345
+ const mockClient = {
346
+ config: {
347
+ metadata: {
348
+ name: "Test App",
349
+ },
350
+ },
351
+ request: vi.fn().mockRejectedValue(error),
352
+ } as unknown as FrakClient;
353
+
354
+ const params: DisplayModalParamsType<ModalStepTypes[]> = {
355
+ steps: {
356
+ login: { allowSso: true },
357
+ },
358
+ };
359
+
360
+ await expect(displayModal(mockClient, params)).rejects.toThrow(
361
+ "Modal display failed"
362
+ );
363
+ });
364
+
365
+ it("should handle network errors", async () => {
366
+ const error = new Error("Network timeout");
367
+ const mockClient = {
368
+ config: {
369
+ metadata: {
370
+ name: "Test App",
371
+ },
372
+ },
373
+ request: vi.fn().mockRejectedValue(error),
374
+ } as unknown as FrakClient;
375
+
376
+ const params: DisplayModalParamsType<ModalStepTypes[]> = {
377
+ steps: {
378
+ openSession: {},
379
+ },
380
+ };
381
+
382
+ await expect(displayModal(mockClient, params)).rejects.toThrow(
383
+ "Network timeout"
384
+ );
385
+ });
386
+ });
387
+ });