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,81 @@
1
+ import { mock } from "bun:test";
2
+ import { fileURLToPath } from "node:url";
3
+ import { Window } from "happy-dom";
4
+
5
+ import { buildMockSdkConfigModule } from "./sdkConfigState.ts";
6
+ import { wagmiState } from "./wagmiState.ts";
7
+
8
+ const sdkJsRoot = fileURLToPath(new URL("../../sdk_js/", import.meta.url));
9
+ const sdkJsConfigPath = `${sdkJsRoot}config.ts`;
10
+
11
+ /**
12
+ * tsup の dist は各バンドルに config をインライン化するため、`dist/config.js` を mock しても verify/sign 等はゼロアドレスのまま。
13
+ * テストではソースモジュールを読み、`config.ts` だけをモックして切り替える。
14
+ */
15
+ mock.module(sdkJsConfigPath, () => buildMockSdkConfigModule());
16
+
17
+ const verifyModule = await import("../../sdk_js/verify.ts");
18
+
19
+ const sigSingleSrc = await import(
20
+ "../../sdk_js/signatureTransfer/single/index.ts"
21
+ );
22
+ const sigBatchSrc = await import(
23
+ "../../sdk_js/signatureTransfer/batch/index.ts"
24
+ );
25
+ const sigUnifiedSrc = await import(
26
+ "../../sdk_js/signatureTransfer/unified/index.ts"
27
+ );
28
+ const sigCancelSrc = await import(
29
+ "../../sdk_js/signatureTransfer/cancelAuthorization/index.ts"
30
+ );
31
+
32
+ const selfSingleSrc = await import("../../sdk_js/selfTransfer/single/index.ts");
33
+ const selfBatchSrc = await import("../../sdk_js/selfTransfer/batch/index.ts");
34
+ const selfUnifiedSrc = await import(
35
+ "../../sdk_js/selfTransfer/unified/index.ts"
36
+ );
37
+
38
+ mock.module("eth-twc-sdk-js/verify", () => verifyModule);
39
+
40
+ mock.module("eth-twc-sdk-js/signatureTransfer/single", () => sigSingleSrc);
41
+ mock.module("eth-twc-sdk-js/signatureTransfer/batch", () => sigBatchSrc);
42
+ mock.module("eth-twc-sdk-js/signatureTransfer/unified", () => sigUnifiedSrc);
43
+ mock.module(
44
+ "eth-twc-sdk-js/signatureTransfer/cancelAuthorization",
45
+ () => sigCancelSrc,
46
+ );
47
+
48
+ mock.module("eth-twc-sdk-js/selfTransfer/single", () => selfSingleSrc);
49
+ mock.module("eth-twc-sdk-js/selfTransfer/batch", () => selfBatchSrc);
50
+ mock.module("eth-twc-sdk-js/selfTransfer/unified", () => selfUnifiedSrc);
51
+
52
+ mock.module("eth-twc-sdk-js/config", () => buildMockSdkConfigModule());
53
+
54
+ const window = new Window({ url: "http://localhost/" });
55
+ (globalThis as unknown as { window: Window }).window = window;
56
+ (globalThis as unknown as { document: Document }).document = window.document;
57
+ (globalThis as unknown as { navigator: Navigator }).navigator =
58
+ window.navigator as unknown as Navigator;
59
+ (globalThis as Record<string, unknown>).HTMLElement = window.HTMLElement;
60
+
61
+ (window as unknown as { SyntaxError?: typeof Error }).SyntaxError ??= Error;
62
+ (globalThis as unknown as { SyntaxError?: typeof Error }).SyntaxError ??= Error;
63
+
64
+ mock.module("wagmi", () => ({
65
+ useConfig: () => ({}),
66
+ useChainId: () => wagmiState.publicClient?.chain?.id ?? 1,
67
+ usePublicClient: () => wagmiState.publicClient,
68
+ useWalletClient: () => ({
69
+ data: wagmiState.walletClient,
70
+ error: null,
71
+ isError: false,
72
+ isPending: false,
73
+ status: wagmiState.walletClient ? "success" : "pending",
74
+ }),
75
+ useConnection: () => ({
76
+ address: wagmiState.address,
77
+ chainId: wagmiState.publicClient?.chain?.id,
78
+ status: wagmiState.address ? "connected" : "disconnected",
79
+ connector: undefined,
80
+ }),
81
+ }));
@@ -0,0 +1,17 @@
1
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
2
+ import type { ReactNode } from "react";
3
+
4
+ export function createTestQueryClient(): QueryClient {
5
+ return new QueryClient({
6
+ defaultOptions: {
7
+ queries: { retry: false },
8
+ mutations: { retry: false },
9
+ },
10
+ });
11
+ }
12
+
13
+ export function createQueryWrapper(qc: QueryClient) {
14
+ return function QueryWrapper({ children }: { children: ReactNode }) {
15
+ return <QueryClientProvider client={qc}>{children}</QueryClientProvider>;
16
+ };
17
+ }
@@ -0,0 +1,33 @@
1
+ import { TEST_VERIFIER_CONTRACT } from "./fixtures.ts";
2
+ import {
3
+ EIP712_DOMAIN_NAME,
4
+ EIP712_DOMAIN_VERSION,
5
+ TRANSFER_WITH_COMMITMENT_CREATE2_SALT,
6
+ } from "../../sdk_js/twcConstants.ts";
7
+
8
+ const ZERO = "0x0000000000000000000000000000000000000000" as const;
9
+
10
+ /** preload の `eth-twc-sdk-js/config` モックと共有。テストで上書き可能。 */
11
+ export const sdkConfigState = {
12
+ transferWithCommitmentAddress: TEST_VERIFIER_CONTRACT as `0x${string}`,
13
+ };
14
+
15
+ export function resetSdkConfigAddress(): void {
16
+ sdkConfigState.transferWithCommitmentAddress =
17
+ TEST_VERIFIER_CONTRACT as `0x${string}`;
18
+ }
19
+
20
+ export function setSdkConfigAddressZero(): void {
21
+ sdkConfigState.transferWithCommitmentAddress = ZERO;
22
+ }
23
+
24
+ export function buildMockSdkConfigModule() {
25
+ return {
26
+ EIP712_DOMAIN_NAME,
27
+ EIP712_DOMAIN_VERSION,
28
+ TRANSFER_WITH_COMMITMENT_CREATE2_SALT,
29
+ get transferWithCommitmentAddress() {
30
+ return sdkConfigState.transferWithCommitmentAddress;
31
+ },
32
+ };
33
+ }
package/test/stubs.ts ADDED
@@ -0,0 +1,257 @@
1
+ import { mock } from "bun:test";
2
+ import type { SignedBatchTransfer } from "eth-twc-sdk-js/signatureTransfer/batch";
3
+ import type { SignedCancelAuthorization } from "eth-twc-sdk-js/signatureTransfer/cancelAuthorization";
4
+ import type { SignedSingleTransfer } from "eth-twc-sdk-js/signatureTransfer/single";
5
+ import type { SignedUnifiedTransfer } from "eth-twc-sdk-js/signatureTransfer/unified";
6
+ import { UINT256_MAX } from "eth-twc-sdk-js/types/utils";
7
+ import { mainnet } from "viem/chains";
8
+ import type { Address, Hex, PublicClient, WalletClient } from "viem";
9
+
10
+ const ZERO_ADDR =
11
+ "0x0000000000000000000000000000000000000000" as Address;
12
+
13
+ import {
14
+ ADDR,
15
+ ADDR_B,
16
+ COMMIT,
17
+ SALT_ZERO,
18
+ TEST_VERIFIER_CONTRACT,
19
+ testDomain,
20
+ } from "./fixtures.ts";
21
+
22
+ const TX_HASH = ("0x" + "aa".repeat(32)) as Hex;
23
+
24
+ /** PublicClient スタブ(chain は mainnet、既定で TWC がデプロイ済み扱いの getCode)。 */
25
+ export function stubPublicClient(
26
+ overrides: Partial<PublicClient> = {},
27
+ ): PublicClient {
28
+ const { getCode: overrideGetCode, ...rest } = overrides;
29
+ const defaultGetCode: PublicClient["getCode"] = async (args) => {
30
+ const a = args.address;
31
+ if (!a || a.toLowerCase() === ZERO_ADDR.toLowerCase()) {
32
+ return "0x";
33
+ }
34
+ return "0x6000";
35
+ };
36
+ const getCode = overrideGetCode ?? defaultGetCode;
37
+ return {
38
+ chain: mainnet,
39
+ getCode,
40
+ ...rest,
41
+ } as PublicClient;
42
+ }
43
+
44
+ export function stubWalletClient(
45
+ overrides: Partial<WalletClient> = {},
46
+ ): WalletClient {
47
+ return {
48
+ chain: mainnet,
49
+ ...overrides,
50
+ } as WalletClient;
51
+ }
52
+
53
+ /** Self-Call: simulate → write が成功する最小モック。 */
54
+ export function stubClientsSelfTransferSuccess(): {
55
+ publicClient: PublicClient;
56
+ walletClient: WalletClient;
57
+ } {
58
+ const simulateContract = mock(() =>
59
+ Promise.resolve({
60
+ result: undefined,
61
+ request: {
62
+ address: TEST_VERIFIER_CONTRACT,
63
+ abi: [],
64
+ functionName: "transfer",
65
+ args: [],
66
+ },
67
+ }),
68
+ ) as unknown as PublicClient["simulateContract"];
69
+ const writeContract = mock(() => Promise.resolve(TX_HASH));
70
+ return {
71
+ publicClient: stubPublicClient({ simulateContract }),
72
+ walletClient: stubWalletClient({ writeContract }),
73
+ };
74
+ }
75
+
76
+ /** Self-Call: simulate が RPC エラー。 */
77
+ export function stubClientsSelfTransferSimulateRpcError(): {
78
+ publicClient: PublicClient;
79
+ walletClient: WalletClient;
80
+ } {
81
+ const simulateContract = mock(() =>
82
+ Promise.reject(new Error("RPC: eth_call failed")),
83
+ ) as unknown as PublicClient["simulateContract"];
84
+ const writeContract = mock(() => Promise.reject(new Error("should not write")));
85
+ return {
86
+ publicClient: stubPublicClient({ simulateContract }),
87
+ walletClient: stubWalletClient({ writeContract }),
88
+ };
89
+ }
90
+
91
+ /** 署名: getEip712Domain + signTypedData が成功。 */
92
+ export function stubClientsSignSuccess(): {
93
+ publicClient: PublicClient;
94
+ walletClient: WalletClient;
95
+ sig: Hex;
96
+ } {
97
+ const domain = testDomain();
98
+ const sig = ("0x" + "bb".repeat(65)) as Hex;
99
+ const getEip712Domain = mock(() =>
100
+ Promise.resolve({
101
+ domain,
102
+ fields: "0x0f" as Hex,
103
+ extensions: [] as const,
104
+ }),
105
+ );
106
+ const signTypedData = mock(() => Promise.resolve(sig));
107
+ return {
108
+ publicClient: stubPublicClient({ getEip712Domain }),
109
+ walletClient: stubWalletClient({ signTypedData }),
110
+ sig,
111
+ };
112
+ }
113
+
114
+ export function stubClientsSignTypedDataRejects(): {
115
+ publicClient: PublicClient;
116
+ walletClient: WalletClient;
117
+ } {
118
+ const domain = testDomain();
119
+ const getEip712Domain = mock(() =>
120
+ Promise.resolve({
121
+ domain,
122
+ fields: "0x0f" as Hex,
123
+ extensions: [] as const,
124
+ }),
125
+ );
126
+ const signTypedData = mock(() =>
127
+ Promise.reject(new Error("User rejected the request.")),
128
+ );
129
+ return {
130
+ publicClient: stubPublicClient({ getEip712Domain }),
131
+ walletClient: stubWalletClient({ signTypedData }),
132
+ };
133
+ }
134
+
135
+ export function stubClientsGetEip712DomainRejects(): {
136
+ publicClient: PublicClient;
137
+ walletClient: WalletClient;
138
+ } {
139
+ const getEip712Domain = mock(() =>
140
+ Promise.reject(new Error("RPC: getEip712Domain failed")),
141
+ );
142
+ return {
143
+ publicClient: stubPublicClient({ getEip712Domain }),
144
+ walletClient: stubWalletClient({}),
145
+ };
146
+ }
147
+
148
+ function domainWithoutZeroSalt(
149
+ d: ReturnType<typeof testDomain>,
150
+ ): SignedSingleTransfer["domain"] {
151
+ if (
152
+ d.salt !== undefined &&
153
+ typeof d.salt === "string" &&
154
+ d.salt.toLowerCase() === SALT_ZERO.toLowerCase()
155
+ ) {
156
+ const { salt: _s, ...rest } = d;
157
+ return rest as SignedSingleTransfer["domain"];
158
+ }
159
+ return d as SignedSingleTransfer["domain"];
160
+ }
161
+
162
+ /** Executor 送信: 署名済みバンドル + simulate / write 成功。 */
163
+ export function minimalSignedSingleTransfer(): SignedSingleTransfer {
164
+ const raw = testDomain();
165
+ return {
166
+ domain: domainWithoutZeroSalt(raw),
167
+ from: ADDR,
168
+ to: ADDR_B,
169
+ token: ADDR,
170
+ value: 1n,
171
+ validAfter: 0n,
172
+ validBefore: UINT256_MAX,
173
+ commitment: COMMIT,
174
+ signature: ("0x" + "cc".repeat(65)) as Hex,
175
+ };
176
+ }
177
+
178
+ export function minimalSignedUni(): SignedUnifiedTransfer {
179
+ const raw = testDomain();
180
+ return {
181
+ domain: domainWithoutZeroSalt(raw),
182
+ from: ADDR,
183
+ details: [{ to: ADDR_B, token: ADDR, value: 1n }],
184
+ validAfter: 0n,
185
+ validBefore: UINT256_MAX,
186
+ commitment: COMMIT,
187
+ signature: ("0x" + "cc".repeat(65)) as Hex,
188
+ };
189
+ }
190
+
191
+ /** バッチ EIP-712 の `batchCommitment`(明細行の `commitment` と別ワードにする)。 */
192
+ const BATCH_COMMIT = ("0x" +
193
+ "bb".repeat(32)) as Hex;
194
+
195
+ export function minimalSignedBatch(): SignedBatchTransfer {
196
+ const raw = testDomain();
197
+ return {
198
+ domain: domainWithoutZeroSalt(raw),
199
+ from: ADDR,
200
+ details: [
201
+ { to: ADDR_B, token: ADDR, value: 1n, commitment: COMMIT },
202
+ ],
203
+ validAfter: 0n,
204
+ validBefore: UINT256_MAX,
205
+ batchCommitment: BATCH_COMMIT,
206
+ signature: ("0x" + "cc".repeat(65)) as Hex,
207
+ };
208
+ }
209
+
210
+ export function minimalSignedCancel(): SignedCancelAuthorization {
211
+ const raw = testDomain();
212
+ return {
213
+ domain: domainWithoutZeroSalt(raw),
214
+ authorizer: ADDR,
215
+ commitment: COMMIT,
216
+ signature: ("0x" + "cc".repeat(65)) as Hex,
217
+ };
218
+ }
219
+
220
+ export function stubClientsSignatureSendSuccess(): {
221
+ publicClient: PublicClient;
222
+ walletClient: WalletClient;
223
+ } {
224
+ const simulateContract = mock(() =>
225
+ Promise.resolve({
226
+ result: undefined,
227
+ request: {
228
+ address: TEST_VERIFIER_CONTRACT,
229
+ abi: [],
230
+ functionName: "transferWithAuthorization",
231
+ args: [],
232
+ },
233
+ }),
234
+ ) as unknown as PublicClient["simulateContract"];
235
+ const writeContract = mock(() => Promise.resolve(TX_HASH));
236
+ return {
237
+ publicClient: stubPublicClient({ simulateContract }),
238
+ walletClient: stubWalletClient({ writeContract }),
239
+ };
240
+ }
241
+
242
+ export function stubClientsSignatureSendSimulateFails(): {
243
+ publicClient: PublicClient;
244
+ walletClient: WalletClient;
245
+ } {
246
+ const simulateContract = mock(() =>
247
+ Promise.reject(new Error("RPC: execution reverted")),
248
+ ) as unknown as PublicClient["simulateContract"];
249
+ return {
250
+ publicClient: stubPublicClient({ simulateContract }),
251
+ walletClient: stubWalletClient({
252
+ writeContract: mock(() =>
253
+ Promise.reject(new Error("should not write")),
254
+ ),
255
+ }),
256
+ };
257
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "types": ["bun"]
5
+ },
6
+ "include": ["./**/*.ts", "./**/*.tsx"],
7
+ "exclude": []
8
+ }
@@ -0,0 +1,18 @@
1
+ import type { Hex, PublicClient, WalletClient } from "viem";
2
+
3
+ /** preload の `wagmi` モックが参照する接続状態。 */
4
+ export const wagmiState: {
5
+ publicClient: PublicClient | undefined;
6
+ walletClient: WalletClient | undefined;
7
+ address: Hex | undefined;
8
+ } = {
9
+ publicClient: undefined,
10
+ walletClient: undefined,
11
+ address: undefined,
12
+ };
13
+
14
+ export function resetWagmiState(): void {
15
+ wagmiState.publicClient = undefined;
16
+ wagmiState.walletClient = undefined;
17
+ wagmiState.address = undefined;
18
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "include": ["index.ts", "hooks/**/*.ts"],
3
+ "exclude": ["test"],
4
+ "compilerOptions": {
5
+ // Environment setup & latest features
6
+ "lib": ["ESNext"],
7
+ "target": "ESNext",
8
+ "module": "Preserve",
9
+ "moduleDetection": "force",
10
+ "jsx": "react-jsx",
11
+ "allowJs": true,
12
+
13
+ // Bundler mode
14
+ "moduleResolution": "bundler",
15
+ "allowImportingTsExtensions": true,
16
+ "verbatimModuleSyntax": true,
17
+ "noEmit": true,
18
+
19
+ // Best practices
20
+ "strict": true,
21
+ "skipLibCheck": true,
22
+ "noFallthroughCasesInSwitch": true,
23
+ "noUncheckedIndexedAccess": true,
24
+ "noImplicitOverride": true,
25
+
26
+ // Some stricter flags (disabled by default)
27
+ "noUnusedLocals": false,
28
+ "noUnusedParameters": false,
29
+ "noPropertyAccessFromIndexSignature": false
30
+ }
31
+ }