@tomo-inc/wallet-adaptor-base 0.0.19 → 0.0.21
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/dist/index.cjs +18 -22
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +18 -22
- package/package.json +7 -2
- package/project.json +1 -1
- package/src/__tests__/WalletConnectProvider.test.ts +535 -0
- package/src/__tests__/WalletConnectSolanaProvider.test.ts +537 -0
- package/src/__tests__/browsers.test.ts +263 -0
- package/src/__tests__/chainId.test.ts +66 -0
- package/src/__tests__/chains-utils.test.ts +49 -0
- package/src/__tests__/defaultConnectors.test.ts +8 -5
- package/src/__tests__/detector.test.ts +402 -0
- package/src/__tests__/index.test.ts +275 -0
- package/src/__tests__/isMobile.test.ts +187 -0
- package/src/__tests__/log.test.ts +40 -0
- package/src/__tests__/utils.test.ts +66 -0
- package/src/__tests__/wallet-api.test.ts +1755 -0
- package/src/__tests__/wallet-config.test.ts +75 -0
- package/src/__tests__/wallet-eip6963.test.ts +377 -0
- package/src/__tests__/wallet-standard.test.ts +41 -3
- package/src/__tests__/wallet-walletconnect.test.ts +370 -0
- package/src/__tests__/wallets/index.test.ts +42 -0
- package/src/type.ts +4 -0
- package/src/utils/chainId.ts +6 -5
- package/src/utils/wallet-config.ts +2 -9
- package/src/wallet-api/connect.ts +4 -2
- package/src/wallets/detector.ts +3 -4
- package/src/wallets/providers/WalletConnectProvider.ts +5 -4
- package/src/wallets/providers/WalletConnectSolanaProvider.ts +1 -1
- package/src/wallets/wallet-eip6963.ts +1 -1
- package/vitest.config.ts +20 -0
|
@@ -0,0 +1,1755 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { connect, disconnect, connectMobile } from "../wallet-api/connect";
|
|
3
|
+
import { getBalance } from "../wallet-api/balance";
|
|
4
|
+
import { switchChain, addChain } from "../wallet-api/chain";
|
|
5
|
+
import { signInWithWallet } from "../wallet-api/sign-in";
|
|
6
|
+
import { signMessage } from "../wallet-api/sign-message";
|
|
7
|
+
import { WalletOptions, ConnectParams, WalletInfo } from "../type";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
// Mock Solana Connection
|
|
11
|
+
vi.mock("@solana/web3.js", () => {
|
|
12
|
+
const mockGetMultipleAccountsInfo = vi.fn().mockResolvedValue([
|
|
13
|
+
{
|
|
14
|
+
lamports: 1000000,
|
|
15
|
+
},
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
// Mock PublicKey to accept any string and return it
|
|
19
|
+
class MockPublicKey {
|
|
20
|
+
constructor(public address: string) {}
|
|
21
|
+
toString() {
|
|
22
|
+
return this.address;
|
|
23
|
+
}
|
|
24
|
+
toBase58() {
|
|
25
|
+
return this.address;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Mock Connection as a class constructor
|
|
30
|
+
class MockConnection {
|
|
31
|
+
constructor(public rpcUrl: string) {}
|
|
32
|
+
getMultipleAccountsInfo = mockGetMultipleAccountsInfo;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
Connection: MockConnection,
|
|
37
|
+
PublicKey: MockPublicKey,
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const mockGetAccountResource = vi.hoisted(() =>
|
|
42
|
+
vi.fn().mockResolvedValue({ coin: { value: "1000000" } }),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const mockCreateWalletConnectEVMProvider = vi.hoisted(() => vi.fn());
|
|
46
|
+
const mockOpenUri = vi.hoisted(() => vi.fn());
|
|
47
|
+
const mockIsAndroid = vi.hoisted(() => vi.fn(() => false));
|
|
48
|
+
const mockIsIOS = vi.hoisted(() => vi.fn(() => false));
|
|
49
|
+
const mockIsMobile = vi.hoisted(() => vi.fn(() => false));
|
|
50
|
+
vi.mock("../wallets/wallet-walletconnect", () => ({
|
|
51
|
+
createWalletConnectEVMProvider: mockCreateWalletConnectEVMProvider,
|
|
52
|
+
}));
|
|
53
|
+
vi.mock("utils/browsers", () => ({ openUri: mockOpenUri }));
|
|
54
|
+
vi.mock("../utils/isMobile", () => ({
|
|
55
|
+
isMobile: mockIsMobile,
|
|
56
|
+
isAndroid: mockIsAndroid,
|
|
57
|
+
isIOS: mockIsIOS,
|
|
58
|
+
}));
|
|
59
|
+
vi.mock("utils/isMobile", () => ({
|
|
60
|
+
isMobile: mockIsMobile,
|
|
61
|
+
isAndroid: mockIsAndroid,
|
|
62
|
+
isIOS: mockIsIOS,
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
// Mock Aptos SDK
|
|
66
|
+
vi.mock("@aptos-labs/ts-sdk", () => {
|
|
67
|
+
// Mock AptosConfig as a class constructor
|
|
68
|
+
class MockAptosConfig {
|
|
69
|
+
constructor(public config: any) {}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Mock Aptos as a class constructor
|
|
73
|
+
class MockAptos {
|
|
74
|
+
getAccountResource = mockGetAccountResource;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
Aptos: MockAptos,
|
|
79
|
+
AptosConfig: MockAptosConfig,
|
|
80
|
+
Network: {
|
|
81
|
+
MAINNET: "mainnet",
|
|
82
|
+
TESTNET: "testnet",
|
|
83
|
+
DEVNET: "devnet",
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("Wallet API", () => {
|
|
89
|
+
beforeEach(() => {
|
|
90
|
+
vi.clearAllMocks();
|
|
91
|
+
// Mock window object
|
|
92
|
+
global.window = {
|
|
93
|
+
...global.window,
|
|
94
|
+
ethereum: undefined,
|
|
95
|
+
} as any;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
afterEach(() => {
|
|
99
|
+
vi.restoreAllMocks();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe("connect", () => {
|
|
103
|
+
it("should throw error when provider is not provided", async () => {
|
|
104
|
+
const walletOptions: Omit<WalletOptions, "account"> = {
|
|
105
|
+
provider: undefined,
|
|
106
|
+
chainType: "evm",
|
|
107
|
+
walletInfo: {} as WalletInfo,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const connectParams: ConnectParams = {};
|
|
111
|
+
|
|
112
|
+
await expect(connect(connectParams, walletOptions)).rejects.toThrow("Provider is required");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should call connectMobile when isMobile and no provider and uuid not walletConnect", async () => {
|
|
116
|
+
mockIsMobile.mockReturnValueOnce(true);
|
|
117
|
+
const mockMobileProvider = { request: vi.fn().mockResolvedValue("0x1") };
|
|
118
|
+
mockCreateWalletConnectEVMProvider.mockReturnValue({
|
|
119
|
+
getProvider: vi.fn().mockResolvedValue(mockMobileProvider),
|
|
120
|
+
connectUri: "wc://x",
|
|
121
|
+
connect: vi.fn().mockResolvedValue(["0xmobile-from-connect"]),
|
|
122
|
+
});
|
|
123
|
+
const getUri = vi.fn().mockResolvedValue("phantom://wc");
|
|
124
|
+
const result = await connect(
|
|
125
|
+
{ useWalletConnect: true, dappLink: "https://app.com" },
|
|
126
|
+
{
|
|
127
|
+
chainType: "evm",
|
|
128
|
+
walletInfo: { uuid: "phantom", mobile: { getUri }, links: {} } as WalletInfo,
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
expect(result.address).toBe("0xmobile-from-connect");
|
|
132
|
+
expect(result.chainId).toBe("0x1");
|
|
133
|
+
expect(result.provider).toBe(mockMobileProvider);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("should throw error when chainType is not provided", async () => {
|
|
137
|
+
const mockProvider = {
|
|
138
|
+
request: vi.fn(),
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const walletOptions: Omit<WalletOptions, "account"> = {
|
|
142
|
+
provider: mockProvider as any,
|
|
143
|
+
chainType: undefined,
|
|
144
|
+
walletInfo: {} as WalletInfo,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const connectParams: ConnectParams = {};
|
|
148
|
+
|
|
149
|
+
await expect(connect(connectParams, walletOptions)).rejects.toThrow("Chain type is required");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("should use getProvider when provider has no request (wagmi-style)", async () => {
|
|
153
|
+
const innerProvider = {
|
|
154
|
+
request: vi.fn().mockImplementation((params: any) => {
|
|
155
|
+
if (params.method === "eth_requestAccounts") return Promise.resolve(["0xwagmi-address"]);
|
|
156
|
+
if (params.method === "eth_chainId") return Promise.resolve("0x1");
|
|
157
|
+
return Promise.resolve(null);
|
|
158
|
+
}),
|
|
159
|
+
};
|
|
160
|
+
const getProviderFn = vi.fn().mockResolvedValue(innerProvider);
|
|
161
|
+
const mockProvider = { getProvider: getProviderFn };
|
|
162
|
+
const walletOptions: Omit<WalletOptions, "account"> = {
|
|
163
|
+
provider: mockProvider as any,
|
|
164
|
+
chainType: "evm",
|
|
165
|
+
walletInfo: {} as WalletInfo,
|
|
166
|
+
};
|
|
167
|
+
await expect(connect({}, walletOptions)).rejects.toThrow();
|
|
168
|
+
expect(getProviderFn).toHaveBeenCalled();
|
|
169
|
+
expect(innerProvider.request).toHaveBeenCalledWith({ method: "eth_requestAccounts" });
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("should connect EVM wallet", async () => {
|
|
173
|
+
const mockProvider = {
|
|
174
|
+
request: vi.fn().mockImplementation((params: any) => {
|
|
175
|
+
if (params.method === "eth_requestAccounts") {
|
|
176
|
+
return Promise.resolve(["0x1234567890123456789012345678901234567890"]);
|
|
177
|
+
}
|
|
178
|
+
if (params.method === "eth_chainId") {
|
|
179
|
+
return Promise.resolve("0x1");
|
|
180
|
+
}
|
|
181
|
+
return Promise.resolve(null);
|
|
182
|
+
}),
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const walletOptions: Omit<WalletOptions, "account"> = {
|
|
186
|
+
provider: mockProvider as any,
|
|
187
|
+
chainType: "evm",
|
|
188
|
+
walletInfo: {} as WalletInfo,
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const connectParams: ConnectParams = {};
|
|
192
|
+
|
|
193
|
+
const result = await connect(connectParams, walletOptions);
|
|
194
|
+
|
|
195
|
+
expect(result.address).toBe("0x1234567890123456789012345678901234567890");
|
|
196
|
+
expect(result.chainId).toBe("0x1");
|
|
197
|
+
expect(mockProvider.request).toHaveBeenCalledWith({ method: "eth_requestAccounts" });
|
|
198
|
+
expect(mockProvider.request).toHaveBeenCalledWith({ method: "eth_chainId" });
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("should connect Solana wallet with connect method", async () => {
|
|
202
|
+
const mockProvider = {
|
|
203
|
+
connect: vi.fn().mockResolvedValue({
|
|
204
|
+
publicKey: {
|
|
205
|
+
toString: () => "SolanaPublicKey123",
|
|
206
|
+
},
|
|
207
|
+
}),
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const walletOptions: Omit<WalletOptions, "account"> = {
|
|
211
|
+
provider: mockProvider as any,
|
|
212
|
+
chainType: "solana",
|
|
213
|
+
walletInfo: {} as WalletInfo,
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const connectParams: ConnectParams = {};
|
|
217
|
+
|
|
218
|
+
const result = await connect(connectParams, walletOptions);
|
|
219
|
+
|
|
220
|
+
expect(result.address).toBe("SolanaPublicKey123");
|
|
221
|
+
expect(result.network).toBe("mainnet-beta");
|
|
222
|
+
expect(mockProvider.connect).toHaveBeenCalled();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("should connect Solana wallet with request method", async () => {
|
|
226
|
+
const mockProvider = {
|
|
227
|
+
request: vi.fn().mockResolvedValue({
|
|
228
|
+
publicKey: {
|
|
229
|
+
toString: () => "SolanaPublicKey456",
|
|
230
|
+
},
|
|
231
|
+
}),
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const walletOptions: Omit<WalletOptions, "account"> = {
|
|
235
|
+
provider: mockProvider as any,
|
|
236
|
+
chainType: "solana",
|
|
237
|
+
walletInfo: {} as WalletInfo,
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const connectParams: ConnectParams = {};
|
|
241
|
+
|
|
242
|
+
const result = await connect(connectParams, walletOptions);
|
|
243
|
+
|
|
244
|
+
expect(result.address).toBe("SolanaPublicKey456");
|
|
245
|
+
expect(result.network).toBe("mainnet-beta");
|
|
246
|
+
expect(mockProvider.request).toHaveBeenCalledWith({ method: "connect" });
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("should connect Solana wallet with standard:connect", async () => {
|
|
250
|
+
const mockProvider = {
|
|
251
|
+
"standard:connect": {
|
|
252
|
+
connect: vi.fn().mockResolvedValue({
|
|
253
|
+
accounts: [
|
|
254
|
+
{
|
|
255
|
+
address: "SolanaStandardAddress",
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
}),
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const walletOptions: Omit<WalletOptions, "account"> = {
|
|
263
|
+
provider: mockProvider as any,
|
|
264
|
+
chainType: "solana",
|
|
265
|
+
walletInfo: {} as WalletInfo,
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const connectParams: ConnectParams = {};
|
|
269
|
+
|
|
270
|
+
const result = await connect(connectParams, walletOptions);
|
|
271
|
+
|
|
272
|
+
expect(result.address).toBe("SolanaStandardAddress");
|
|
273
|
+
expect(result.network).toBe("mainnet-beta");
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("should connect Aptos wallet with connect method", async () => {
|
|
277
|
+
const mockProvider = {
|
|
278
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
279
|
+
account: vi.fn().mockResolvedValue({
|
|
280
|
+
address: "0xaptos123",
|
|
281
|
+
}),
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const walletOptions: Omit<WalletOptions, "account"> = {
|
|
285
|
+
provider: mockProvider as any,
|
|
286
|
+
chainType: "aptos",
|
|
287
|
+
walletInfo: {} as WalletInfo,
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const connectParams: ConnectParams = {};
|
|
291
|
+
|
|
292
|
+
const result = await connect(connectParams, walletOptions);
|
|
293
|
+
|
|
294
|
+
expect(result.address).toBe("0xaptos123");
|
|
295
|
+
expect(result.network).toBe("mainnet");
|
|
296
|
+
expect(mockProvider.connect).toHaveBeenCalled();
|
|
297
|
+
expect(mockProvider.account).toHaveBeenCalled();
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("should connect Aptos wallet with request method", async () => {
|
|
301
|
+
const mockProvider = {
|
|
302
|
+
request: vi.fn().mockResolvedValue(undefined),
|
|
303
|
+
account: vi.fn().mockResolvedValue({ address: "0xaptos-req" }),
|
|
304
|
+
};
|
|
305
|
+
const walletOptions: Omit<WalletOptions, "account"> = {
|
|
306
|
+
provider: mockProvider as any,
|
|
307
|
+
chainType: "aptos",
|
|
308
|
+
walletInfo: {} as WalletInfo,
|
|
309
|
+
};
|
|
310
|
+
const result = await connect({}, walletOptions);
|
|
311
|
+
expect(result.address).toBe("0xaptos-req");
|
|
312
|
+
expect(mockProvider.request).toHaveBeenCalledWith({ method: "connect" });
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("should connect Aptos wallet with aptos:connect", async () => {
|
|
316
|
+
const mockProvider = {
|
|
317
|
+
"aptos:connect": {
|
|
318
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
319
|
+
},
|
|
320
|
+
"aptos:account": {
|
|
321
|
+
account: vi.fn().mockResolvedValue({
|
|
322
|
+
address: {
|
|
323
|
+
data: new Uint8Array([
|
|
324
|
+
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
|
|
325
|
+
29, 30, 31, 32,
|
|
326
|
+
]),
|
|
327
|
+
},
|
|
328
|
+
}),
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const walletOptions: Omit<WalletOptions, "account"> = {
|
|
333
|
+
provider: mockProvider as any,
|
|
334
|
+
chainType: "aptos",
|
|
335
|
+
walletInfo: {} as WalletInfo,
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const connectParams: ConnectParams = {};
|
|
339
|
+
|
|
340
|
+
const result = await connect(connectParams, walletOptions);
|
|
341
|
+
|
|
342
|
+
expect(result.address).toMatch(/^0x[0-9a-f]{64}$/);
|
|
343
|
+
expect(result.network).toBe("mainnet");
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it("should connect Dogecoin wallet with connect method", async () => {
|
|
347
|
+
const mockProvider = {
|
|
348
|
+
connect: vi.fn().mockResolvedValue({
|
|
349
|
+
address: "DogeAddress123",
|
|
350
|
+
}),
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const walletOptions: Omit<WalletOptions, "account"> = {
|
|
354
|
+
provider: mockProvider as any,
|
|
355
|
+
chainType: "dogecoin",
|
|
356
|
+
walletInfo: {} as WalletInfo,
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
const connectParams: ConnectParams = {};
|
|
360
|
+
|
|
361
|
+
const result = await connect(connectParams, walletOptions);
|
|
362
|
+
|
|
363
|
+
expect(result.address).toBe("DogeAddress123");
|
|
364
|
+
expect(mockProvider.connect).toHaveBeenCalled();
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it("should connect Dogecoin wallet with requestAccounts method", async () => {
|
|
368
|
+
const mockProvider = {
|
|
369
|
+
requestAccounts: vi.fn().mockResolvedValue(["DogeAddress456"]),
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const walletOptions: Omit<WalletOptions, "account"> = {
|
|
373
|
+
provider: mockProvider as any,
|
|
374
|
+
chainType: "dogecoin",
|
|
375
|
+
walletInfo: {} as WalletInfo,
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const connectParams: ConnectParams = {};
|
|
379
|
+
|
|
380
|
+
const result = await connect(connectParams, walletOptions);
|
|
381
|
+
|
|
382
|
+
expect(result.address).toBe("DogeAddress456");
|
|
383
|
+
expect(mockProvider.requestAccounts).toHaveBeenCalled();
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it("should throw error for unsupported Dogecoin connect", async () => {
|
|
387
|
+
const mockProvider = {};
|
|
388
|
+
|
|
389
|
+
const walletOptions: Omit<WalletOptions, "account"> = {
|
|
390
|
+
provider: mockProvider as any,
|
|
391
|
+
chainType: "dogecoin",
|
|
392
|
+
walletInfo: {} as WalletInfo,
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const connectParams: ConnectParams = {};
|
|
396
|
+
|
|
397
|
+
await expect(connect(connectParams, walletOptions)).rejects.toThrow("connect not supported in dogecoin");
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
describe("disconnect", () => {
|
|
402
|
+
it("should throw error when provider is not provided", async () => {
|
|
403
|
+
const walletOptions: WalletOptions = {
|
|
404
|
+
provider: undefined,
|
|
405
|
+
chainType: "evm",
|
|
406
|
+
walletInfo: {} as WalletInfo,
|
|
407
|
+
account: {
|
|
408
|
+
address: "0x123",
|
|
409
|
+
chainId: "0x1",
|
|
410
|
+
},
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
await expect(disconnect(walletOptions)).rejects.toThrow("Provider is required");
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it("should throw error when chainType is not provided", async () => {
|
|
417
|
+
const mockProvider = {
|
|
418
|
+
request: vi.fn(),
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const walletOptions: WalletOptions = {
|
|
422
|
+
provider: mockProvider as any,
|
|
423
|
+
chainType: undefined,
|
|
424
|
+
walletInfo: {} as WalletInfo,
|
|
425
|
+
account: {
|
|
426
|
+
address: "0x123",
|
|
427
|
+
chainId: "0x1",
|
|
428
|
+
},
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
await expect(disconnect(walletOptions)).rejects.toThrow("chainType is required");
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it("should disconnect EVM wallet", async () => {
|
|
435
|
+
const mockProvider = {
|
|
436
|
+
request: vi.fn().mockResolvedValue(true),
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const walletOptions: WalletOptions = {
|
|
440
|
+
provider: mockProvider as any,
|
|
441
|
+
chainType: "evm",
|
|
442
|
+
walletInfo: {} as WalletInfo,
|
|
443
|
+
account: {
|
|
444
|
+
address: "0x123",
|
|
445
|
+
chainId: "0x1",
|
|
446
|
+
},
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const result = await disconnect(walletOptions);
|
|
450
|
+
|
|
451
|
+
expect(mockProvider.request).toHaveBeenCalledWith({ method: "wallet_revokePermissions" });
|
|
452
|
+
expect(result).toBe(false);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it("should disconnect Solana wallet with disconnect method", async () => {
|
|
456
|
+
const mockProvider = {
|
|
457
|
+
disconnect: vi.fn().mockResolvedValue(undefined),
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const walletOptions: WalletOptions = {
|
|
461
|
+
provider: mockProvider as any,
|
|
462
|
+
chainType: "solana",
|
|
463
|
+
walletInfo: {} as WalletInfo,
|
|
464
|
+
account: {
|
|
465
|
+
address: "SolanaAddress",
|
|
466
|
+
chainId: "",
|
|
467
|
+
},
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
const result = await disconnect(walletOptions);
|
|
471
|
+
|
|
472
|
+
expect(result).toBe(true);
|
|
473
|
+
expect(mockProvider.disconnect).toHaveBeenCalled();
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it("should disconnect Solana wallet with standard:disconnect", async () => {
|
|
477
|
+
const mockProvider = {
|
|
478
|
+
"standard:disconnect": {
|
|
479
|
+
disconnect: vi.fn().mockResolvedValue(undefined),
|
|
480
|
+
},
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
const walletOptions: WalletOptions = {
|
|
484
|
+
provider: mockProvider as any,
|
|
485
|
+
chainType: "solana",
|
|
486
|
+
walletInfo: {} as WalletInfo,
|
|
487
|
+
account: {
|
|
488
|
+
address: "SolanaAddress",
|
|
489
|
+
chainId: "",
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const result = await disconnect(walletOptions);
|
|
494
|
+
|
|
495
|
+
expect(result).toBe(true);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it("should disconnect Aptos wallet with disconnect method", async () => {
|
|
499
|
+
const mockProvider = {
|
|
500
|
+
disconnect: vi.fn().mockResolvedValue(undefined),
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
const walletOptions: WalletOptions = {
|
|
504
|
+
provider: mockProvider as any,
|
|
505
|
+
chainType: "aptos",
|
|
506
|
+
walletInfo: {} as WalletInfo,
|
|
507
|
+
account: {
|
|
508
|
+
address: "0xaptos",
|
|
509
|
+
chainId: "",
|
|
510
|
+
},
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
const result = await disconnect(walletOptions);
|
|
514
|
+
|
|
515
|
+
expect(result).toBe(true);
|
|
516
|
+
expect(mockProvider.disconnect).toHaveBeenCalled();
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it("should disconnect Aptos wallet with aptos:disconnect", async () => {
|
|
520
|
+
const mockProvider = {
|
|
521
|
+
"aptos:disconnect": {
|
|
522
|
+
disconnect: vi.fn().mockResolvedValue(undefined),
|
|
523
|
+
},
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
const walletOptions: WalletOptions = {
|
|
527
|
+
provider: mockProvider as any,
|
|
528
|
+
chainType: "aptos",
|
|
529
|
+
walletInfo: {} as WalletInfo,
|
|
530
|
+
account: {
|
|
531
|
+
address: "0xaptos",
|
|
532
|
+
chainId: "",
|
|
533
|
+
},
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
const result = await disconnect(walletOptions);
|
|
537
|
+
|
|
538
|
+
expect(result).toBe(true);
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it("should disconnect Dogecoin wallet", async () => {
|
|
542
|
+
const mockProvider = {
|
|
543
|
+
disconnect: vi.fn().mockResolvedValue(undefined),
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
const walletOptions: WalletOptions = {
|
|
547
|
+
provider: mockProvider as any,
|
|
548
|
+
chainType: "dogecoin",
|
|
549
|
+
walletInfo: {} as WalletInfo,
|
|
550
|
+
account: {
|
|
551
|
+
address: "DogeAddress",
|
|
552
|
+
chainId: "",
|
|
553
|
+
},
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
const result = await disconnect(walletOptions);
|
|
557
|
+
|
|
558
|
+
expect(result).toBe(true);
|
|
559
|
+
expect(mockProvider.disconnect).toHaveBeenCalled();
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it("should return true for Dogecoin even without disconnect method", async () => {
|
|
563
|
+
const mockProvider = {};
|
|
564
|
+
|
|
565
|
+
const walletOptions: WalletOptions = {
|
|
566
|
+
provider: mockProvider as any,
|
|
567
|
+
chainType: "dogecoin",
|
|
568
|
+
walletInfo: {} as WalletInfo,
|
|
569
|
+
account: {
|
|
570
|
+
address: "DogeAddress",
|
|
571
|
+
chainId: "",
|
|
572
|
+
},
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
const result = await disconnect(walletOptions);
|
|
576
|
+
|
|
577
|
+
expect(result).toBe(true);
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
describe("connectMobile", () => {
|
|
582
|
+
it("should resolve with address and chainId when useWalletConnect and mobile.getUri returns uri", async () => {
|
|
583
|
+
const mockMobileProvider = {
|
|
584
|
+
request: vi.fn().mockResolvedValue("0x1"),
|
|
585
|
+
};
|
|
586
|
+
mockCreateWalletConnectEVMProvider.mockReturnValue({
|
|
587
|
+
getProvider: vi.fn().mockResolvedValue(mockMobileProvider),
|
|
588
|
+
connectUri: "wc://test-uri",
|
|
589
|
+
connect: vi.fn().mockResolvedValue(["0xmobile-addr"]),
|
|
590
|
+
});
|
|
591
|
+
const getUri = vi.fn().mockResolvedValue("phantom://wc");
|
|
592
|
+
const result = await connectMobile({
|
|
593
|
+
connectParams: { useWalletConnect: true, dappLink: "https://app.com" },
|
|
594
|
+
walletOptions: {
|
|
595
|
+
walletInfo: { uuid: "phantom", mobile: { getUri }, links: {} } as WalletInfo,
|
|
596
|
+
chainType: "evm",
|
|
597
|
+
},
|
|
598
|
+
});
|
|
599
|
+
expect((result as any).address).toBe("0xmobile-addr");
|
|
600
|
+
expect((result as any).chainId).toBe("0x1");
|
|
601
|
+
expect(getUri).toHaveBeenCalledWith("wc://test-uri");
|
|
602
|
+
expect(mockOpenUri).toHaveBeenCalledWith("phantom://wc");
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
it("should resolve with empty address when useWalletConnect but mobile.getUri returns null", async () => {
|
|
606
|
+
mockCreateWalletConnectEVMProvider.mockReturnValue({
|
|
607
|
+
getProvider: vi.fn().mockResolvedValue({ request: vi.fn() }),
|
|
608
|
+
connectUri: "wc://x",
|
|
609
|
+
connect: vi.fn(),
|
|
610
|
+
});
|
|
611
|
+
const result = await connectMobile({
|
|
612
|
+
connectParams: { useWalletConnect: true },
|
|
613
|
+
walletOptions: {
|
|
614
|
+
walletInfo: { uuid: "wc", mobile: { getUri: vi.fn().mockResolvedValue(null) }, links: {} } as WalletInfo,
|
|
615
|
+
chainType: "evm",
|
|
616
|
+
},
|
|
617
|
+
});
|
|
618
|
+
expect((result as any).address).toBe("");
|
|
619
|
+
expect((result as any).chainId).toBe("");
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
it("should reject when connectWithWalletConnect getProvider or connect fails", async () => {
|
|
623
|
+
mockCreateWalletConnectEVMProvider.mockReturnValue({
|
|
624
|
+
getProvider: vi.fn().mockRejectedValue(new Error("WC init failed")),
|
|
625
|
+
connectUri: "wc://x",
|
|
626
|
+
connect: vi.fn(),
|
|
627
|
+
});
|
|
628
|
+
const getUri = vi.fn().mockResolvedValue("wc://link");
|
|
629
|
+
await expect(
|
|
630
|
+
connectMobile({
|
|
631
|
+
connectParams: { useWalletConnect: true },
|
|
632
|
+
walletOptions: {
|
|
633
|
+
walletInfo: { uuid: "phantom", mobile: { getUri }, links: {} } as WalletInfo,
|
|
634
|
+
chainType: "evm",
|
|
635
|
+
},
|
|
636
|
+
}),
|
|
637
|
+
).rejects.toThrow("WC init failed");
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it("should not call openUri when uuid is walletConnect", async () => {
|
|
641
|
+
mockOpenUri.mockClear();
|
|
642
|
+
const mockMobileProvider = { request: vi.fn().mockResolvedValue("0x1") };
|
|
643
|
+
mockCreateWalletConnectEVMProvider.mockReturnValue({
|
|
644
|
+
getProvider: vi.fn().mockResolvedValue(mockMobileProvider),
|
|
645
|
+
connectUri: "wc://uri",
|
|
646
|
+
connect: vi.fn().mockResolvedValue(["0xaddr"]),
|
|
647
|
+
});
|
|
648
|
+
const getUri = vi.fn().mockResolvedValue("wc://link");
|
|
649
|
+
await connectMobile({
|
|
650
|
+
connectParams: { useWalletConnect: true },
|
|
651
|
+
walletOptions: {
|
|
652
|
+
walletInfo: { uuid: "walletConnect", mobile: { getUri }, links: {} } as WalletInfo,
|
|
653
|
+
chainType: "evm",
|
|
654
|
+
},
|
|
655
|
+
});
|
|
656
|
+
expect(getUri).toHaveBeenCalledWith("wc://uri");
|
|
657
|
+
expect(mockOpenUri).not.toHaveBeenCalled();
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
it("should use connectWithWalletConnect when useWalletConnect false but mobile.getUri exists", async () => {
|
|
661
|
+
const mockMobileProvider = { request: vi.fn().mockResolvedValue("0x1") };
|
|
662
|
+
mockCreateWalletConnectEVMProvider.mockReturnValue({
|
|
663
|
+
getProvider: vi.fn().mockResolvedValue(mockMobileProvider),
|
|
664
|
+
connectUri: "wc://y",
|
|
665
|
+
connect: vi.fn().mockResolvedValue(["0xvia-getUri"]),
|
|
666
|
+
});
|
|
667
|
+
const getUri = vi.fn().mockResolvedValue("mywallet://wc");
|
|
668
|
+
const result = await connectMobile({
|
|
669
|
+
connectParams: { useWalletConnect: false, dappLink: "https://app.com" },
|
|
670
|
+
walletOptions: {
|
|
671
|
+
walletInfo: { uuid: "mywallet", mobile: { getUri }, links: {} } as WalletInfo,
|
|
672
|
+
chainType: "evm",
|
|
673
|
+
},
|
|
674
|
+
});
|
|
675
|
+
expect((result as any).address).toBe("0xvia-getUri");
|
|
676
|
+
expect((result as any).chainId).toBe("0x1");
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
it("should call openUri when deeplinkTemplate.deeplink is provided", async () => {
|
|
680
|
+
mockOpenUri.mockClear();
|
|
681
|
+
connectMobile({
|
|
682
|
+
connectParams: { useWalletConnect: false, dappLink: "https://myapp.com" },
|
|
683
|
+
walletOptions: {
|
|
684
|
+
walletInfo: {
|
|
685
|
+
mobile: { deeplinkTemplate: { deeplink: "https://phantom.app/ul/{dappUrl}" } },
|
|
686
|
+
links: {},
|
|
687
|
+
} as WalletInfo,
|
|
688
|
+
chainType: "solana",
|
|
689
|
+
},
|
|
690
|
+
});
|
|
691
|
+
await vi.waitFor(() => {
|
|
692
|
+
expect(mockOpenUri).toHaveBeenCalledWith("https://phantom.app/ul/https%3A%2F%2Fmyapp.com");
|
|
693
|
+
});
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
it("should call openUri and throw when links have android_install", async () => {
|
|
697
|
+
mockIsAndroid.mockReturnValueOnce(true);
|
|
698
|
+
await expect(
|
|
699
|
+
connectMobile({
|
|
700
|
+
connectParams: { useWalletConnect: false },
|
|
701
|
+
walletOptions: {
|
|
702
|
+
walletInfo: {
|
|
703
|
+
name: "TestWallet",
|
|
704
|
+
mobile: {},
|
|
705
|
+
links: {
|
|
706
|
+
android_install: "https://play.google.com",
|
|
707
|
+
ios_install: "https://apps.apple.com",
|
|
708
|
+
homepage: "https://wallet.com",
|
|
709
|
+
},
|
|
710
|
+
} as WalletInfo,
|
|
711
|
+
chainType: "evm",
|
|
712
|
+
},
|
|
713
|
+
}),
|
|
714
|
+
).rejects.toThrow("TestWallet not supported");
|
|
715
|
+
expect(mockOpenUri).toHaveBeenCalledWith("https://play.google.com");
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
it("should call openUri with ios_install when isIOS and links provided", async () => {
|
|
719
|
+
mockIsIOS.mockReturnValueOnce(true);
|
|
720
|
+
mockOpenUri.mockClear();
|
|
721
|
+
await expect(
|
|
722
|
+
connectMobile({
|
|
723
|
+
connectParams: { useWalletConnect: false },
|
|
724
|
+
walletOptions: {
|
|
725
|
+
walletInfo: {
|
|
726
|
+
name: "IosWallet",
|
|
727
|
+
mobile: {},
|
|
728
|
+
links: {
|
|
729
|
+
android_install: "https://play.google.com",
|
|
730
|
+
ios_install: "https://apps.apple.com",
|
|
731
|
+
homepage: "https://wallet.com",
|
|
732
|
+
},
|
|
733
|
+
} as WalletInfo,
|
|
734
|
+
chainType: "evm",
|
|
735
|
+
},
|
|
736
|
+
}),
|
|
737
|
+
).rejects.toThrow("IosWallet not supported");
|
|
738
|
+
expect(mockOpenUri).toHaveBeenCalledWith("https://apps.apple.com");
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
it("should call openUri with homepage when links have no android/ios install", async () => {
|
|
742
|
+
mockIsAndroid.mockReturnValue(false);
|
|
743
|
+
mockIsIOS.mockReturnValue(false);
|
|
744
|
+
mockOpenUri.mockClear();
|
|
745
|
+
await expect(
|
|
746
|
+
connectMobile({
|
|
747
|
+
connectParams: { useWalletConnect: false },
|
|
748
|
+
walletOptions: {
|
|
749
|
+
walletInfo: {
|
|
750
|
+
name: "DesktopWallet",
|
|
751
|
+
mobile: {},
|
|
752
|
+
links: { homepage: "https://wallet.com" },
|
|
753
|
+
} as WalletInfo,
|
|
754
|
+
chainType: "evm",
|
|
755
|
+
},
|
|
756
|
+
}),
|
|
757
|
+
).rejects.toThrow("DesktopWallet not supported");
|
|
758
|
+
expect(mockOpenUri).toHaveBeenCalledWith("https://wallet.com");
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
it("should throw when wallet has no supported mobile path", async () => {
|
|
762
|
+
await expect(
|
|
763
|
+
connectMobile({
|
|
764
|
+
connectParams: { useWalletConnect: false },
|
|
765
|
+
walletOptions: {
|
|
766
|
+
walletInfo: { name: "NoSupport", mobile: {}, links: {} } as WalletInfo,
|
|
767
|
+
chainType: "evm",
|
|
768
|
+
},
|
|
769
|
+
}),
|
|
770
|
+
).rejects.toThrow("NoSupport not supported");
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
describe("getBalance", () => {
|
|
775
|
+
it("should throw error when provider is not provided", async () => {
|
|
776
|
+
const walletOptions: WalletOptions = {
|
|
777
|
+
provider: undefined,
|
|
778
|
+
chainType: "evm",
|
|
779
|
+
walletInfo: {} as WalletInfo,
|
|
780
|
+
account: {
|
|
781
|
+
address: "0x123",
|
|
782
|
+
chainId: "0x1",
|
|
783
|
+
},
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
await expect(
|
|
787
|
+
getBalance(
|
|
788
|
+
{
|
|
789
|
+
chainId: "0x1",
|
|
790
|
+
},
|
|
791
|
+
walletOptions,
|
|
792
|
+
),
|
|
793
|
+
).rejects.toThrow("Provider is required");
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
it("should throw error when chainType is not provided", async () => {
|
|
797
|
+
const mockProvider = {
|
|
798
|
+
request: vi.fn(),
|
|
799
|
+
};
|
|
800
|
+
|
|
801
|
+
const walletOptions: WalletOptions = {
|
|
802
|
+
provider: mockProvider as any,
|
|
803
|
+
chainType: undefined,
|
|
804
|
+
walletInfo: {} as WalletInfo,
|
|
805
|
+
account: {
|
|
806
|
+
address: "0x123",
|
|
807
|
+
chainId: "0x1",
|
|
808
|
+
},
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
await expect(
|
|
812
|
+
getBalance(
|
|
813
|
+
{
|
|
814
|
+
chainId: "0x1",
|
|
815
|
+
},
|
|
816
|
+
walletOptions,
|
|
817
|
+
),
|
|
818
|
+
).rejects.toThrow("Chain type is required");
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
it("should get EVM balance", async () => {
|
|
822
|
+
const mockProvider = {
|
|
823
|
+
request: vi.fn().mockResolvedValue("0x2386f26fc10000"), // 10000000000000000 wei
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
const walletOptions: WalletOptions = {
|
|
827
|
+
provider: mockProvider as any,
|
|
828
|
+
chainType: "evm",
|
|
829
|
+
walletInfo: {} as WalletInfo,
|
|
830
|
+
account: {
|
|
831
|
+
address: "0x1234567890123456789012345678901234567890",
|
|
832
|
+
chainId: "0x1",
|
|
833
|
+
},
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
const result = await getBalance(
|
|
837
|
+
{
|
|
838
|
+
chainId: "0x1",
|
|
839
|
+
},
|
|
840
|
+
walletOptions,
|
|
841
|
+
);
|
|
842
|
+
|
|
843
|
+
expect(result.total).toBe(10000000000000000);
|
|
844
|
+
expect(mockProvider.request).toHaveBeenCalledWith({
|
|
845
|
+
method: "eth_getBalance",
|
|
846
|
+
params: ["0x1234567890123456789012345678901234567890", "latest"],
|
|
847
|
+
});
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
it("should throw error when EVM chainId mismatch", async () => {
|
|
851
|
+
const mockProvider = {
|
|
852
|
+
request: vi.fn(),
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
const walletOptions: WalletOptions = {
|
|
856
|
+
provider: mockProvider as any,
|
|
857
|
+
chainType: "evm",
|
|
858
|
+
walletInfo: {} as WalletInfo,
|
|
859
|
+
account: {
|
|
860
|
+
address: "0x123",
|
|
861
|
+
chainId: "0x1",
|
|
862
|
+
},
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
await expect(
|
|
866
|
+
getBalance(
|
|
867
|
+
{
|
|
868
|
+
chainId: "0x2",
|
|
869
|
+
},
|
|
870
|
+
walletOptions,
|
|
871
|
+
),
|
|
872
|
+
).rejects.toThrow("Chain id is required and must be current chain id");
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
it("should throw error for unsupported chain type", async () => {
|
|
876
|
+
const mockProvider = {};
|
|
877
|
+
|
|
878
|
+
const walletOptions: WalletOptions = {
|
|
879
|
+
provider: mockProvider as any,
|
|
880
|
+
chainType: "unsupported" as any,
|
|
881
|
+
walletInfo: {} as WalletInfo,
|
|
882
|
+
account: {
|
|
883
|
+
address: "0x123",
|
|
884
|
+
chainId: "0x1",
|
|
885
|
+
},
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
await expect(
|
|
889
|
+
getBalance(
|
|
890
|
+
{
|
|
891
|
+
chainId: "0x1",
|
|
892
|
+
},
|
|
893
|
+
walletOptions,
|
|
894
|
+
),
|
|
895
|
+
).rejects.toThrow("getBalance not supported in unsupported");
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
it("should get balance for Solana", async () => {
|
|
899
|
+
const mockProvider = {
|
|
900
|
+
request: vi.fn(),
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
// Use a valid Solana address (base58 encoded)
|
|
904
|
+
// System Program address: 11111111111111111111111111111111
|
|
905
|
+
const validSolanaAddress = "11111111111111111111111111111111";
|
|
906
|
+
|
|
907
|
+
const walletOptions: WalletOptions = {
|
|
908
|
+
provider: mockProvider as any,
|
|
909
|
+
chainType: "solana",
|
|
910
|
+
walletInfo: {} as WalletInfo,
|
|
911
|
+
account: {
|
|
912
|
+
address: validSolanaAddress,
|
|
913
|
+
chainId: "mainnet-beta",
|
|
914
|
+
},
|
|
915
|
+
};
|
|
916
|
+
|
|
917
|
+
const result = await getBalance(
|
|
918
|
+
{
|
|
919
|
+
chainId: "mainnet-beta",
|
|
920
|
+
address: validSolanaAddress,
|
|
921
|
+
},
|
|
922
|
+
walletOptions,
|
|
923
|
+
);
|
|
924
|
+
|
|
925
|
+
expect(result).toHaveProperty("total");
|
|
926
|
+
expect(result).toHaveProperty("tokenInfo");
|
|
927
|
+
expect(result.total).toBe(1000000);
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
it("should get balance for Aptos", async () => {
|
|
931
|
+
const mockProvider = {
|
|
932
|
+
request: vi.fn(),
|
|
933
|
+
};
|
|
934
|
+
|
|
935
|
+
const walletOptions: WalletOptions = {
|
|
936
|
+
provider: mockProvider as any,
|
|
937
|
+
chainType: "aptos",
|
|
938
|
+
walletInfo: {} as WalletInfo,
|
|
939
|
+
account: {
|
|
940
|
+
address: "0x1",
|
|
941
|
+
chainId: "mainnet",
|
|
942
|
+
network: "mainnet",
|
|
943
|
+
},
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
const result = await getBalance(
|
|
947
|
+
{
|
|
948
|
+
chainId: "mainnet",
|
|
949
|
+
address: "0x1",
|
|
950
|
+
},
|
|
951
|
+
walletOptions,
|
|
952
|
+
);
|
|
953
|
+
|
|
954
|
+
expect(result).toHaveProperty("total");
|
|
955
|
+
expect(result).toHaveProperty("tokenInfo");
|
|
956
|
+
expect(result.total).toBe(1000000);
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
it("should throw error when address is missing for Aptos", async () => {
|
|
960
|
+
const mockProvider = {
|
|
961
|
+
request: vi.fn(),
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
const walletOptions: WalletOptions = {
|
|
965
|
+
provider: mockProvider as any,
|
|
966
|
+
chainType: "aptos",
|
|
967
|
+
walletInfo: {} as WalletInfo,
|
|
968
|
+
account: {
|
|
969
|
+
address: "",
|
|
970
|
+
chainId: "mainnet",
|
|
971
|
+
},
|
|
972
|
+
};
|
|
973
|
+
|
|
974
|
+
await expect(
|
|
975
|
+
getBalance(
|
|
976
|
+
{
|
|
977
|
+
chainId: "mainnet",
|
|
978
|
+
},
|
|
979
|
+
walletOptions,
|
|
980
|
+
),
|
|
981
|
+
).rejects.toThrow("Address is required for Aptos balance query");
|
|
982
|
+
});
|
|
983
|
+
|
|
984
|
+
it("should rethrow when Aptos getAccountResource fails", async () => {
|
|
985
|
+
mockGetAccountResource.mockRejectedValueOnce(new Error("Aptos RPC error"));
|
|
986
|
+
const walletOptions: WalletOptions = {
|
|
987
|
+
provider: {} as any,
|
|
988
|
+
chainType: "aptos",
|
|
989
|
+
walletInfo: {} as WalletInfo,
|
|
990
|
+
account: { address: "0x1", chainId: "mainnet", network: "mainnet" },
|
|
991
|
+
};
|
|
992
|
+
await expect(
|
|
993
|
+
getBalance({ chainId: "mainnet", address: "0x1" }, walletOptions),
|
|
994
|
+
).rejects.toThrow("Aptos RPC error");
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
it("should get balance for Dogecoin", async () => {
|
|
998
|
+
const mockProvider = {
|
|
999
|
+
getBalance: vi.fn().mockResolvedValue({ balance: "1000000" }),
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
const walletOptions: WalletOptions = {
|
|
1003
|
+
provider: mockProvider as any,
|
|
1004
|
+
chainType: "dogecoin",
|
|
1005
|
+
walletInfo: {} as WalletInfo,
|
|
1006
|
+
account: {
|
|
1007
|
+
address: "DogeAddress123",
|
|
1008
|
+
chainId: "mainnet",
|
|
1009
|
+
},
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
const result = await getBalance(
|
|
1013
|
+
{
|
|
1014
|
+
chainId: "mainnet",
|
|
1015
|
+
},
|
|
1016
|
+
walletOptions,
|
|
1017
|
+
);
|
|
1018
|
+
|
|
1019
|
+
expect(result).toHaveProperty("total");
|
|
1020
|
+
expect(mockProvider.getBalance).toHaveBeenCalled();
|
|
1021
|
+
});
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
describe("switchChain", () => {
|
|
1025
|
+
it("should switch chain for EVM", async () => {
|
|
1026
|
+
const mockProvider = {
|
|
1027
|
+
request: vi.fn().mockResolvedValue(null),
|
|
1028
|
+
};
|
|
1029
|
+
|
|
1030
|
+
const walletOptions: WalletOptions = {
|
|
1031
|
+
provider: mockProvider as any,
|
|
1032
|
+
chainType: "evm",
|
|
1033
|
+
walletInfo: {} as WalletInfo,
|
|
1034
|
+
account: {
|
|
1035
|
+
address: "0x123",
|
|
1036
|
+
chainId: "0x1",
|
|
1037
|
+
},
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
const result = await switchChain("0x5", walletOptions);
|
|
1041
|
+
expect(result).toBe(true);
|
|
1042
|
+
expect(mockProvider.request).toHaveBeenCalledWith({
|
|
1043
|
+
method: "wallet_switchEthereumChain",
|
|
1044
|
+
params: [{ chainId: "0x5" }],
|
|
1045
|
+
});
|
|
1046
|
+
});
|
|
1047
|
+
|
|
1048
|
+
it("should throw error when provider is missing", async () => {
|
|
1049
|
+
const walletOptions: WalletOptions = {
|
|
1050
|
+
provider: undefined as any,
|
|
1051
|
+
chainType: "evm",
|
|
1052
|
+
walletInfo: {} as WalletInfo,
|
|
1053
|
+
account: {
|
|
1054
|
+
address: "0x123",
|
|
1055
|
+
chainId: "0x1",
|
|
1056
|
+
},
|
|
1057
|
+
};
|
|
1058
|
+
|
|
1059
|
+
await expect(switchChain("0x5", walletOptions)).rejects.toThrow("Provider is required");
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
it("should throw error when chainType is missing", async () => {
|
|
1063
|
+
const mockProvider = {
|
|
1064
|
+
request: vi.fn(),
|
|
1065
|
+
};
|
|
1066
|
+
|
|
1067
|
+
const walletOptions: WalletOptions = {
|
|
1068
|
+
provider: mockProvider as any,
|
|
1069
|
+
chainType: undefined as any,
|
|
1070
|
+
walletInfo: {} as WalletInfo,
|
|
1071
|
+
account: {
|
|
1072
|
+
address: "0x123",
|
|
1073
|
+
chainId: "0x1",
|
|
1074
|
+
},
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1077
|
+
await expect(switchChain("0x5", walletOptions)).rejects.toThrow("Chain type is required");
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
it("should throw error for unsupported chain type", async () => {
|
|
1081
|
+
const mockProvider = {
|
|
1082
|
+
request: vi.fn(),
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1085
|
+
const walletOptions: WalletOptions = {
|
|
1086
|
+
provider: mockProvider as any,
|
|
1087
|
+
chainType: "solana",
|
|
1088
|
+
walletInfo: {} as WalletInfo,
|
|
1089
|
+
account: {
|
|
1090
|
+
address: "0x123",
|
|
1091
|
+
chainId: "0x1",
|
|
1092
|
+
},
|
|
1093
|
+
};
|
|
1094
|
+
|
|
1095
|
+
await expect(switchChain("0x5", walletOptions)).rejects.toThrow("switchChain Not supported in solana");
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
it("should throw when chainId is empty or invalid", async () => {
|
|
1099
|
+
const mockProvider = { request: vi.fn() };
|
|
1100
|
+
const walletOptions: WalletOptions = {
|
|
1101
|
+
provider: mockProvider as any,
|
|
1102
|
+
chainType: "evm",
|
|
1103
|
+
walletInfo: {} as WalletInfo,
|
|
1104
|
+
account: { address: "0x123", chainId: "0x1" },
|
|
1105
|
+
};
|
|
1106
|
+
await expect(switchChain("", walletOptions)).rejects.toThrow("Chain ID is required");
|
|
1107
|
+
});
|
|
1108
|
+
|
|
1109
|
+
it("should rethrow when provider.request fails in switchChain", async () => {
|
|
1110
|
+
const mockProvider = {
|
|
1111
|
+
request: vi.fn().mockRejectedValue(new Error("User rejected")),
|
|
1112
|
+
};
|
|
1113
|
+
const walletOptions: WalletOptions = {
|
|
1114
|
+
provider: mockProvider as any,
|
|
1115
|
+
chainType: "evm",
|
|
1116
|
+
walletInfo: {} as WalletInfo,
|
|
1117
|
+
account: { address: "0x123", chainId: "0x1" },
|
|
1118
|
+
};
|
|
1119
|
+
await expect(switchChain("0x5", walletOptions)).rejects.toThrow("User rejected");
|
|
1120
|
+
});
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
describe("addChain", () => {
|
|
1124
|
+
it("should add chain for EVM", async () => {
|
|
1125
|
+
const mockProvider = {
|
|
1126
|
+
request: vi.fn().mockResolvedValue(null),
|
|
1127
|
+
};
|
|
1128
|
+
|
|
1129
|
+
const walletOptions: WalletOptions = {
|
|
1130
|
+
provider: mockProvider as any,
|
|
1131
|
+
chainType: "evm",
|
|
1132
|
+
walletInfo: {} as WalletInfo,
|
|
1133
|
+
account: {
|
|
1134
|
+
address: "0x123",
|
|
1135
|
+
chainId: "0x1",
|
|
1136
|
+
},
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
const chainInfo = {
|
|
1140
|
+
chainId: "0x5",
|
|
1141
|
+
chainName: "Goerli",
|
|
1142
|
+
nativeCurrency: {
|
|
1143
|
+
name: "ETH",
|
|
1144
|
+
symbol: "ETH",
|
|
1145
|
+
decimals: 18,
|
|
1146
|
+
},
|
|
1147
|
+
rpcUrls: ["https://goerli.infura.io/v3/"],
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1150
|
+
const result = await addChain(chainInfo, walletOptions);
|
|
1151
|
+
expect(result).toBe(true);
|
|
1152
|
+
expect(mockProvider.request).toHaveBeenCalledWith({
|
|
1153
|
+
method: "wallet_addEthereumChain",
|
|
1154
|
+
params: [chainInfo],
|
|
1155
|
+
});
|
|
1156
|
+
});
|
|
1157
|
+
|
|
1158
|
+
it("should convert decimal chainId to hex", async () => {
|
|
1159
|
+
const mockProvider = {
|
|
1160
|
+
request: vi.fn().mockResolvedValue(null),
|
|
1161
|
+
};
|
|
1162
|
+
|
|
1163
|
+
const walletOptions: WalletOptions = {
|
|
1164
|
+
provider: mockProvider as any,
|
|
1165
|
+
chainType: "evm",
|
|
1166
|
+
walletInfo: {} as WalletInfo,
|
|
1167
|
+
account: {
|
|
1168
|
+
address: "0x123",
|
|
1169
|
+
chainId: "0x1",
|
|
1170
|
+
},
|
|
1171
|
+
};
|
|
1172
|
+
|
|
1173
|
+
const chainInfo = {
|
|
1174
|
+
chainId: "5",
|
|
1175
|
+
chainName: "Goerli",
|
|
1176
|
+
nativeCurrency: {
|
|
1177
|
+
name: "ETH",
|
|
1178
|
+
symbol: "ETH",
|
|
1179
|
+
decimals: 18,
|
|
1180
|
+
},
|
|
1181
|
+
rpcUrls: ["https://goerli.infura.io/v3/"],
|
|
1182
|
+
};
|
|
1183
|
+
|
|
1184
|
+
await addChain(chainInfo, walletOptions);
|
|
1185
|
+
expect(chainInfo.chainId).toBe("0x5");
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
it("should throw error when chainId is missing", async () => {
|
|
1189
|
+
const mockProvider = {
|
|
1190
|
+
request: vi.fn(),
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
const walletOptions: WalletOptions = {
|
|
1194
|
+
provider: mockProvider as any,
|
|
1195
|
+
chainType: "evm",
|
|
1196
|
+
walletInfo: {} as WalletInfo,
|
|
1197
|
+
account: {
|
|
1198
|
+
address: "0x123",
|
|
1199
|
+
chainId: "0x1",
|
|
1200
|
+
},
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
await expect(addChain({} as any, walletOptions)).rejects.toThrow("Chain ID is required");
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
it("should throw when provider is missing in addChain", async () => {
|
|
1207
|
+
const walletOptions: WalletOptions = {
|
|
1208
|
+
provider: undefined as any,
|
|
1209
|
+
chainType: "evm",
|
|
1210
|
+
walletInfo: {} as WalletInfo,
|
|
1211
|
+
account: { address: "0x123", chainId: "0x1" },
|
|
1212
|
+
};
|
|
1213
|
+
await expect(
|
|
1214
|
+
addChain({ chainId: "0x5", chainName: "Goerli", rpcUrls: [] }, walletOptions),
|
|
1215
|
+
).rejects.toThrow("Provider is required");
|
|
1216
|
+
});
|
|
1217
|
+
|
|
1218
|
+
it("should throw when chainType is missing in addChain", async () => {
|
|
1219
|
+
const walletOptions: WalletOptions = {
|
|
1220
|
+
provider: { request: vi.fn() } as any,
|
|
1221
|
+
chainType: undefined as any,
|
|
1222
|
+
walletInfo: {} as WalletInfo,
|
|
1223
|
+
account: { address: "0x123", chainId: "0x1" },
|
|
1224
|
+
};
|
|
1225
|
+
await expect(
|
|
1226
|
+
addChain({ chainId: "0x5", chainName: "Goerli", rpcUrls: [] }, walletOptions),
|
|
1227
|
+
).rejects.toThrow("Chain type is required");
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
it("should rethrow when provider.request fails in addChain", async () => {
|
|
1231
|
+
const mockProvider = {
|
|
1232
|
+
request: vi.fn().mockRejectedValue(new Error("Add chain failed")),
|
|
1233
|
+
};
|
|
1234
|
+
const walletOptions: WalletOptions = {
|
|
1235
|
+
provider: mockProvider as any,
|
|
1236
|
+
chainType: "evm",
|
|
1237
|
+
walletInfo: {} as WalletInfo,
|
|
1238
|
+
account: { address: "0x123", chainId: "0x1" },
|
|
1239
|
+
};
|
|
1240
|
+
await expect(
|
|
1241
|
+
addChain({ chainId: "0x5", chainName: "Goerli", rpcUrls: [] }, walletOptions),
|
|
1242
|
+
).rejects.toThrow("Add chain failed");
|
|
1243
|
+
});
|
|
1244
|
+
|
|
1245
|
+
it("should throw for unsupported chain type in addChain", async () => {
|
|
1246
|
+
const walletOptions: WalletOptions = {
|
|
1247
|
+
provider: { request: vi.fn() } as any,
|
|
1248
|
+
chainType: "solana",
|
|
1249
|
+
walletInfo: {} as WalletInfo,
|
|
1250
|
+
account: { address: "0x123", chainId: "0x1" },
|
|
1251
|
+
};
|
|
1252
|
+
await expect(
|
|
1253
|
+
addChain({ chainId: "0x5", chainName: "Goerli", rpcUrls: [] }, walletOptions),
|
|
1254
|
+
).rejects.toThrow("addChain Not supported in solana");
|
|
1255
|
+
});
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
describe("signInWithWallet", () => {
|
|
1259
|
+
const baseSignInData = {
|
|
1260
|
+
domain: "example.com",
|
|
1261
|
+
scheme: "https",
|
|
1262
|
+
nonce: "test-nonce",
|
|
1263
|
+
};
|
|
1264
|
+
|
|
1265
|
+
it("should sign in with EVM wallet", async () => {
|
|
1266
|
+
const mockProvider = {
|
|
1267
|
+
request: vi.fn().mockResolvedValue("0xsignature"),
|
|
1268
|
+
};
|
|
1269
|
+
|
|
1270
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1271
|
+
provider: mockProvider as any,
|
|
1272
|
+
chainType: "evm",
|
|
1273
|
+
walletInfo: {} as WalletInfo,
|
|
1274
|
+
account: {
|
|
1275
|
+
address: "0x123",
|
|
1276
|
+
chainId: "0x1",
|
|
1277
|
+
},
|
|
1278
|
+
};
|
|
1279
|
+
|
|
1280
|
+
const signInData = {
|
|
1281
|
+
...baseSignInData,
|
|
1282
|
+
statement: "Sign in",
|
|
1283
|
+
version: "1",
|
|
1284
|
+
issuedAt: new Date().toISOString(),
|
|
1285
|
+
};
|
|
1286
|
+
|
|
1287
|
+
const result = await signInWithWallet(signInData, walletOptions);
|
|
1288
|
+
expect(result).toBe("0xsignature");
|
|
1289
|
+
});
|
|
1290
|
+
|
|
1291
|
+
it("should sign in with Aptos wallet", async () => {
|
|
1292
|
+
const mockProvider = {
|
|
1293
|
+
signMessage: vi.fn().mockResolvedValue({ args: { signature: "0xaptos-sig" } }),
|
|
1294
|
+
};
|
|
1295
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1296
|
+
provider: mockProvider as any,
|
|
1297
|
+
chainType: "aptos",
|
|
1298
|
+
walletInfo: {} as WalletInfo,
|
|
1299
|
+
account: { address: "0xaptos", chainId: "mainnet" },
|
|
1300
|
+
};
|
|
1301
|
+
const result = await signInWithWallet(
|
|
1302
|
+
{ ...baseSignInData, statement: "Sign", version: "1", issuedAt: new Date().toISOString() },
|
|
1303
|
+
walletOptions,
|
|
1304
|
+
);
|
|
1305
|
+
expect(result).toBeDefined();
|
|
1306
|
+
});
|
|
1307
|
+
|
|
1308
|
+
it("should sign in with Solana wallet via signIn", async () => {
|
|
1309
|
+
const mockProvider = {
|
|
1310
|
+
signIn: vi.fn().mockResolvedValue({ signature: { toHex: () => "0xsol-sig" } }),
|
|
1311
|
+
};
|
|
1312
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1313
|
+
provider: mockProvider as any,
|
|
1314
|
+
chainType: "solana",
|
|
1315
|
+
walletInfo: {} as WalletInfo,
|
|
1316
|
+
account: { address: "SolanaAddr", chainId: "" },
|
|
1317
|
+
};
|
|
1318
|
+
const signInData = {
|
|
1319
|
+
...baseSignInData,
|
|
1320
|
+
domain: "example.com",
|
|
1321
|
+
scheme: "https",
|
|
1322
|
+
statement: "Sign",
|
|
1323
|
+
version: "1",
|
|
1324
|
+
issuedAt: new Date().toISOString(),
|
|
1325
|
+
resources: ["https://example.com"] as `https://${string}`[],
|
|
1326
|
+
};
|
|
1327
|
+
const result = await signInWithWallet(signInData, walletOptions);
|
|
1328
|
+
expect(result).toBe("0xsol-sig");
|
|
1329
|
+
expect(mockProvider.signIn).toHaveBeenCalled();
|
|
1330
|
+
});
|
|
1331
|
+
|
|
1332
|
+
it("should sign in with Solana wallet via solana:signIn", async () => {
|
|
1333
|
+
const mockProvider = {
|
|
1334
|
+
"solana:signIn": {
|
|
1335
|
+
signIn: vi.fn().mockResolvedValue("0xsol-sig-string"),
|
|
1336
|
+
},
|
|
1337
|
+
};
|
|
1338
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1339
|
+
provider: mockProvider as any,
|
|
1340
|
+
chainType: "solana",
|
|
1341
|
+
walletInfo: {} as WalletInfo,
|
|
1342
|
+
account: { address: "SolanaAddr", chainId: "" },
|
|
1343
|
+
};
|
|
1344
|
+
const result = await signInWithWallet(
|
|
1345
|
+
{ ...baseSignInData, domain: "example.com", scheme: "https" },
|
|
1346
|
+
walletOptions,
|
|
1347
|
+
);
|
|
1348
|
+
expect(result).toBe("0xsol-sig-string");
|
|
1349
|
+
});
|
|
1350
|
+
|
|
1351
|
+
it("should sign in with Solana when signIn returns array with signature", async () => {
|
|
1352
|
+
const mockProvider = {
|
|
1353
|
+
signIn: vi.fn().mockResolvedValue([{ signature: { toHex: () => "0xsol-array-sig" } }]),
|
|
1354
|
+
};
|
|
1355
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1356
|
+
provider: mockProvider as any,
|
|
1357
|
+
chainType: "solana",
|
|
1358
|
+
walletInfo: {} as WalletInfo,
|
|
1359
|
+
account: { address: "SolanaAddr", chainId: "" },
|
|
1360
|
+
};
|
|
1361
|
+
const result = await signInWithWallet(
|
|
1362
|
+
{ ...baseSignInData, domain: "example.com", scheme: "https" },
|
|
1363
|
+
walletOptions,
|
|
1364
|
+
);
|
|
1365
|
+
expect(result).toBe("0xsol-array-sig");
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
it("should sign in with Solana when signIn returns plain string", async () => {
|
|
1369
|
+
const mockProvider = {
|
|
1370
|
+
signIn: vi.fn().mockResolvedValue("0xsol-plain-string"),
|
|
1371
|
+
};
|
|
1372
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1373
|
+
provider: mockProvider as any,
|
|
1374
|
+
chainType: "solana",
|
|
1375
|
+
walletInfo: {} as WalletInfo,
|
|
1376
|
+
account: { address: "SolanaAddr", chainId: "" },
|
|
1377
|
+
};
|
|
1378
|
+
const result = await signInWithWallet(
|
|
1379
|
+
{ ...baseSignInData, domain: "example.com", scheme: "https" },
|
|
1380
|
+
walletOptions,
|
|
1381
|
+
);
|
|
1382
|
+
expect(result).toBe("0xsol-plain-string");
|
|
1383
|
+
});
|
|
1384
|
+
|
|
1385
|
+
it("should sign in with Solana via signMessage when no signIn method", async () => {
|
|
1386
|
+
const mockProvider = {
|
|
1387
|
+
signMessage: vi.fn().mockResolvedValue({ signature: { toHex: () => "0xfallback-sig" } }),
|
|
1388
|
+
};
|
|
1389
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1390
|
+
provider: mockProvider as any,
|
|
1391
|
+
chainType: "solana",
|
|
1392
|
+
walletInfo: {} as WalletInfo,
|
|
1393
|
+
account: { address: "SolanaAddr", chainId: "" },
|
|
1394
|
+
};
|
|
1395
|
+
const result = await signInWithWallet(
|
|
1396
|
+
{ ...baseSignInData, domain: "example.com", scheme: "https", nonce: "n1" },
|
|
1397
|
+
walletOptions,
|
|
1398
|
+
);
|
|
1399
|
+
expect(result).toBe("0xfallback-sig");
|
|
1400
|
+
expect(mockProvider.signMessage).toHaveBeenCalled();
|
|
1401
|
+
});
|
|
1402
|
+
|
|
1403
|
+
it("should rethrow when Solana signIn throws", async () => {
|
|
1404
|
+
const mockProvider = {
|
|
1405
|
+
signIn: vi.fn().mockRejectedValue(new Error("User rejected")),
|
|
1406
|
+
};
|
|
1407
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1408
|
+
provider: mockProvider as any,
|
|
1409
|
+
chainType: "solana",
|
|
1410
|
+
walletInfo: {} as WalletInfo,
|
|
1411
|
+
account: { address: "SolanaAddr", chainId: "" },
|
|
1412
|
+
};
|
|
1413
|
+
await expect(
|
|
1414
|
+
signInWithWallet(
|
|
1415
|
+
{ ...baseSignInData, domain: "example.com", scheme: "https" },
|
|
1416
|
+
walletOptions,
|
|
1417
|
+
),
|
|
1418
|
+
).rejects.toThrow("User rejected");
|
|
1419
|
+
});
|
|
1420
|
+
|
|
1421
|
+
it("should sign in with Dogecoin wallet", async () => {
|
|
1422
|
+
const mockProvider = {
|
|
1423
|
+
requestSignedMessage: vi.fn().mockResolvedValue({ signedMessage: "0xdogecoin-sig" }),
|
|
1424
|
+
};
|
|
1425
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1426
|
+
provider: mockProvider as any,
|
|
1427
|
+
chainType: "dogecoin",
|
|
1428
|
+
walletInfo: {} as WalletInfo,
|
|
1429
|
+
account: { address: "DogeAddr", chainId: "" },
|
|
1430
|
+
};
|
|
1431
|
+
const result = await signInWithWallet(
|
|
1432
|
+
{ ...baseSignInData, domain: "example.com", scheme: "https" },
|
|
1433
|
+
walletOptions,
|
|
1434
|
+
);
|
|
1435
|
+
expect(result).toBe("0xdogecoin-sig");
|
|
1436
|
+
});
|
|
1437
|
+
|
|
1438
|
+
it("should throw for unsupported chain type", async () => {
|
|
1439
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1440
|
+
provider: { request: vi.fn() } as any,
|
|
1441
|
+
chainType: "bitcoin" as any,
|
|
1442
|
+
walletInfo: {} as WalletInfo,
|
|
1443
|
+
account: { address: "addr", chainId: "" },
|
|
1444
|
+
};
|
|
1445
|
+
await expect(signInWithWallet(baseSignInData as any, walletOptions)).rejects.toThrow(
|
|
1446
|
+
"signInWithWallet not supported in bitcoin",
|
|
1447
|
+
);
|
|
1448
|
+
});
|
|
1449
|
+
|
|
1450
|
+
it("should throw error when provider is missing", async () => {
|
|
1451
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1452
|
+
provider: undefined as any,
|
|
1453
|
+
chainType: "evm",
|
|
1454
|
+
walletInfo: {} as WalletInfo,
|
|
1455
|
+
account: {
|
|
1456
|
+
address: "0x123",
|
|
1457
|
+
chainId: "0x1",
|
|
1458
|
+
},
|
|
1459
|
+
};
|
|
1460
|
+
|
|
1461
|
+
await expect(signInWithWallet({} as any, walletOptions)).rejects.toThrow("Provider is required");
|
|
1462
|
+
});
|
|
1463
|
+
});
|
|
1464
|
+
|
|
1465
|
+
describe("signMessage", () => {
|
|
1466
|
+
it("should sign message for EVM", async () => {
|
|
1467
|
+
const mockProvider = {
|
|
1468
|
+
request: vi.fn().mockResolvedValue("0xsignature"),
|
|
1469
|
+
};
|
|
1470
|
+
|
|
1471
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1472
|
+
provider: mockProvider as any,
|
|
1473
|
+
chainType: "evm",
|
|
1474
|
+
walletInfo: {} as WalletInfo,
|
|
1475
|
+
account: {
|
|
1476
|
+
address: "0x123",
|
|
1477
|
+
chainId: "0x1",
|
|
1478
|
+
},
|
|
1479
|
+
};
|
|
1480
|
+
|
|
1481
|
+
const result = await signMessage({ message: "test message" }, walletOptions);
|
|
1482
|
+
expect(result).toBe("0xsignature");
|
|
1483
|
+
expect(mockProvider.request).toHaveBeenCalledWith({
|
|
1484
|
+
method: "personal_sign",
|
|
1485
|
+
params: ["test message", "0x123"],
|
|
1486
|
+
});
|
|
1487
|
+
});
|
|
1488
|
+
|
|
1489
|
+
it("should schedule delayed mobile wallet open for EVM when isMobile and not installed", async () => {
|
|
1490
|
+
vi.useFakeTimers();
|
|
1491
|
+
mockIsMobile.mockReturnValueOnce(true);
|
|
1492
|
+
const mockGetDeeplink = vi.fn().mockResolvedValue("myapp://sign");
|
|
1493
|
+
const mockProvider = {
|
|
1494
|
+
request: vi.fn().mockResolvedValue("0xsignature"),
|
|
1495
|
+
};
|
|
1496
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1497
|
+
provider: mockProvider as any,
|
|
1498
|
+
chainType: "evm",
|
|
1499
|
+
isInstalled: false,
|
|
1500
|
+
walletInfo: { uuid: "phantom", mobile: { getDeeplink: mockGetDeeplink } } as WalletInfo,
|
|
1501
|
+
account: { address: "0x123", chainId: "0x1" },
|
|
1502
|
+
};
|
|
1503
|
+
global.window = { ...global.window, location: { href: "https://example.com" } } as any;
|
|
1504
|
+
|
|
1505
|
+
const resultPromise = signMessage({ message: "test" }, walletOptions);
|
|
1506
|
+
await vi.advanceTimersByTimeAsync(350);
|
|
1507
|
+
const result = await resultPromise;
|
|
1508
|
+
expect(result).toBe("0xsignature");
|
|
1509
|
+
expect(mockGetDeeplink).toHaveBeenCalledWith("https://example.com", 1);
|
|
1510
|
+
expect(mockOpenUri).toHaveBeenCalledWith("myapp://sign");
|
|
1511
|
+
vi.useRealTimers();
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
it("should not open URI when mobile getDeeplink returns null", async () => {
|
|
1515
|
+
mockIsMobile.mockReturnValueOnce(true);
|
|
1516
|
+
const mockProvider = {
|
|
1517
|
+
request: vi.fn().mockResolvedValue("0xsig"),
|
|
1518
|
+
};
|
|
1519
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1520
|
+
provider: mockProvider as any,
|
|
1521
|
+
chainType: "evm",
|
|
1522
|
+
isInstalled: false,
|
|
1523
|
+
walletInfo: { uuid: "phantom", mobile: { getDeeplink: vi.fn().mockResolvedValue(null) } } as WalletInfo,
|
|
1524
|
+
account: { address: "0x123", chainId: "0x1" },
|
|
1525
|
+
};
|
|
1526
|
+
const result = await signMessage({ message: "test" }, walletOptions);
|
|
1527
|
+
expect(result).toBe("0xsig");
|
|
1528
|
+
expect(mockOpenUri).not.toHaveBeenCalled();
|
|
1529
|
+
});
|
|
1530
|
+
|
|
1531
|
+
it("should log error when mobile getDeeplink rejects", async () => {
|
|
1532
|
+
mockIsMobile.mockReturnValueOnce(true);
|
|
1533
|
+
const err = new Error("getDeeplink failed");
|
|
1534
|
+
const mockProvider = {
|
|
1535
|
+
request: vi.fn().mockResolvedValue("0xsig"),
|
|
1536
|
+
};
|
|
1537
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1538
|
+
provider: mockProvider as any,
|
|
1539
|
+
chainType: "evm",
|
|
1540
|
+
isInstalled: false,
|
|
1541
|
+
walletInfo: { uuid: "phantom", mobile: { getDeeplink: vi.fn().mockRejectedValue(err) } } as WalletInfo,
|
|
1542
|
+
account: { address: "0x123", chainId: "0x1" },
|
|
1543
|
+
};
|
|
1544
|
+
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
1545
|
+
const result = await signMessage({ message: "test" }, walletOptions);
|
|
1546
|
+
expect(result).toBe("0xsig");
|
|
1547
|
+
await vi.waitFor(() => {
|
|
1548
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith("Failed to schedule mobile wallet open:", err);
|
|
1549
|
+
});
|
|
1550
|
+
consoleErrorSpy.mockRestore();
|
|
1551
|
+
});
|
|
1552
|
+
|
|
1553
|
+
it("should sign message for Solana via signMessage", async () => {
|
|
1554
|
+
const mockProvider = {
|
|
1555
|
+
signMessage: vi.fn().mockResolvedValue({ signature: { toHex: () => "0xsol-sig" } }),
|
|
1556
|
+
};
|
|
1557
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1558
|
+
provider: mockProvider as any,
|
|
1559
|
+
chainType: "solana",
|
|
1560
|
+
walletInfo: {} as WalletInfo,
|
|
1561
|
+
account: { address: "SolanaAddr", chainId: "" },
|
|
1562
|
+
};
|
|
1563
|
+
const result = await signMessage({ message: "hello" }, walletOptions);
|
|
1564
|
+
expect(result).toBe("0xsol-sig");
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
it("should sign message for Solana via signMessage returning string", async () => {
|
|
1568
|
+
const mockProvider = {
|
|
1569
|
+
signMessage: vi.fn().mockResolvedValue("0xsol-string-sig"),
|
|
1570
|
+
};
|
|
1571
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1572
|
+
provider: mockProvider as any,
|
|
1573
|
+
chainType: "solana",
|
|
1574
|
+
walletInfo: {} as WalletInfo,
|
|
1575
|
+
account: { address: "SolanaAddr", chainId: "" },
|
|
1576
|
+
};
|
|
1577
|
+
const result = await signMessage({ message: "hello" }, walletOptions);
|
|
1578
|
+
expect(result).toBe("0xsol-string-sig");
|
|
1579
|
+
});
|
|
1580
|
+
|
|
1581
|
+
it("should sign message for Solana via solana:signMessage", async () => {
|
|
1582
|
+
const mockProvider = {
|
|
1583
|
+
"solana:signMessage": {
|
|
1584
|
+
signMessage: vi.fn().mockResolvedValue([{ signature: { toHex: () => "0xsol-sig2" } }]),
|
|
1585
|
+
},
|
|
1586
|
+
};
|
|
1587
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1588
|
+
provider: mockProvider as any,
|
|
1589
|
+
chainType: "solana",
|
|
1590
|
+
walletInfo: {} as WalletInfo,
|
|
1591
|
+
account: { address: "SolanaAddr", chainId: "" },
|
|
1592
|
+
};
|
|
1593
|
+
const result = await signMessage({ message: "hello" }, walletOptions);
|
|
1594
|
+
expect(result).toBe("0xsol-sig2");
|
|
1595
|
+
});
|
|
1596
|
+
|
|
1597
|
+
it("should throw when Solana has no signMessage support", async () => {
|
|
1598
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1599
|
+
provider: {} as any,
|
|
1600
|
+
chainType: "solana",
|
|
1601
|
+
walletInfo: {} as WalletInfo,
|
|
1602
|
+
account: { address: "SolanaAddr", chainId: "" },
|
|
1603
|
+
};
|
|
1604
|
+
await expect(signMessage({ message: "hello" }, walletOptions)).rejects.toThrow(
|
|
1605
|
+
"signMessage solana not supported",
|
|
1606
|
+
);
|
|
1607
|
+
});
|
|
1608
|
+
|
|
1609
|
+
it("should sign message for Aptos via signMessage (signature with toHex)", async () => {
|
|
1610
|
+
const mockProvider = {
|
|
1611
|
+
signMessage: vi.fn().mockResolvedValue({
|
|
1612
|
+
args: { signature: { data: { data: { toHex: () => "0xaptos-sig" } } } },
|
|
1613
|
+
}),
|
|
1614
|
+
};
|
|
1615
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1616
|
+
provider: mockProvider as any,
|
|
1617
|
+
chainType: "aptos",
|
|
1618
|
+
walletInfo: {} as WalletInfo,
|
|
1619
|
+
account: { address: "0xaptos", chainId: "" },
|
|
1620
|
+
};
|
|
1621
|
+
const result = await signMessage({ message: "hello", nonce: "n1" }, walletOptions);
|
|
1622
|
+
expect(result).toBe("0xaptos-sig");
|
|
1623
|
+
});
|
|
1624
|
+
|
|
1625
|
+
it("should sign message for Aptos (res.signature string like okx)", async () => {
|
|
1626
|
+
const mockProvider = {
|
|
1627
|
+
signMessage: vi.fn().mockResolvedValue({ signature: "0xokx-string-sig" }),
|
|
1628
|
+
};
|
|
1629
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1630
|
+
provider: mockProvider as any,
|
|
1631
|
+
chainType: "aptos",
|
|
1632
|
+
walletInfo: {} as WalletInfo,
|
|
1633
|
+
account: { address: "0xaptos", chainId: "" },
|
|
1634
|
+
};
|
|
1635
|
+
const result = await signMessage({ message: "hello", nonce: "n1" }, walletOptions);
|
|
1636
|
+
expect(result).toBe("0xokx-string-sig");
|
|
1637
|
+
});
|
|
1638
|
+
|
|
1639
|
+
it("should sign message for Aptos (gate wallet style data.data object)", async () => {
|
|
1640
|
+
const mockProvider = {
|
|
1641
|
+
signMessage: vi.fn().mockResolvedValue({
|
|
1642
|
+
args: { signature: { data: { data: { 0: 97, 1: 112, 2: 116, 3: 111, 4: 115 } } } },
|
|
1643
|
+
}),
|
|
1644
|
+
};
|
|
1645
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1646
|
+
provider: mockProvider as any,
|
|
1647
|
+
chainType: "aptos",
|
|
1648
|
+
walletInfo: {} as WalletInfo,
|
|
1649
|
+
account: { address: "0xaptos", chainId: "" },
|
|
1650
|
+
};
|
|
1651
|
+
const result = await signMessage({ message: "hello", nonce: "n1" }, walletOptions);
|
|
1652
|
+
expect(result).toBe("6170746f73");
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
it("should sign message for Aptos (args.signature object fallback)", async () => {
|
|
1656
|
+
const mockProvider = {
|
|
1657
|
+
signMessage: vi.fn().mockResolvedValue({
|
|
1658
|
+
args: { signature: { otherKey: "0xfallback-hex" } },
|
|
1659
|
+
}),
|
|
1660
|
+
};
|
|
1661
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1662
|
+
provider: mockProvider as any,
|
|
1663
|
+
chainType: "aptos",
|
|
1664
|
+
walletInfo: {} as WalletInfo,
|
|
1665
|
+
account: { address: "0xaptos", chainId: "" },
|
|
1666
|
+
};
|
|
1667
|
+
const result = await signMessage({ message: "hello", nonce: "n1" }, walletOptions);
|
|
1668
|
+
expect(result).toBeDefined();
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1671
|
+
it("should sign message for Aptos via aptos:signMessage", async () => {
|
|
1672
|
+
const mockProvider = {
|
|
1673
|
+
"aptos:signMessage": {
|
|
1674
|
+
signMessage: vi.fn().mockResolvedValue({
|
|
1675
|
+
args: { signature: { signature: Buffer.from("aptos") } },
|
|
1676
|
+
}),
|
|
1677
|
+
},
|
|
1678
|
+
};
|
|
1679
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1680
|
+
provider: mockProvider as any,
|
|
1681
|
+
chainType: "aptos",
|
|
1682
|
+
walletInfo: {} as WalletInfo,
|
|
1683
|
+
account: { address: "0xaptos", chainId: "" },
|
|
1684
|
+
};
|
|
1685
|
+
const result = await signMessage({ message: "hello", nonce: "n1" }, walletOptions);
|
|
1686
|
+
expect(result).toBeDefined();
|
|
1687
|
+
});
|
|
1688
|
+
|
|
1689
|
+
it("should throw when Aptos has no signMessage support", async () => {
|
|
1690
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1691
|
+
provider: {} as any,
|
|
1692
|
+
chainType: "aptos",
|
|
1693
|
+
walletInfo: {} as WalletInfo,
|
|
1694
|
+
account: { address: "0xaptos", chainId: "" },
|
|
1695
|
+
};
|
|
1696
|
+
await expect(signMessage({ message: "hello", nonce: "n1" }, walletOptions)).rejects.toThrow(
|
|
1697
|
+
"signMessage aptos not supported",
|
|
1698
|
+
);
|
|
1699
|
+
});
|
|
1700
|
+
|
|
1701
|
+
it("should sign message for Dogecoin", async () => {
|
|
1702
|
+
const mockProvider = {
|
|
1703
|
+
requestSignedMessage: vi.fn().mockResolvedValue({ signedMessage: "doge-sig" }),
|
|
1704
|
+
};
|
|
1705
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1706
|
+
provider: mockProvider as any,
|
|
1707
|
+
chainType: "dogecoin",
|
|
1708
|
+
walletInfo: {} as WalletInfo,
|
|
1709
|
+
account: { address: "DogeAddr", chainId: "" },
|
|
1710
|
+
};
|
|
1711
|
+
const result = await signMessage({ message: "hello", type: "text" }, walletOptions);
|
|
1712
|
+
expect(result).toBe("doge-sig");
|
|
1713
|
+
});
|
|
1714
|
+
|
|
1715
|
+
it("should return empty string when Dogecoin requestSignedMessage returns no signedMessage", async () => {
|
|
1716
|
+
const mockProvider = {
|
|
1717
|
+
requestSignedMessage: vi.fn().mockResolvedValue({}),
|
|
1718
|
+
};
|
|
1719
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1720
|
+
provider: mockProvider as any,
|
|
1721
|
+
chainType: "dogecoin",
|
|
1722
|
+
walletInfo: {} as WalletInfo,
|
|
1723
|
+
account: { address: "DogeAddr", chainId: "" },
|
|
1724
|
+
};
|
|
1725
|
+
const result = await signMessage({ message: "hello", type: "text" }, walletOptions);
|
|
1726
|
+
expect(result).toBe("");
|
|
1727
|
+
});
|
|
1728
|
+
|
|
1729
|
+
it("should throw for unsupported chain type", async () => {
|
|
1730
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1731
|
+
provider: { request: vi.fn() } as any,
|
|
1732
|
+
chainType: "bitcoin" as any,
|
|
1733
|
+
walletInfo: {} as WalletInfo,
|
|
1734
|
+
account: { address: "addr", chainId: "" },
|
|
1735
|
+
};
|
|
1736
|
+
await expect(signMessage({ message: "hi" }, walletOptions)).rejects.toThrow(
|
|
1737
|
+
"signMessage not supported in bitcoin",
|
|
1738
|
+
);
|
|
1739
|
+
});
|
|
1740
|
+
|
|
1741
|
+
it("should throw error when provider is missing", async () => {
|
|
1742
|
+
const walletOptions: WalletOptions & { isInstalled?: boolean } = {
|
|
1743
|
+
provider: undefined as any,
|
|
1744
|
+
chainType: "evm",
|
|
1745
|
+
walletInfo: {} as WalletInfo,
|
|
1746
|
+
account: {
|
|
1747
|
+
address: "0x123",
|
|
1748
|
+
chainId: "0x1",
|
|
1749
|
+
},
|
|
1750
|
+
};
|
|
1751
|
+
|
|
1752
|
+
await expect(signMessage({ message: "test" }, walletOptions)).rejects.toThrow("Provider is required");
|
|
1753
|
+
});
|
|
1754
|
+
});
|
|
1755
|
+
});
|