eth-twc-sdk-react 0.1.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.
@@ -0,0 +1,231 @@
1
+ import { renderHook, waitFor } from "@testing-library/react";
2
+ import { beforeEach, describe, expect, test } from "bun:test";
3
+ import type { Hex } from "viem";
4
+
5
+ import {
6
+ useSignBatchTransferWithCommit,
7
+ useSignCancelAuthorization,
8
+ useSignSingleTransfer,
9
+ useSignUniCommitTransfers,
10
+ } from "../hooks/sign.ts";
11
+ import { ADDR, ADDR_B, COMMIT } from "./fixtures.ts";
12
+ import { createQueryWrapper, createTestQueryClient } from "./queryClient.tsx";
13
+ import { resetSdkConfigAddress, setSdkConfigAddressZero } from "./sdkConfigState.ts";
14
+ import {
15
+ stubClientsGetEip712DomainRejects,
16
+ stubClientsSignSuccess,
17
+ stubClientsSignTypedDataRejects,
18
+ stubPublicClient,
19
+ stubWalletClient,
20
+ } from "./stubs.ts";
21
+ import { resetWagmiState, wagmiState } from "./wagmiState.ts";
22
+
23
+ describe("useSign*", () => {
24
+ beforeEach(() => {
25
+ resetWagmiState();
26
+ resetSdkConfigAddress();
27
+ });
28
+
29
+ test("未接続時は mutate が narrowWriteClients で失敗", async () => {
30
+ wagmiState.publicClient = stubPublicClient();
31
+ wagmiState.walletClient = stubWalletClient();
32
+ wagmiState.address = undefined;
33
+
34
+ const qc = createTestQueryClient();
35
+ const { result } = renderHook(() => useSignSingleTransfer(), {
36
+ wrapper: createQueryWrapper(qc),
37
+ });
38
+ result.current.mutate({
39
+ from: ADDR,
40
+ to: ADDR_B,
41
+ token: ADDR,
42
+ executor: ADDR_B,
43
+ value: 1n,
44
+ commitment: COMMIT,
45
+ });
46
+ await waitFor(() => expect(result.current.isError).toBe(true));
47
+ expect(result.current.error?.message).toMatch(
48
+ /TWC: connect a wallet to send or sign/,
49
+ );
50
+ });
51
+
52
+ test("sdk_js: Signer と EIP-712 from が不一致なら署名前に失敗", async () => {
53
+ const { publicClient, walletClient } = stubClientsSignSuccess();
54
+ wagmiState.publicClient = publicClient;
55
+ wagmiState.walletClient = walletClient;
56
+ wagmiState.address = ADDR_B as Hex;
57
+
58
+ const qc = createTestQueryClient();
59
+ const { result } = renderHook(() => useSignSingleTransfer(), {
60
+ wrapper: createQueryWrapper(qc),
61
+ });
62
+ result.current.mutate({
63
+ from: ADDR,
64
+ to: ADDR_B,
65
+ token: ADDR,
66
+ executor: ADDR_B,
67
+ value: 1n,
68
+ commitment: COMMIT,
69
+ });
70
+ await waitFor(() => expect(result.current.isError).toBe(true));
71
+ expect(result.current.error?.message).toMatch(
72
+ /Signer account .* does not match EIP-712 message from/,
73
+ );
74
+ });
75
+
76
+ test("config ゼロアドレスでは assert が失敗", async () => {
77
+ setSdkConfigAddressZero();
78
+ const { publicClient: pc, walletClient } = stubClientsSignSuccess();
79
+ wagmiState.publicClient = stubPublicClient({
80
+ getEip712Domain: pc.getEip712Domain,
81
+ getCode: async () => "0x",
82
+ });
83
+ wagmiState.walletClient = walletClient;
84
+ wagmiState.address = ADDR as Hex;
85
+
86
+ const qc = createTestQueryClient();
87
+ const { result } = renderHook(() => useSignSingleTransfer(), {
88
+ wrapper: createQueryWrapper(qc),
89
+ });
90
+ result.current.mutate({
91
+ from: ADDR,
92
+ to: ADDR_B,
93
+ token: ADDR,
94
+ executor: ADDR_B,
95
+ value: 1n,
96
+ commitment: COMMIT,
97
+ });
98
+ await waitFor(() => expect(result.current.isError).toBe(true));
99
+ expect(result.current.error?.message).toMatch(
100
+ /not deployed at/,
101
+ );
102
+ });
103
+
104
+ test("RPC: getEip712Domain が失敗したらエラーが伝播", async () => {
105
+ const { publicClient, walletClient } = stubClientsGetEip712DomainRejects();
106
+ wagmiState.publicClient = publicClient;
107
+ wagmiState.walletClient = walletClient;
108
+ wagmiState.address = ADDR as Hex;
109
+
110
+ const qc = createTestQueryClient();
111
+ const { result } = renderHook(() => useSignSingleTransfer(), {
112
+ wrapper: createQueryWrapper(qc),
113
+ });
114
+ result.current.mutate({
115
+ from: ADDR,
116
+ to: ADDR_B,
117
+ token: ADDR,
118
+ executor: ADDR_B,
119
+ value: 1n,
120
+ commitment: COMMIT,
121
+ });
122
+ await waitFor(() => expect(result.current.isError).toBe(true));
123
+ expect(result.current.error?.message).toMatch(/getEip712Domain failed/);
124
+ });
125
+
126
+ test("ウォレット: signTypedData が拒否されたらエラーが伝播", async () => {
127
+ const { publicClient, walletClient } = stubClientsSignTypedDataRejects();
128
+ wagmiState.publicClient = publicClient;
129
+ wagmiState.walletClient = walletClient;
130
+ wagmiState.address = ADDR as Hex;
131
+
132
+ const qc = createTestQueryClient();
133
+ const { result } = renderHook(() => useSignSingleTransfer(), {
134
+ wrapper: createQueryWrapper(qc),
135
+ });
136
+ result.current.mutate({
137
+ from: ADDR,
138
+ to: ADDR_B,
139
+ token: ADDR,
140
+ executor: ADDR_B,
141
+ value: 1n,
142
+ commitment: COMMIT,
143
+ });
144
+ await waitFor(() => expect(result.current.isError).toBe(true));
145
+ expect(result.current.error?.message).toMatch(/User rejected/);
146
+ });
147
+
148
+ test("成功時は Signed* が data に入る", async () => {
149
+ const { publicClient, walletClient, sig } = stubClientsSignSuccess();
150
+ wagmiState.publicClient = publicClient;
151
+ wagmiState.walletClient = walletClient;
152
+ wagmiState.address = ADDR as Hex;
153
+
154
+ const qc = createTestQueryClient();
155
+ const { result } = renderHook(() => useSignSingleTransfer(), {
156
+ wrapper: createQueryWrapper(qc),
157
+ });
158
+ result.current.mutate({
159
+ from: ADDR,
160
+ to: ADDR_B,
161
+ token: ADDR,
162
+ executor: ADDR_B,
163
+ value: 1n,
164
+ commitment: COMMIT,
165
+ });
166
+ await waitFor(() => expect(result.current.isSuccess).toBe(true));
167
+ expect(result.current.data?.signature).toBe(sig);
168
+ });
169
+
170
+ test("useSignUniCommitTransfers: RPC 失敗を伝播", async () => {
171
+ const { publicClient, walletClient } = stubClientsGetEip712DomainRejects();
172
+ wagmiState.publicClient = publicClient;
173
+ wagmiState.walletClient = walletClient;
174
+ wagmiState.address = ADDR as Hex;
175
+
176
+ const qc = createTestQueryClient();
177
+ const { result } = renderHook(() => useSignUniCommitTransfers(), {
178
+ wrapper: createQueryWrapper(qc),
179
+ });
180
+ result.current.mutate({
181
+ from: ADDR,
182
+ executor: ADDR_B,
183
+ details: [{ to: ADDR_B, token: ADDR, value: 1n }],
184
+ commitment: COMMIT,
185
+ });
186
+ await waitFor(() => expect(result.current.isError).toBe(true));
187
+ expect(result.current.error?.message).toMatch(/getEip712Domain failed/);
188
+ });
189
+
190
+ test("useSignBatchTransferWithCommit: RPC 失敗を伝播", async () => {
191
+ const { publicClient, walletClient } = stubClientsGetEip712DomainRejects();
192
+ wagmiState.publicClient = publicClient;
193
+ wagmiState.walletClient = walletClient;
194
+ wagmiState.address = ADDR as Hex;
195
+
196
+ const qc = createTestQueryClient();
197
+ const { result } = renderHook(() => useSignBatchTransferWithCommit(), {
198
+ wrapper: createQueryWrapper(qc),
199
+ });
200
+ result.current.mutate({
201
+ from: ADDR,
202
+ executor: ADDR_B,
203
+ details: [
204
+ { to: ADDR_B, token: ADDR, value: 1n, commitment: COMMIT },
205
+ ],
206
+ batchCommitment: ("0x" + "aa".repeat(32)) as Hex,
207
+ });
208
+ await waitFor(() => expect(result.current.isError).toBe(true));
209
+ expect(result.current.error?.message).toMatch(/getEip712Domain failed/);
210
+ });
211
+
212
+ test("useSignCancelAuthorization: authorizer と接続アドレスが不一致なら失敗", async () => {
213
+ const { publicClient, walletClient } = stubClientsSignSuccess();
214
+ wagmiState.publicClient = publicClient;
215
+ wagmiState.walletClient = walletClient;
216
+ wagmiState.address = ADDR_B as Hex;
217
+
218
+ const qc = createTestQueryClient();
219
+ const { result } = renderHook(() => useSignCancelAuthorization(), {
220
+ wrapper: createQueryWrapper(qc),
221
+ });
222
+ result.current.mutate({
223
+ authorizer: ADDR,
224
+ commitment: COMMIT,
225
+ });
226
+ await waitFor(() => expect(result.current.isError).toBe(true));
227
+ expect(result.current.error?.message).toMatch(
228
+ /Signer account .* does not match EIP-712 message authorizer/,
229
+ );
230
+ });
231
+ });
@@ -0,0 +1,198 @@
1
+ import { renderHook, waitFor } from "@testing-library/react";
2
+ import { beforeEach, describe, expect, test } from "bun:test";
3
+ import type { SignedSingleTransfer } from "eth-twc-sdk-js/signatureTransfer/single";
4
+ import type { Hex } from "viem";
5
+
6
+ import {
7
+ useSendAuthorizedBatchTransfer,
8
+ useSendAuthorizedSingleTransfer,
9
+ useSendAuthorizedUnifiedTransfer,
10
+ useSendCancelAuthorization,
11
+ } from "../hooks/signatureTransfer.ts";
12
+ import { ADDR, ADDR_B, COMMIT, testDomain } from "./fixtures.ts";
13
+ import { createQueryWrapper, createTestQueryClient } from "./queryClient.tsx";
14
+ import { resetSdkConfigAddress, setSdkConfigAddressZero } from "./sdkConfigState.ts";
15
+ import {
16
+ minimalSignedBatch,
17
+ minimalSignedCancel,
18
+ minimalSignedSingleTransfer,
19
+ minimalSignedUni,
20
+ stubClientsSignatureSendSimulateFails,
21
+ stubClientsSignatureSendSuccess,
22
+ stubPublicClient,
23
+ stubWalletClient,
24
+ } from "./stubs.ts";
25
+ import { resetWagmiState, wagmiState } from "./wagmiState.ts";
26
+
27
+ describe("useSendAuthorized* / useSendCancelAuthorization", () => {
28
+ beforeEach(() => {
29
+ resetWagmiState();
30
+ resetSdkConfigAddress();
31
+ });
32
+
33
+ test("未接続時は mutate が失敗", async () => {
34
+ wagmiState.publicClient = stubPublicClient();
35
+ wagmiState.walletClient = stubWalletClient();
36
+ wagmiState.address = undefined;
37
+
38
+ const qc = createTestQueryClient();
39
+ const { result } = renderHook(() => useSendAuthorizedSingleTransfer(), {
40
+ wrapper: createQueryWrapper(qc),
41
+ });
42
+ result.current.mutate(minimalSignedSingleTransfer());
43
+ await waitFor(() => expect(result.current.isError).toBe(true));
44
+ expect(result.current.error?.message).toMatch(
45
+ /TWC: connect a wallet to send or sign/,
46
+ );
47
+ });
48
+
49
+ test("sdk_js: config ゼロでは assert が失敗", async () => {
50
+ setSdkConfigAddressZero();
51
+ const { publicClient: pc, walletClient } = stubClientsSignatureSendSuccess();
52
+ wagmiState.publicClient = stubPublicClient({
53
+ simulateContract: pc.simulateContract,
54
+ getCode: async () => "0x",
55
+ });
56
+ wagmiState.walletClient = walletClient;
57
+ wagmiState.address = ADDR as Hex;
58
+
59
+ const qc = createTestQueryClient();
60
+ const { result } = renderHook(() => useSendAuthorizedSingleTransfer(), {
61
+ wrapper: createQueryWrapper(qc),
62
+ });
63
+ result.current.mutate(minimalSignedSingleTransfer());
64
+ await waitFor(() => expect(result.current.isError).toBe(true));
65
+ expect(result.current.error?.message).toMatch(
66
+ /not deployed at/,
67
+ );
68
+ });
69
+
70
+ test("RPC: simulate が revert 相当で失敗すると伝播", async () => {
71
+ const { publicClient, walletClient } = stubClientsSignatureSendSimulateFails();
72
+ wagmiState.publicClient = publicClient;
73
+ wagmiState.walletClient = walletClient;
74
+ wagmiState.address = ADDR as Hex;
75
+
76
+ const qc = createTestQueryClient();
77
+ const { result } = renderHook(() => useSendAuthorizedSingleTransfer(), {
78
+ wrapper: createQueryWrapper(qc),
79
+ });
80
+ result.current.mutate(minimalSignedSingleTransfer());
81
+ await waitFor(() => expect(result.current.isError).toBe(true));
82
+ expect(result.current.error?.message).toMatch(/RPC: execution reverted/);
83
+ });
84
+
85
+ test("sdk_js: domain の verifyingContract が config と不一致なら失敗", async () => {
86
+ const { publicClient, walletClient } = stubClientsSignatureSendSuccess();
87
+ wagmiState.publicClient = publicClient;
88
+ wagmiState.walletClient = walletClient;
89
+ wagmiState.address = ADDR as Hex;
90
+
91
+ const base = minimalSignedSingleTransfer();
92
+ const wrong: typeof base = {
93
+ ...base,
94
+ domain: {
95
+ ...base.domain,
96
+ verifyingContract: ADDR_B,
97
+ },
98
+ };
99
+
100
+ const qc = createTestQueryClient();
101
+ const { result } = renderHook(() => useSendAuthorizedSingleTransfer(), {
102
+ wrapper: createQueryWrapper(qc),
103
+ });
104
+ result.current.mutate(wrong);
105
+ await waitFor(() => expect(result.current.isError).toBe(true));
106
+ expect(result.current.error?.message).toMatch(
107
+ /verifyingContract .* does not match transferWithCommitmentAddress/,
108
+ );
109
+ });
110
+
111
+ test("成功時は tx ハッシュが data", async () => {
112
+ const { publicClient, walletClient } = stubClientsSignatureSendSuccess();
113
+ wagmiState.publicClient = publicClient;
114
+ wagmiState.walletClient = walletClient;
115
+ wagmiState.address = ADDR as Hex;
116
+
117
+ const qc = createTestQueryClient();
118
+ const { result } = renderHook(() => useSendAuthorizedSingleTransfer(), {
119
+ wrapper: createQueryWrapper(qc),
120
+ });
121
+ result.current.mutate(minimalSignedSingleTransfer());
122
+ await waitFor(() => expect(result.current.isSuccess).toBe(true));
123
+ expect(result.current.data).toMatch(/^0x/);
124
+ });
125
+
126
+ test("useSendAuthorizedUnifiedTransfer が RPC 失敗を伝播", async () => {
127
+ const { publicClient, walletClient } = stubClientsSignatureSendSimulateFails();
128
+ wagmiState.publicClient = publicClient;
129
+ wagmiState.walletClient = walletClient;
130
+ wagmiState.address = ADDR as Hex;
131
+
132
+ const qc = createTestQueryClient();
133
+ const { result } = renderHook(() => useSendAuthorizedUnifiedTransfer(), {
134
+ wrapper: createQueryWrapper(qc),
135
+ });
136
+ result.current.mutate(minimalSignedUni());
137
+ await waitFor(() => expect(result.current.isError).toBe(true));
138
+ expect(result.current.error?.message).toMatch(/RPC: execution reverted/);
139
+ });
140
+
141
+ test("useSendAuthorizedBatchTransfer が RPC 失敗を伝播", async () => {
142
+ const { publicClient, walletClient } = stubClientsSignatureSendSimulateFails();
143
+ wagmiState.publicClient = publicClient;
144
+ wagmiState.walletClient = walletClient;
145
+ wagmiState.address = ADDR as Hex;
146
+
147
+ const qc = createTestQueryClient();
148
+ const { result } = renderHook(() => useSendAuthorizedBatchTransfer(), {
149
+ wrapper: createQueryWrapper(qc),
150
+ });
151
+ result.current.mutate(minimalSignedBatch());
152
+ await waitFor(() => expect(result.current.isError).toBe(true));
153
+ expect(result.current.error?.message).toMatch(/RPC: execution reverted/);
154
+ });
155
+
156
+ test("useSendCancelAuthorization が RPC 失敗を伝播", async () => {
157
+ const { publicClient, walletClient } = stubClientsSignatureSendSimulateFails();
158
+ wagmiState.publicClient = publicClient;
159
+ wagmiState.walletClient = walletClient;
160
+ wagmiState.address = ADDR as Hex;
161
+
162
+ const qc = createTestQueryClient();
163
+ const { result } = renderHook(() => useSendCancelAuthorization(), {
164
+ wrapper: createQueryWrapper(qc),
165
+ });
166
+ result.current.mutate(minimalSignedCancel());
167
+ await waitFor(() => expect(result.current.isError).toBe(true));
168
+ expect(result.current.error?.message).toMatch(/RPC: execution reverted/);
169
+ });
170
+
171
+ test("domain chainId がクライアントと不一致なら失敗", async () => {
172
+ const { publicClient, walletClient } = stubClientsSignatureSendSuccess();
173
+ wagmiState.publicClient = publicClient;
174
+ wagmiState.walletClient = walletClient;
175
+ wagmiState.address = ADDR as Hex;
176
+
177
+ const d = testDomain(undefined, 2);
178
+ const wrongSigned = {
179
+ ...minimalSignedSingleTransfer(),
180
+ domain: {
181
+ name: d.name,
182
+ version: d.version,
183
+ chainId: d.chainId,
184
+ verifyingContract: d.verifyingContract,
185
+ },
186
+ };
187
+
188
+ const qc = createTestQueryClient();
189
+ const { result } = renderHook(() => useSendAuthorizedSingleTransfer(), {
190
+ wrapper: createQueryWrapper(qc),
191
+ });
192
+ result.current.mutate(wrongSigned as SignedSingleTransfer);
193
+ await waitFor(() => expect(result.current.isError).toBe(true));
194
+ expect(result.current.error?.message).toMatch(
195
+ /domain\.chainId .* does not match client chain id/,
196
+ );
197
+ });
198
+ });
@@ -0,0 +1,230 @@
1
+ import { renderHook, waitFor } from "@testing-library/react";
2
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
3
+ import { transferWithCommitmentAbi } from "eth-twc-sdk-js/abi";
4
+ import { transferWithCommitmentAddress } from "eth-twc-sdk-js/config";
5
+ import { encodeAbiParameters, encodeEventTopics } from "viem";
6
+ import { mainnet } from "viem/chains";
7
+ import type { Chain, Hex, PublicClient } from "viem";
8
+
9
+ import {
10
+ useTransferWithCommitmentSentLogs,
11
+ useVerifyTransfer,
12
+ } from "../hooks/verify.ts";
13
+ import { ADDR, COMMIT } from "./fixtures.ts";
14
+ import { createQueryWrapper, createTestQueryClient } from "./queryClient.tsx";
15
+ import { resetSdkConfigAddress, setSdkConfigAddressZero } from "./sdkConfigState.ts";
16
+ import { stubPublicClient } from "./stubs.ts";
17
+ import { resetWagmiState, wagmiState } from "./wagmiState.ts";
18
+
19
+ const TX = ("0x" + "ab".repeat(32)) as Hex;
20
+
21
+ const validVerifyArgs = {
22
+ from: ADDR,
23
+ token: ADDR,
24
+ to: ADDR,
25
+ value: 1n,
26
+ commitment: COMMIT,
27
+ };
28
+
29
+ describe("useVerifyTransfer", () => {
30
+ beforeEach(() => {
31
+ resetWagmiState();
32
+ resetSdkConfigAddress();
33
+ });
34
+
35
+ test("txHash / args が無いときはクエリが enabled にならず fetch しない", async () => {
36
+ wagmiState.publicClient = stubPublicClient();
37
+ const qc = createTestQueryClient();
38
+ const { result } = renderHook(
39
+ () => useVerifyTransfer(undefined, validVerifyArgs),
40
+ { wrapper: createQueryWrapper(qc) },
41
+ );
42
+ expect(result.current.fetchStatus).toBe("idle");
43
+ expect(result.current.status).toBe("pending");
44
+ });
45
+
46
+ test("sdk_js: verify の arktype 失敗は query error", async () => {
47
+ wagmiState.publicClient = stubPublicClient();
48
+ const qc = createTestQueryClient();
49
+ const { result } = renderHook(
50
+ () =>
51
+ useVerifyTransfer(TX, {
52
+ ...validVerifyArgs,
53
+ from: "0xbad" as Hex,
54
+ }),
55
+ { wrapper: createQueryWrapper(qc) },
56
+ );
57
+ await waitFor(() => expect(result.current.isError).toBe(true));
58
+ expect(result.current.error).toBeDefined();
59
+ });
60
+
61
+ test("sdk_js: config ゼロでは verify が失敗して query error", async () => {
62
+ setSdkConfigAddressZero();
63
+ wagmiState.publicClient = stubPublicClient({
64
+ getCode: async () => "0x",
65
+ }) as unknown as PublicClient;
66
+ const qc = createTestQueryClient();
67
+ const { result } = renderHook(
68
+ () => useVerifyTransfer(TX, validVerifyArgs),
69
+ { wrapper: createQueryWrapper(qc) },
70
+ );
71
+ await waitFor(() => expect(result.current.isError).toBe(true));
72
+ expect(String(result.current.error?.message)).toMatch(
73
+ /not deployed at/,
74
+ );
75
+ });
76
+
77
+ test("RPC: getTransactionReceipt が失敗したら query error に伝播", async () => {
78
+ const getTransactionReceipt = mock(() =>
79
+ Promise.reject(new Error("RPC: receipt timeout")),
80
+ );
81
+ wagmiState.publicClient = stubPublicClient({
82
+ getTransactionReceipt,
83
+ }) as unknown as PublicClient;
84
+
85
+ const qc = createTestQueryClient();
86
+ const { result } = renderHook(
87
+ () => useVerifyTransfer(TX, validVerifyArgs),
88
+ { wrapper: createQueryWrapper(qc) },
89
+ );
90
+ await waitFor(() => expect(result.current.isError).toBe(true));
91
+ expect(result.current.error?.message).toMatch(/RPC: receipt timeout/);
92
+ });
93
+
94
+ test("レシートに該当ログが無いとき sdk_js のメッセージが伝播", async () => {
95
+ const getTransactionReceipt = mock(() =>
96
+ Promise.resolve({
97
+ logs: [],
98
+ }),
99
+ ) as unknown as PublicClient["getTransactionReceipt"];
100
+ wagmiState.publicClient = stubPublicClient({
101
+ getTransactionReceipt,
102
+ }) as unknown as PublicClient;
103
+
104
+ const qc = createTestQueryClient();
105
+ const { result } = renderHook(
106
+ () => useVerifyTransfer(TX, validVerifyArgs),
107
+ { wrapper: createQueryWrapper(qc) },
108
+ );
109
+ await waitFor(() => expect(result.current.isError).toBe(true));
110
+ expect(result.current.error?.message).toMatch(
111
+ /TransferWithCommitmentSent event not found/,
112
+ );
113
+ });
114
+
115
+ test("例外的な chain id でも TWC デプロイ済みなら verify はレシートまで進む", async () => {
116
+ const getTransactionReceipt = mock(() =>
117
+ Promise.resolve({
118
+ logs: [],
119
+ }),
120
+ ) as unknown as PublicClient["getTransactionReceipt"];
121
+ wagmiState.publicClient = stubPublicClient({
122
+ chain: { id: 999_998, name: "CustomTest" } as unknown as Chain,
123
+ getTransactionReceipt,
124
+ }) as unknown as PublicClient;
125
+
126
+ const qc = createTestQueryClient();
127
+ const { result } = renderHook(
128
+ () => useVerifyTransfer(TX, validVerifyArgs),
129
+ { wrapper: createQueryWrapper(qc) },
130
+ );
131
+ await waitFor(() => expect(result.current.isError).toBe(true));
132
+ expect(result.current.error?.message).toMatch(
133
+ /TransferWithCommitmentSent event not found/,
134
+ );
135
+ });
136
+
137
+ test("成功時は TanStack Query 用に null を data として返す", async () => {
138
+ const getTransactionReceipt = mock(() =>
139
+ Promise.resolve({
140
+ logs: [
141
+ {
142
+ address: transferWithCommitmentAddress,
143
+ topics: encodeEventTopics({
144
+ abi: transferWithCommitmentAbi,
145
+ eventName: "TransferWithCommitmentSent",
146
+ args: {
147
+ from: validVerifyArgs.from,
148
+ to: validVerifyArgs.to,
149
+ token: validVerifyArgs.token,
150
+ },
151
+ }),
152
+ data: encodeAbiParameters(
153
+ [{ type: "uint256" }, { type: "bytes32" }],
154
+ [validVerifyArgs.value, validVerifyArgs.commitment],
155
+ ),
156
+ },
157
+ ],
158
+ }),
159
+ ) as unknown as PublicClient["getTransactionReceipt"];
160
+ wagmiState.publicClient = stubPublicClient({
161
+ getTransactionReceipt,
162
+ }) as unknown as PublicClient;
163
+
164
+ const qc = createTestQueryClient();
165
+ const { result } = renderHook(
166
+ () => useVerifyTransfer(TX, validVerifyArgs),
167
+ { wrapper: createQueryWrapper(qc) },
168
+ );
169
+ await waitFor(() => expect(result.current.isSuccess).toBe(true));
170
+ expect(result.current.data).toBeNull();
171
+ });
172
+ });
173
+
174
+ describe("useTransferWithCommitmentSentLogs", () => {
175
+ beforeEach(() => {
176
+ resetWagmiState();
177
+ resetSdkConfigAddress();
178
+ });
179
+
180
+ test("config ゼロでは assert が失敗", async () => {
181
+ setSdkConfigAddressZero();
182
+ wagmiState.publicClient = stubPublicClient({
183
+ getCode: async () => "0x",
184
+ }) as unknown as PublicClient;
185
+ const qc = createTestQueryClient();
186
+ const { result } = renderHook(() => useTransferWithCommitmentSentLogs(TX), {
187
+ wrapper: createQueryWrapper(qc),
188
+ });
189
+ await waitFor(() => expect(result.current.isError).toBe(true));
190
+ expect(String(result.current.error?.message)).toMatch(
191
+ /not deployed at/,
192
+ );
193
+ });
194
+
195
+ test("RPC 失敗を query error に伝播", async () => {
196
+ const getTransactionReceipt = mock(() =>
197
+ Promise.reject(new Error("RPC: not found")),
198
+ );
199
+ wagmiState.publicClient = stubPublicClient({
200
+ getTransactionReceipt,
201
+ }) as unknown as PublicClient;
202
+
203
+ const qc = createTestQueryClient();
204
+ const { result } = renderHook(() => useTransferWithCommitmentSentLogs(TX), {
205
+ wrapper: createQueryWrapper(qc),
206
+ });
207
+ await waitFor(() => expect(result.current.isError).toBe(true));
208
+ expect(result.current.error?.message).toMatch(/RPC: not found/);
209
+ });
210
+
211
+ test("成功時はログ配列が data(空でも例外にしない)", async () => {
212
+ const getTransactionReceipt = mock(() =>
213
+ Promise.resolve({
214
+ logs: [],
215
+ }),
216
+ ) as unknown as PublicClient["getTransactionReceipt"];
217
+ wagmiState.publicClient = stubPublicClient({
218
+ chain: mainnet,
219
+ getTransactionReceipt,
220
+ }) as unknown as PublicClient;
221
+
222
+ const qc = createTestQueryClient();
223
+ const { result } = renderHook(() => useTransferWithCommitmentSentLogs(TX), {
224
+ wrapper: createQueryWrapper(qc),
225
+ });
226
+ await waitFor(() => expect(result.current.isSuccess).toBe(true));
227
+ expect(Array.isArray(result.current.data)).toBe(true);
228
+ expect(result.current.data).toEqual([]);
229
+ });
230
+ });