@frak-labs/react-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.
- package/README.md +25 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +29 -68
- package/dist/index.d.ts +28 -67
- package/dist/index.js +1 -1
- package/package.json +17 -16
- package/src/hook/helper/useReferralInteraction.test.ts +309 -0
- package/src/hook/helper/useReferralInteraction.ts +73 -0
- package/src/hook/index.ts +10 -0
- package/src/hook/useDisplayModal.test.ts +275 -0
- package/src/hook/useDisplayModal.ts +68 -0
- package/src/hook/useFrakClient.test.ts +119 -0
- package/src/hook/useFrakClient.ts +11 -0
- package/src/hook/useFrakConfig.test.ts +184 -0
- package/src/hook/useFrakConfig.ts +22 -0
- package/src/hook/useGetMerchantInformation.ts +56 -0
- package/src/hook/useOpenSso.test.ts +202 -0
- package/src/hook/useOpenSso.ts +51 -0
- package/src/hook/usePrepareSso.test.ts +197 -0
- package/src/hook/usePrepareSso.ts +55 -0
- package/src/hook/useSendTransaction.test.ts +218 -0
- package/src/hook/useSendTransaction.ts +62 -0
- package/src/hook/useSiweAuthenticate.test.ts +258 -0
- package/src/hook/useSiweAuthenticate.ts +66 -0
- package/src/hook/useWalletStatus.test.ts +112 -0
- package/src/hook/useWalletStatus.ts +55 -0
- package/src/hook/utils/useFrakContext.test.ts +157 -0
- package/src/hook/utils/useFrakContext.ts +42 -0
- package/src/hook/utils/useMounted.test.ts +70 -0
- package/src/hook/utils/useMounted.ts +12 -0
- package/src/hook/utils/useWindowLocation.test.ts +54 -0
- package/src/hook/utils/useWindowLocation.ts +40 -0
- package/src/index.ts +25 -0
- package/src/provider/FrakConfigProvider.test.ts +246 -0
- package/src/provider/FrakConfigProvider.ts +54 -0
- package/src/provider/FrakIFrameClientProvider.test.tsx +209 -0
- package/src/provider/FrakIFrameClientProvider.ts +86 -0
- package/src/provider/index.ts +7 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { OpenSsoParamsType, OpenSsoReturnType } from "@frak-labs/core-sdk";
|
|
2
|
+
import { openSso } from "@frak-labs/core-sdk/actions";
|
|
3
|
+
import { ClientNotFound, type FrakRpcError } from "@frak-labs/frame-connector";
|
|
4
|
+
import { type UseMutationOptions, useMutation } from "@tanstack/react-query";
|
|
5
|
+
import { useFrakClient } from "./useFrakClient";
|
|
6
|
+
|
|
7
|
+
/** @ignore */
|
|
8
|
+
type MutationOptions = Omit<
|
|
9
|
+
UseMutationOptions<OpenSsoReturnType, FrakRpcError, OpenSsoParamsType>,
|
|
10
|
+
"mutationFn" | "mutationKey"
|
|
11
|
+
>;
|
|
12
|
+
|
|
13
|
+
/** @inline */
|
|
14
|
+
interface UseOpenSsoParams {
|
|
15
|
+
/**
|
|
16
|
+
* Optional mutation options, see {@link @tanstack/react-query!useMutation | `useMutation()`} for more infos
|
|
17
|
+
*/
|
|
18
|
+
mutations?: MutationOptions;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Hook that return a mutation helping to open the SSO page
|
|
23
|
+
*
|
|
24
|
+
* It's a {@link @tanstack/react-query!home | `tanstack`} wrapper around the {@link @frak-labs/core-sdk!actions.openSso | `openSso()`} action
|
|
25
|
+
*
|
|
26
|
+
* @param args - Optional config object with `mutations` for customizing the underlying {@link @tanstack/react-query!useMutation | `useMutation()`}
|
|
27
|
+
*
|
|
28
|
+
* @group hooks
|
|
29
|
+
*
|
|
30
|
+
* @returns
|
|
31
|
+
* The mutation hook wrapping the `openSso()` action
|
|
32
|
+
* The `mutate` and `mutateAsync` argument is of type {@link @frak-labs/core-sdk!index.OpenSsoParamsType | `OpenSsoParamsType`}
|
|
33
|
+
* The mutation doesn't output any value
|
|
34
|
+
*
|
|
35
|
+
* @see {@link @frak-labs/core-sdk!actions.openSso | `openSso()`} for more info about the underlying action
|
|
36
|
+
* @see {@link @tanstack/react-query!useMutation | `useMutation()`} for more info about the mutation options and response
|
|
37
|
+
*/
|
|
38
|
+
export function useOpenSso({ mutations }: UseOpenSsoParams = {}) {
|
|
39
|
+
const client = useFrakClient();
|
|
40
|
+
|
|
41
|
+
return useMutation({
|
|
42
|
+
...mutations,
|
|
43
|
+
mutationKey: ["frak-sdk", "open-sso"],
|
|
44
|
+
mutationFn: async (params: OpenSsoParamsType) => {
|
|
45
|
+
if (!client) {
|
|
46
|
+
throw new ClientNotFound();
|
|
47
|
+
}
|
|
48
|
+
return openSso(client, params);
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for usePrepareSso hook
|
|
3
|
+
* Tests TanStack Query wrapper for preparing SSO URLs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { vi } from "vitest";
|
|
7
|
+
|
|
8
|
+
vi.mock("@frak-labs/core-sdk/actions");
|
|
9
|
+
|
|
10
|
+
import type { PrepareSsoReturnType } from "@frak-labs/core-sdk";
|
|
11
|
+
import { prepareSso } 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 { usePrepareSso } from "./usePrepareSso";
|
|
16
|
+
|
|
17
|
+
describe("usePrepareSso", () => {
|
|
18
|
+
test("should throw ClientNotFound when client is not available", async ({
|
|
19
|
+
queryWrapper,
|
|
20
|
+
}) => {
|
|
21
|
+
const { result } = renderHook(
|
|
22
|
+
() => usePrepareSso({ directExit: true }),
|
|
23
|
+
{
|
|
24
|
+
wrapper: queryWrapper.wrapper,
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
await waitFor(() => {
|
|
29
|
+
expect(result.current.isError).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(result.current.error).toBeInstanceOf(ClientNotFound);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("should prepare SSO URL successfully", async ({
|
|
36
|
+
mockFrakProviders,
|
|
37
|
+
}) => {
|
|
38
|
+
const mockSsoResult: PrepareSsoReturnType = {
|
|
39
|
+
ssoUrl: "https://wallet-test.frak.id/sso?params=xyz",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
vi.mocked(prepareSso).mockResolvedValue(mockSsoResult);
|
|
43
|
+
|
|
44
|
+
const { result } = renderHook(
|
|
45
|
+
() => usePrepareSso({ directExit: true }),
|
|
46
|
+
{
|
|
47
|
+
wrapper: mockFrakProviders,
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
await waitFor(() => {
|
|
52
|
+
expect(result.current.isSuccess).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(result.current.data).toEqual(mockSsoResult);
|
|
56
|
+
expect(result.current.data?.ssoUrl).toContain("sso");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("should handle SSO params with redirectUrl", async ({
|
|
60
|
+
mockFrakProviders,
|
|
61
|
+
}) => {
|
|
62
|
+
const mockSsoResult: PrepareSsoReturnType = {
|
|
63
|
+
ssoUrl: "https://wallet-test.frak.id/sso?redirect=example.com",
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
vi.mocked(prepareSso).mockResolvedValue(mockSsoResult);
|
|
67
|
+
|
|
68
|
+
const { result } = renderHook(
|
|
69
|
+
() =>
|
|
70
|
+
usePrepareSso({
|
|
71
|
+
redirectUrl: "https://example.com/callback",
|
|
72
|
+
directExit: false,
|
|
73
|
+
}),
|
|
74
|
+
{
|
|
75
|
+
wrapper: mockFrakProviders,
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
await waitFor(() => {
|
|
80
|
+
expect(result.current.isSuccess).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(result.current.data).toEqual(mockSsoResult);
|
|
84
|
+
expect(prepareSso).toHaveBeenCalledWith(
|
|
85
|
+
expect.anything(),
|
|
86
|
+
expect.objectContaining({
|
|
87
|
+
redirectUrl: "https://example.com/callback",
|
|
88
|
+
directExit: false,
|
|
89
|
+
})
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("should handle SSO params with metadata", async ({
|
|
94
|
+
mockFrakProviders,
|
|
95
|
+
}) => {
|
|
96
|
+
const mockSsoResult: PrepareSsoReturnType = {
|
|
97
|
+
ssoUrl: "https://wallet-test.frak.id/sso?metadata=xyz",
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
vi.mocked(prepareSso).mockResolvedValue(mockSsoResult);
|
|
101
|
+
|
|
102
|
+
const { result } = renderHook(
|
|
103
|
+
() =>
|
|
104
|
+
usePrepareSso({
|
|
105
|
+
metadata: {
|
|
106
|
+
logoUrl: "https://example.com/logo.png",
|
|
107
|
+
homepageLink: "https://example.com",
|
|
108
|
+
},
|
|
109
|
+
directExit: true,
|
|
110
|
+
}),
|
|
111
|
+
{
|
|
112
|
+
wrapper: mockFrakProviders,
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
await waitFor(() => {
|
|
117
|
+
expect(result.current.isSuccess).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
expect(result.current.data).toEqual(mockSsoResult);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("should handle RPC errors", async ({ mockFrakProviders }) => {
|
|
124
|
+
const error = new Error("SSO preparation failed");
|
|
125
|
+
vi.mocked(prepareSso).mockRejectedValue(error);
|
|
126
|
+
|
|
127
|
+
const { result } = renderHook(
|
|
128
|
+
() => usePrepareSso({ directExit: true }),
|
|
129
|
+
{
|
|
130
|
+
wrapper: mockFrakProviders,
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
await waitFor(() => {
|
|
135
|
+
expect(result.current.isError).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
expect(result.current.error).toEqual(error);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("should update query key with params", async ({
|
|
142
|
+
mockFrakProviders,
|
|
143
|
+
}) => {
|
|
144
|
+
const mockSsoResult: PrepareSsoReturnType = {
|
|
145
|
+
ssoUrl: "https://wallet-test.frak.id/sso?params=abc",
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
vi.mocked(prepareSso).mockResolvedValue(mockSsoResult);
|
|
149
|
+
|
|
150
|
+
const params = {
|
|
151
|
+
directExit: false,
|
|
152
|
+
redirectUrl: "https://example.com",
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const { result, rerender } = renderHook(() => usePrepareSso(params), {
|
|
156
|
+
wrapper: mockFrakProviders,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await waitFor(() => {
|
|
160
|
+
expect(result.current.isSuccess).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
expect(result.current.data).toEqual(mockSsoResult);
|
|
164
|
+
|
|
165
|
+
// Query key includes params, so changes should trigger re-fetch
|
|
166
|
+
rerender();
|
|
167
|
+
expect(result.current.data).toEqual(mockSsoResult);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("should call prepareSso with client and params", async ({
|
|
171
|
+
mockFrakProviders,
|
|
172
|
+
mockFrakClient,
|
|
173
|
+
}) => {
|
|
174
|
+
const mockSsoResult: PrepareSsoReturnType = {
|
|
175
|
+
ssoUrl: "https://wallet-test.frak.id/sso?test=123",
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
vi.mocked(prepareSso).mockResolvedValue(mockSsoResult);
|
|
179
|
+
|
|
180
|
+
const params = {
|
|
181
|
+
directExit: true,
|
|
182
|
+
metadata: {
|
|
183
|
+
logoUrl: "https://example.com/logo.png",
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const { result } = renderHook(() => usePrepareSso(params), {
|
|
188
|
+
wrapper: mockFrakProviders,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
await waitFor(() => {
|
|
192
|
+
expect(result.current.isSuccess).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(prepareSso).toHaveBeenCalledWith(mockFrakClient, params);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { PrepareSsoParamsType } from "@frak-labs/core-sdk";
|
|
2
|
+
import { prepareSso } from "@frak-labs/core-sdk/actions";
|
|
3
|
+
import { ClientNotFound } from "@frak-labs/frame-connector";
|
|
4
|
+
import { useQuery } from "@tanstack/react-query";
|
|
5
|
+
import { useFrakClient } from "./useFrakClient";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Hook that generates SSO URL for popup flow
|
|
9
|
+
*
|
|
10
|
+
* This is a **synchronous** hook (no async calls) that generates the SSO URL
|
|
11
|
+
* client-side without communicating with the wallet iframe.
|
|
12
|
+
*
|
|
13
|
+
* @param params - SSO parameters for URL generation
|
|
14
|
+
*
|
|
15
|
+
* @group hooks
|
|
16
|
+
*
|
|
17
|
+
* @returns
|
|
18
|
+
* Object containing:
|
|
19
|
+
* - `ssoUrl`: Generated SSO URL (or undefined if client not ready)
|
|
20
|
+
* - `isReady`: Boolean indicating if URL is available
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* function MyComponent() {
|
|
25
|
+
* const { data } = usePrepareSso({
|
|
26
|
+
* metadata: { logoUrl: "..." },
|
|
27
|
+
* directExit: true
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* const handleClick = () => {
|
|
31
|
+
* if (ssoUrl) {
|
|
32
|
+
* window.open(data?.ssoUrl, "_blank");
|
|
33
|
+
* }
|
|
34
|
+
* };
|
|
35
|
+
*
|
|
36
|
+
* return <button onClick={handleClick} disabled={!isReady}>Login</button>;
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @see {@link @frak-labs/core-sdk!actions.prepareSso | `prepareSso()`} for the underlying action
|
|
41
|
+
* @see {@link @frak-labs/core-sdk!actions.openSso | `openSso()`} for the recommended high-level API
|
|
42
|
+
*/
|
|
43
|
+
export function usePrepareSso(params: PrepareSsoParamsType) {
|
|
44
|
+
const client = useFrakClient();
|
|
45
|
+
|
|
46
|
+
return useQuery({
|
|
47
|
+
queryKey: ["frak-sdk", "prepare-sso", params],
|
|
48
|
+
queryFn: async () => {
|
|
49
|
+
if (!client) {
|
|
50
|
+
throw new ClientNotFound();
|
|
51
|
+
}
|
|
52
|
+
return prepareSso(client, params);
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -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
|
+
}
|