@frak-labs/react-sdk 0.1.1 → 0.2.0

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 (38) hide show
  1. package/README.md +25 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.d.cts +28 -64
  4. package/dist/index.d.ts +28 -64
  5. package/dist/index.js +1 -1
  6. package/package.json +17 -16
  7. package/src/hook/helper/useReferralInteraction.test.ts +358 -0
  8. package/src/hook/helper/useReferralInteraction.ts +78 -0
  9. package/src/hook/index.ts +10 -0
  10. package/src/hook/useDisplayModal.test.ts +275 -0
  11. package/src/hook/useDisplayModal.ts +68 -0
  12. package/src/hook/useFrakClient.test.ts +119 -0
  13. package/src/hook/useFrakClient.ts +11 -0
  14. package/src/hook/useFrakConfig.test.ts +184 -0
  15. package/src/hook/useFrakConfig.ts +22 -0
  16. package/src/hook/useGetMerchantInformation.ts +56 -0
  17. package/src/hook/useOpenSso.test.ts +202 -0
  18. package/src/hook/useOpenSso.ts +51 -0
  19. package/src/hook/usePrepareSso.test.ts +197 -0
  20. package/src/hook/usePrepareSso.ts +55 -0
  21. package/src/hook/useSendTransaction.test.ts +218 -0
  22. package/src/hook/useSendTransaction.ts +62 -0
  23. package/src/hook/useSiweAuthenticate.test.ts +258 -0
  24. package/src/hook/useSiweAuthenticate.ts +66 -0
  25. package/src/hook/useWalletStatus.test.ts +112 -0
  26. package/src/hook/useWalletStatus.ts +55 -0
  27. package/src/hook/utils/useFrakContext.test.ts +157 -0
  28. package/src/hook/utils/useFrakContext.ts +42 -0
  29. package/src/hook/utils/useMounted.test.ts +70 -0
  30. package/src/hook/utils/useMounted.ts +12 -0
  31. package/src/hook/utils/useWindowLocation.test.ts +54 -0
  32. package/src/hook/utils/useWindowLocation.ts +40 -0
  33. package/src/index.ts +25 -0
  34. package/src/provider/FrakConfigProvider.test.ts +246 -0
  35. package/src/provider/FrakConfigProvider.ts +54 -0
  36. package/src/provider/FrakIFrameClientProvider.test.tsx +209 -0
  37. package/src/provider/FrakIFrameClientProvider.ts +86 -0
  38. package/src/provider/index.ts +7 -0
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Tests for useSendTransactionAction hook
3
+ * Tests TanStack Mutation wrapper for sending transactions
4
+ */
5
+
6
+ import { vi } from "vitest";
7
+
8
+ vi.mock("@frak-labs/core-sdk/actions");
9
+
10
+ import type { SendTransactionReturnType } from "@frak-labs/core-sdk";
11
+ import { sendTransaction } from "@frak-labs/core-sdk/actions";
12
+ import { ClientNotFound } from "@frak-labs/frame-connector";
13
+ import { renderHook, waitFor } from "@testing-library/react";
14
+ import type { Hex } from "viem";
15
+ import { describe, expect, test } from "../../tests/vitest-fixtures";
16
+ import { useSendTransactionAction } from "./useSendTransaction";
17
+
18
+ describe("useSendTransactionAction", () => {
19
+ test("should throw ClientNotFound when client is not available", async ({
20
+ queryWrapper,
21
+ }) => {
22
+ const { result } = renderHook(() => useSendTransactionAction(), {
23
+ wrapper: queryWrapper.wrapper,
24
+ });
25
+
26
+ await waitFor(() => {
27
+ expect(result.current.mutate).toBeDefined();
28
+ });
29
+
30
+ result.current.mutate({
31
+ tx: {
32
+ to: "0x1234567890123456789012345678901234567890",
33
+ data: "0x",
34
+ },
35
+ });
36
+
37
+ await waitFor(() => {
38
+ expect(result.current.isError).toBe(true);
39
+ });
40
+
41
+ expect(result.current.error).toBeInstanceOf(ClientNotFound);
42
+ });
43
+
44
+ test("should send transaction successfully", async ({
45
+ mockFrakProviders,
46
+ }) => {
47
+ const mockResult: SendTransactionReturnType = {
48
+ hash: "0xabcdef1234567890" as Hex,
49
+ };
50
+
51
+ vi.mocked(sendTransaction).mockResolvedValue(mockResult);
52
+
53
+ const { result } = renderHook(() => useSendTransactionAction(), {
54
+ wrapper: mockFrakProviders,
55
+ });
56
+
57
+ result.current.mutate({
58
+ tx: {
59
+ to: "0x1234567890123456789012345678901234567890",
60
+ data: "0x",
61
+ },
62
+ });
63
+
64
+ await waitFor(() => {
65
+ expect(result.current.isSuccess).toBe(true);
66
+ });
67
+
68
+ expect(result.current.data).toEqual(mockResult);
69
+ expect(sendTransaction).toHaveBeenCalledTimes(1);
70
+ });
71
+
72
+ test("should send transaction with metadata", async ({
73
+ mockFrakProviders,
74
+ }) => {
75
+ const mockResult: SendTransactionReturnType = {
76
+ hash: "0xhash123" as Hex,
77
+ };
78
+
79
+ vi.mocked(sendTransaction).mockResolvedValue(mockResult);
80
+
81
+ const { result } = renderHook(() => useSendTransactionAction(), {
82
+ wrapper: mockFrakProviders,
83
+ });
84
+
85
+ result.current.mutate({
86
+ tx: {
87
+ to: "0x1234567890123456789012345678901234567890",
88
+ data: "0x",
89
+ },
90
+ metadata: {
91
+ header: {
92
+ title: "Send Transaction",
93
+ },
94
+ },
95
+ });
96
+
97
+ await waitFor(() => {
98
+ expect(result.current.isSuccess).toBe(true);
99
+ });
100
+
101
+ expect(result.current.data).toEqual(mockResult);
102
+ });
103
+
104
+ test("should handle mutateAsync", async ({ mockFrakProviders }) => {
105
+ const mockResult: SendTransactionReturnType = {
106
+ hash: "0xasync123" as Hex,
107
+ };
108
+
109
+ vi.mocked(sendTransaction).mockResolvedValue(mockResult);
110
+
111
+ const { result } = renderHook(() => useSendTransactionAction(), {
112
+ wrapper: mockFrakProviders,
113
+ });
114
+
115
+ const response = await result.current.mutateAsync({
116
+ tx: {
117
+ to: "0x1234567890123456789012345678901234567890",
118
+ data: "0x",
119
+ },
120
+ });
121
+
122
+ expect(response).toEqual(mockResult);
123
+
124
+ await waitFor(() => {
125
+ expect(result.current.isSuccess).toBe(true);
126
+ });
127
+ });
128
+
129
+ test("should handle RPC errors", async ({ mockFrakProviders }) => {
130
+ const error = new Error("Transaction send failed");
131
+ vi.mocked(sendTransaction).mockRejectedValue(error);
132
+
133
+ const { result } = renderHook(() => useSendTransactionAction(), {
134
+ wrapper: mockFrakProviders,
135
+ });
136
+
137
+ result.current.mutate({
138
+ tx: {
139
+ to: "0x1234567890123456789012345678901234567890",
140
+ data: "0x",
141
+ },
142
+ });
143
+
144
+ await waitFor(() => {
145
+ expect(result.current.isError).toBe(true);
146
+ });
147
+
148
+ expect(result.current.error).toEqual(error);
149
+ });
150
+
151
+ test("should handle mutation options", async ({ mockFrakProviders }) => {
152
+ const mockResult: SendTransactionReturnType = {
153
+ hash: "0xoptions123" as Hex,
154
+ };
155
+
156
+ vi.mocked(sendTransaction).mockResolvedValue(mockResult);
157
+
158
+ const onSuccess = vi.fn();
159
+ const onError = vi.fn();
160
+
161
+ const { result } = renderHook(
162
+ () =>
163
+ useSendTransactionAction({
164
+ mutations: {
165
+ onSuccess,
166
+ onError,
167
+ },
168
+ }),
169
+ {
170
+ wrapper: mockFrakProviders,
171
+ }
172
+ );
173
+
174
+ result.current.mutate({
175
+ tx: {
176
+ to: "0x1234567890123456789012345678901234567890",
177
+ data: "0x",
178
+ },
179
+ });
180
+
181
+ await waitFor(() => {
182
+ expect(result.current.isSuccess).toBe(true);
183
+ });
184
+
185
+ expect(onSuccess).toHaveBeenCalled();
186
+ expect(onError).not.toHaveBeenCalled();
187
+ });
188
+
189
+ test("should reset mutation state", async ({ mockFrakProviders }) => {
190
+ const mockResult: SendTransactionReturnType = {
191
+ hash: "0xreset123" as Hex,
192
+ };
193
+
194
+ vi.mocked(sendTransaction).mockResolvedValue(mockResult);
195
+
196
+ const { result } = renderHook(() => useSendTransactionAction(), {
197
+ wrapper: mockFrakProviders,
198
+ });
199
+
200
+ result.current.mutate({
201
+ tx: {
202
+ to: "0x1234567890123456789012345678901234567890",
203
+ data: "0x",
204
+ },
205
+ });
206
+
207
+ await waitFor(() => {
208
+ expect(result.current.isSuccess).toBe(true);
209
+ });
210
+
211
+ result.current.reset();
212
+
213
+ await waitFor(() => {
214
+ expect(result.current.data).toBeUndefined();
215
+ expect(result.current.isIdle).toBe(true);
216
+ });
217
+ });
218
+ });
@@ -0,0 +1,62 @@
1
+ import type { SendTransactionReturnType } from "@frak-labs/core-sdk";
2
+ import {
3
+ type SendTransactionParams,
4
+ sendTransaction,
5
+ } from "@frak-labs/core-sdk/actions";
6
+ import { ClientNotFound, type FrakRpcError } from "@frak-labs/frame-connector";
7
+ import { type UseMutationOptions, useMutation } from "@tanstack/react-query";
8
+ import { useFrakClient } from "./useFrakClient";
9
+
10
+ /** @ignore */
11
+ type MutationOptions = Omit<
12
+ UseMutationOptions<
13
+ SendTransactionReturnType,
14
+ FrakRpcError,
15
+ SendTransactionParams
16
+ >,
17
+ "mutationFn" | "mutationKey"
18
+ >;
19
+
20
+ /** @inline */
21
+ interface UseSendTransactionParams {
22
+ /**
23
+ * Optional mutation options, see {@link @tanstack/react-query!useMutation | `useMutation()`} for more infos
24
+ */
25
+ mutations?: MutationOptions;
26
+ }
27
+
28
+ /**
29
+ * Hook that return a mutation helping to send a transaction
30
+ *
31
+ * It's a {@link @tanstack/react-query!home | `tanstack`} wrapper around the {@link @frak-labs/core-sdk!actions.sendTransaction | `sendTransaction()`} action
32
+ *
33
+ * @param args - Optional config object with `mutations` for customizing the underlying {@link @tanstack/react-query!useMutation | `useMutation()`}
34
+ *
35
+ * @group hooks
36
+ *
37
+ * @returns
38
+ * The mutation hook wrapping the `sendTransaction()` action
39
+ * The `mutate` and `mutateAsync` argument is of type {@link @frak-labs/core-sdk!actions.SendTransactionParams | `SendTransactionParams`}
40
+ * The `data` result is a {@link @frak-labs/core-sdk!index.SendTransactionReturnType | `SendTransactionReturnType`}
41
+ *
42
+ * @see {@link @frak-labs/core-sdk!actions.sendTransaction | `sendTransaction()`} for more info about the underlying action
43
+ * @see {@link @tanstack/react-query!useMutation | `useMutation()`} for more info about the mutation options and response
44
+ */
45
+ export function useSendTransactionAction({
46
+ mutations,
47
+ }: UseSendTransactionParams = {}) {
48
+ const client = useFrakClient();
49
+
50
+ return useMutation({
51
+ ...mutations,
52
+ mutationKey: ["frak-sdk", "send-transaction"],
53
+ mutationFn: async (params: SendTransactionParams) => {
54
+ if (!client) {
55
+ throw new ClientNotFound();
56
+ }
57
+
58
+ // Send the transaction
59
+ return sendTransaction(client, params);
60
+ },
61
+ });
62
+ }
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Tests for useSiweAuthenticate hook
3
+ * Tests TanStack Mutation wrapper for SIWE authentication
4
+ */
5
+
6
+ import { vi } from "vitest";
7
+
8
+ vi.mock("@frak-labs/core-sdk/actions");
9
+
10
+ import type { SiweAuthenticateReturnType } from "@frak-labs/core-sdk";
11
+ import { siweAuthenticate } from "@frak-labs/core-sdk/actions";
12
+ import { ClientNotFound } from "@frak-labs/frame-connector";
13
+ import { renderHook, waitFor } from "@testing-library/react";
14
+ import { describe, expect, test } from "../../tests/vitest-fixtures";
15
+ import { useSiweAuthenticate } from "./useSiweAuthenticate";
16
+
17
+ describe("useSiweAuthenticate", () => {
18
+ test("should throw ClientNotFound when client is not available", async ({
19
+ queryWrapper,
20
+ }) => {
21
+ const { result } = renderHook(() => useSiweAuthenticate(), {
22
+ wrapper: queryWrapper.wrapper,
23
+ });
24
+
25
+ await waitFor(() => {
26
+ expect(result.current.mutate).toBeDefined();
27
+ });
28
+
29
+ result.current.mutate({
30
+ siwe: {
31
+ domain: "example.com",
32
+ uri: "https://example.com",
33
+ },
34
+ });
35
+
36
+ await waitFor(() => {
37
+ expect(result.current.isError).toBe(true);
38
+ });
39
+
40
+ expect(result.current.error).toBeInstanceOf(ClientNotFound);
41
+ });
42
+
43
+ test("should authenticate with SIWE successfully", async ({
44
+ mockFrakProviders,
45
+ }) => {
46
+ const mockResult: SiweAuthenticateReturnType = {
47
+ signature: "0xsignature123",
48
+ message: "Example message to sign",
49
+ };
50
+
51
+ vi.mocked(siweAuthenticate).mockResolvedValue(mockResult);
52
+
53
+ const { result } = renderHook(() => useSiweAuthenticate(), {
54
+ wrapper: mockFrakProviders,
55
+ });
56
+
57
+ result.current.mutate({
58
+ siwe: {
59
+ domain: "example.com",
60
+ uri: "https://example.com",
61
+ },
62
+ });
63
+
64
+ await waitFor(() => {
65
+ expect(result.current.isSuccess).toBe(true);
66
+ });
67
+
68
+ expect(result.current.data).toEqual(mockResult);
69
+ expect(siweAuthenticate).toHaveBeenCalledTimes(1);
70
+ });
71
+
72
+ test("should authenticate with custom SIWE params", async ({
73
+ mockFrakProviders,
74
+ }) => {
75
+ const mockResult: SiweAuthenticateReturnType = {
76
+ signature: "0xsignature456",
77
+ message: "Custom message",
78
+ };
79
+
80
+ vi.mocked(siweAuthenticate).mockResolvedValue(mockResult);
81
+
82
+ const { result } = renderHook(() => useSiweAuthenticate(), {
83
+ wrapper: mockFrakProviders,
84
+ });
85
+
86
+ const siweParams = {
87
+ domain: "custom.com",
88
+ uri: "https://custom.com/auth",
89
+ statement: "Sign in to Custom App",
90
+ nonce: "random-nonce-123",
91
+ };
92
+
93
+ result.current.mutate({
94
+ siwe: siweParams,
95
+ });
96
+
97
+ await waitFor(() => {
98
+ expect(result.current.isSuccess).toBe(true);
99
+ });
100
+
101
+ expect(result.current.data).toEqual(mockResult);
102
+ expect(siweAuthenticate).toHaveBeenCalledWith(
103
+ expect.anything(),
104
+ expect.objectContaining({
105
+ siwe: siweParams,
106
+ })
107
+ );
108
+ });
109
+
110
+ test("should authenticate with metadata", async ({ mockFrakProviders }) => {
111
+ const mockResult: SiweAuthenticateReturnType = {
112
+ signature: "0xsignature789",
113
+ message: "Message with metadata",
114
+ };
115
+
116
+ vi.mocked(siweAuthenticate).mockResolvedValue(mockResult);
117
+
118
+ const { result } = renderHook(() => useSiweAuthenticate(), {
119
+ wrapper: mockFrakProviders,
120
+ });
121
+
122
+ result.current.mutate({
123
+ siwe: {
124
+ domain: "example.com",
125
+ uri: "https://example.com",
126
+ },
127
+ metadata: {
128
+ header: {
129
+ title: "Sign In with Ethereum",
130
+ },
131
+ },
132
+ });
133
+
134
+ await waitFor(() => {
135
+ expect(result.current.isSuccess).toBe(true);
136
+ });
137
+
138
+ expect(result.current.data).toEqual(mockResult);
139
+ });
140
+
141
+ test("should handle mutateAsync", async ({ mockFrakProviders }) => {
142
+ const mockResult: SiweAuthenticateReturnType = {
143
+ signature: "0xasync123",
144
+ message: "Async message",
145
+ };
146
+
147
+ vi.mocked(siweAuthenticate).mockResolvedValue(mockResult);
148
+
149
+ const { result } = renderHook(() => useSiweAuthenticate(), {
150
+ wrapper: mockFrakProviders,
151
+ });
152
+
153
+ const response = await result.current.mutateAsync({
154
+ siwe: {
155
+ domain: "example.com",
156
+ uri: "https://example.com",
157
+ },
158
+ });
159
+
160
+ expect(response).toEqual(mockResult);
161
+
162
+ await waitFor(() => {
163
+ expect(result.current.isSuccess).toBe(true);
164
+ });
165
+ });
166
+
167
+ test("should handle RPC errors", async ({ mockFrakProviders }) => {
168
+ const error = new Error("SIWE authentication failed");
169
+ vi.mocked(siweAuthenticate).mockRejectedValue(error);
170
+
171
+ const { result } = renderHook(() => useSiweAuthenticate(), {
172
+ wrapper: mockFrakProviders,
173
+ });
174
+
175
+ result.current.mutate({
176
+ siwe: {
177
+ domain: "example.com",
178
+ uri: "https://example.com",
179
+ },
180
+ });
181
+
182
+ await waitFor(() => {
183
+ expect(result.current.isError).toBe(true);
184
+ });
185
+
186
+ expect(result.current.error).toEqual(error);
187
+ });
188
+
189
+ test("should handle mutation options", async ({ mockFrakProviders }) => {
190
+ const mockResult: SiweAuthenticateReturnType = {
191
+ signature: "0xoptions123",
192
+ message: "Options message",
193
+ };
194
+
195
+ vi.mocked(siweAuthenticate).mockResolvedValue(mockResult);
196
+
197
+ const onSuccess = vi.fn();
198
+ const onError = vi.fn();
199
+
200
+ const { result } = renderHook(
201
+ () =>
202
+ useSiweAuthenticate({
203
+ mutations: {
204
+ onSuccess,
205
+ onError,
206
+ },
207
+ }),
208
+ {
209
+ wrapper: mockFrakProviders,
210
+ }
211
+ );
212
+
213
+ result.current.mutate({
214
+ siwe: {
215
+ domain: "example.com",
216
+ uri: "https://example.com",
217
+ },
218
+ });
219
+
220
+ await waitFor(() => {
221
+ expect(result.current.isSuccess).toBe(true);
222
+ });
223
+
224
+ expect(onSuccess).toHaveBeenCalled();
225
+ expect(onError).not.toHaveBeenCalled();
226
+ });
227
+
228
+ test("should reset mutation state", async ({ mockFrakProviders }) => {
229
+ const mockResult: SiweAuthenticateReturnType = {
230
+ signature: "0xreset123",
231
+ message: "Reset message",
232
+ };
233
+
234
+ vi.mocked(siweAuthenticate).mockResolvedValue(mockResult);
235
+
236
+ const { result } = renderHook(() => useSiweAuthenticate(), {
237
+ wrapper: mockFrakProviders,
238
+ });
239
+
240
+ result.current.mutate({
241
+ siwe: {
242
+ domain: "example.com",
243
+ uri: "https://example.com",
244
+ },
245
+ });
246
+
247
+ await waitFor(() => {
248
+ expect(result.current.isSuccess).toBe(true);
249
+ });
250
+
251
+ result.current.reset();
252
+
253
+ await waitFor(() => {
254
+ expect(result.current.data).toBeUndefined();
255
+ expect(result.current.isIdle).toBe(true);
256
+ });
257
+ });
258
+ });
@@ -0,0 +1,66 @@
1
+ import type { SiweAuthenticateReturnType } from "@frak-labs/core-sdk";
2
+ import {
3
+ type SiweAuthenticateModalParams,
4
+ siweAuthenticate,
5
+ } from "@frak-labs/core-sdk/actions";
6
+ import { ClientNotFound, type FrakRpcError } from "@frak-labs/frame-connector";
7
+ import { type UseMutationOptions, useMutation } from "@tanstack/react-query";
8
+ import { useFrakClient } from "./useFrakClient";
9
+
10
+ /** @inline */
11
+ type MutationOptions = Omit<
12
+ UseMutationOptions<
13
+ SiweAuthenticateReturnType,
14
+ FrakRpcError,
15
+ SiweAuthenticateModalParams
16
+ >,
17
+ "mutationFn" | "mutationKey"
18
+ >;
19
+
20
+ /** @ignore */
21
+ interface UseSiweAuthenticateParams {
22
+ /**
23
+ * Optional mutation options, see {@link @tanstack/react-query!useMutation | `useMutation()`} for more infos
24
+ */
25
+ mutations?: MutationOptions;
26
+ }
27
+
28
+ /**
29
+ * Hook that return a mutation helping to send perform a SIWE authentication
30
+ *
31
+ * It's a {@link @tanstack/react-query!home | `tanstack`} wrapper around the {@link @frak-labs/core-sdk!actions.siweAuthenticate | `siweAuthenticate()`} action
32
+ *
33
+ * @param args - Optional config object with `mutations` for customizing the underlying {@link @tanstack/react-query!useMutation | `useMutation()`}
34
+ *
35
+ * @group hooks
36
+ *
37
+ * @returns
38
+ * The mutation hook wrapping the `siweAuthenticate()` action
39
+ * The `mutate` and `mutateAsync` argument is of type {@link @frak-labs/core-sdk!actions.SiweAuthenticateModalParams | `SiweAuthenticateModalParams`}
40
+ * The `data` result is a {@link @frak-labs/core-sdk!index.SiweAuthenticateReturnType | `SiweAuthenticateReturnType`}
41
+ *
42
+ * @see {@link @frak-labs/core-sdk!actions.siweAuthenticate | `siweAuthenticate()`} for more info about the underlying action
43
+ * @see {@link @tanstack/react-query!useMutation | `useMutation()`} for more info about the mutation options and response
44
+ */
45
+ export function useSiweAuthenticate({
46
+ mutations,
47
+ }: UseSiweAuthenticateParams = {}) {
48
+ const client = useFrakClient();
49
+
50
+ return useMutation({
51
+ ...mutations,
52
+ mutationKey: [
53
+ "frak-sdk",
54
+ "siwe-authenticate",
55
+ client?.config.domain ?? "no-domain",
56
+ ],
57
+ mutationFn: async (params: SiweAuthenticateModalParams) => {
58
+ if (!client) {
59
+ throw new ClientNotFound();
60
+ }
61
+
62
+ // Launch the authentication
63
+ return siweAuthenticate(client, params);
64
+ },
65
+ });
66
+ }