@tomo-inc/chains-service 0.0.23 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/index.cjs +42 -24
  2. package/dist/index.d.cts +3 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.js +43 -25
  5. package/package.json +3 -2
  6. package/project.json +1 -1
  7. package/src/__tests__/config.test.ts +46 -0
  8. package/src/__tests__/dogecoin-utils.test.ts +147 -0
  9. package/src/__tests__/evm-utils.test.ts +133 -0
  10. package/src/__tests__/index.test.ts +40 -0
  11. package/src/__tests__/services.test.ts +285 -0
  12. package/src/__tests__/solana-utils.test.ts +131 -0
  13. package/src/__tests__/utils.test.ts +52 -0
  14. package/src/__tests__/wallet.test.ts +350 -0
  15. package/src/api/__tests__/base.test.ts +146 -0
  16. package/src/api/__tests__/index.test.ts +51 -0
  17. package/src/api/__tests__/network.test.ts +153 -0
  18. package/src/api/__tests__/token.test.ts +231 -2
  19. package/src/api/__tests__/transaction.test.ts +121 -6
  20. package/src/api/__tests__/user.test.ts +237 -3
  21. package/src/api/__tests__/wallet.test.ts +174 -4
  22. package/src/api/network.ts +9 -1
  23. package/src/api/utils/__tests__/index.test.ts +91 -0
  24. package/src/api/utils/__tests__/signature.test.ts +124 -0
  25. package/src/api/utils/index.ts +6 -2
  26. package/src/base/__tests__/network.test.ts +119 -0
  27. package/src/base/__tests__/service.test.ts +68 -0
  28. package/src/base/__tests__/token.test.ts +123 -0
  29. package/src/base/__tests__/transaction.test.ts +210 -0
  30. package/src/config.ts +2 -1
  31. package/src/dogecoin/__tests__/base.test.ts +76 -0
  32. package/src/dogecoin/__tests__/rpc.test.ts +465 -0
  33. package/src/dogecoin/__tests__/service-extended.test.ts +420 -0
  34. package/src/dogecoin/__tests__/utils-doge.test.ts +244 -0
  35. package/src/dogecoin/__tests__/utils-extended.test.ts +323 -0
  36. package/src/dogecoin/base.ts +1 -0
  37. package/src/dogecoin/config.ts +2 -2
  38. package/src/dogecoin/rpc.ts +10 -1
  39. package/src/dogecoin/service.ts +9 -5
  40. package/src/evm/__tests__/rpc.test.ts +132 -0
  41. package/src/evm/__tests__/service.test.ts +535 -0
  42. package/src/evm/__tests__/utils.test.ts +170 -0
  43. package/src/evm/service.ts +11 -0
  44. package/src/evm/utils.ts +2 -2
  45. package/src/solana/__tests__/service.test.ts +425 -0
  46. package/src/solana/__tests__/utils.test.ts +937 -0
  47. package/src/solana/config.ts +1 -1
  48. package/src/solana/service.ts +2 -0
  49. package/src/solana/utils.ts +2 -16
  50. package/src/utils/index.ts +1 -1
  51. package/vitest.config.ts +13 -0
@@ -0,0 +1,350 @@
1
+ import { describe, it, expect, beforeEach, vi } from "vitest";
2
+ import { TomoWallet } from "../wallet";
3
+ import { ChainTypeEnum } from "@tomo-inc/wallet-utils";
4
+ import { SIGN_CONFIG } from "../api/__tests__/config";
5
+
6
+ // Mock tomoPublicApiService
7
+ vi.mock("../api", () => {
8
+ return {
9
+ tomoPublicApiService: vi.fn(() => ({
10
+ tokenAPIs: {},
11
+ transactionAPIs: {},
12
+ networkAPIs: {},
13
+ })),
14
+ };
15
+ });
16
+
17
+ // Mock BaseService after mocking api
18
+ vi.mock("../base/service", async () => {
19
+ const actual = await vi.importActual("../base/service");
20
+ return {
21
+ ...actual,
22
+ BaseService: class BaseService {
23
+ public networks: any;
24
+ public tokens: any;
25
+ public transactions: any;
26
+ public isDappConnected: boolean;
27
+ public approveParams: any;
28
+ public accountInfo: any;
29
+
30
+ constructor(_tomoAppInfo: any, _accountInfo?: any) {
31
+ this.networks = {
32
+ getNetworks: vi.fn(),
33
+ setChainType: vi.fn(),
34
+ getNetworkByChainId: vi.fn(),
35
+ getNetworkByChainIndex: vi.fn(),
36
+ };
37
+ this.transactions = {
38
+ getTransactions: vi.fn(),
39
+ };
40
+ this.tokens = {};
41
+ this.isDappConnected = false;
42
+ this.approveParams = null;
43
+ this.accountInfo = _accountInfo || null;
44
+ }
45
+ },
46
+ };
47
+ });
48
+
49
+ describe("TomoWallet", () => {
50
+ let wallet: TomoWallet;
51
+ const mockTomoAppInfo = {
52
+ tomoStage: "dev" as const,
53
+ tomoClientId: SIGN_CONFIG.clientId || "",
54
+ };
55
+ const walletId = "test-wallet-id";
56
+
57
+ beforeEach(() => {
58
+ vi.clearAllMocks();
59
+ wallet = new TomoWallet(walletId, mockTomoAppInfo);
60
+ });
61
+
62
+ describe("constructor", () => {
63
+ it("should create a new TomoWallet instance", () => {
64
+ expect(wallet).toBeInstanceOf(TomoWallet);
65
+ });
66
+ });
67
+
68
+ describe("getInstance", () => {
69
+ it("should return a new TomoWallet instance", () => {
70
+ const instance = wallet.getInstance(walletId, mockTomoAppInfo);
71
+ expect(instance).toBeInstanceOf(TomoWallet);
72
+ });
73
+ });
74
+
75
+ describe("supportedChains", () => {
76
+ it("should return supported chains for a specific chain type", async () => {
77
+ const mockNetworks = [
78
+ { chainId: 1, name: "Ethereum", platformType: "EVM" },
79
+ { chainId: 56, name: "BSC", platformType: "EVM" },
80
+ ];
81
+ (wallet as any).networks.getNetworks = vi.fn().mockResolvedValue(mockNetworks);
82
+
83
+ const result = await wallet.supportedChains(ChainTypeEnum.EVM);
84
+ expect(result).toEqual(mockNetworks);
85
+ expect((wallet as any).networks.getNetworks).toHaveBeenCalledWith(ChainTypeEnum.EVM);
86
+ });
87
+
88
+ it("should return all chains when chainType is empty", async () => {
89
+ const mockNetworks = [
90
+ { chainId: 1, name: "Ethereum", platformType: "EVM" },
91
+ { chainId: 501, name: "Solana", platformType: "SOLANA" },
92
+ ];
93
+ (wallet as any).networks.getNetworks = vi.fn().mockResolvedValue(mockNetworks);
94
+
95
+ const result = await wallet.supportedChains();
96
+ expect(result).toEqual(mockNetworks);
97
+ expect((wallet as any).networks.getNetworks).toHaveBeenCalledWith("");
98
+ });
99
+ });
100
+
101
+ describe("getChainInfo", () => {
102
+ it("should return chain info by chainId", async () => {
103
+ const mockNetwork = { chainId: 1, name: "Ethereum", platformType: "EVM" };
104
+ (wallet as any).networks.setChainType = vi.fn();
105
+ (wallet as any).networks.getNetworkByChainId = vi.fn().mockResolvedValue(mockNetwork);
106
+
107
+ const result = await wallet.getChainInfo(ChainTypeEnum.EVM, "1");
108
+ expect(result).toEqual(mockNetwork);
109
+ expect((wallet as any).networks.setChainType).toHaveBeenCalledWith(ChainTypeEnum.EVM);
110
+ expect((wallet as any).networks.getNetworkByChainId).toHaveBeenCalledWith("1");
111
+ });
112
+ });
113
+
114
+ describe("isChainSupported", () => {
115
+ it("should return true if chain is supported", async () => {
116
+ const mockNetwork = { chainId: 1, name: "Ethereum" };
117
+ (wallet as any).networks.setChainType = vi.fn();
118
+ (wallet as any).networks.getNetworkByChainId = vi.fn().mockResolvedValue(mockNetwork);
119
+
120
+ const result = await wallet.isChainSupported(ChainTypeEnum.EVM, "1");
121
+ expect(result).toBe(true);
122
+ });
123
+
124
+ it("should return false if chain is not found", async () => {
125
+ (wallet as any).networks.setChainType = vi.fn();
126
+ (wallet as any).networks.getNetworkByChainId = vi.fn().mockResolvedValue(null);
127
+
128
+ const result = await wallet.isChainSupported(ChainTypeEnum.EVM, "999");
129
+ expect(result).toBe(false);
130
+ });
131
+
132
+ it("should return false if chainId is missing", async () => {
133
+ const mockNetwork = { name: "Ethereum" }; // no chainId
134
+ (wallet as any).networks.setChainType = vi.fn();
135
+ (wallet as any).networks.getNetworkByChainId = vi.fn().mockResolvedValue(mockNetwork);
136
+
137
+ const result = await wallet.isChainSupported(ChainTypeEnum.EVM, "1");
138
+ expect(result).toBe(false);
139
+ });
140
+ });
141
+
142
+ describe("getTransactions", () => {
143
+ it("should get transactions successfully", async () => {
144
+ const mockTransactionList = [
145
+ {
146
+ chainIndex: 100,
147
+ txHash: "0x123",
148
+ tokenInfo: {},
149
+ txTime: 1234567890,
150
+ },
151
+ ];
152
+ const mockChainInfo = {
153
+ chainId: 1,
154
+ name: "Ethereum",
155
+ icon: "icon.png",
156
+ platformType: "EVM",
157
+ nativeCurrency: { symbol: "ETH", decimals: 18 },
158
+ };
159
+
160
+ (wallet as any).networks.getNetworkByChainId = vi.fn().mockResolvedValue(mockChainInfo);
161
+ (wallet as any).networks.getNetworkByChainIndex = vi.fn().mockResolvedValue(mockChainInfo);
162
+ (wallet as any).transactions.getTransactions = vi.fn().mockResolvedValue({
163
+ cursor: "next-cursor",
164
+ transactionList: mockTransactionList,
165
+ });
166
+
167
+ const result = await wallet.getTransactions({
168
+ chainId: "1",
169
+ pageLimit: 20,
170
+ });
171
+
172
+ expect(result.cursor).toBe("next-cursor");
173
+ expect(result.transactionList).toHaveLength(1);
174
+ expect(result.transactionList[0]).toHaveProperty("chainType");
175
+ expect(result.transactionList[0]).toHaveProperty("chainInfo");
176
+ expect(result.transactionList[0]).toHaveProperty("txHash");
177
+ });
178
+
179
+ it("should throw error when walletId is missing", async () => {
180
+ const walletWithoutId = new TomoWallet("", mockTomoAppInfo);
181
+ await expect(
182
+ walletWithoutId.getTransactions({
183
+ pageLimit: 20,
184
+ }),
185
+ ).rejects.toThrow("walletId is required");
186
+ });
187
+
188
+ it("should handle transactions with typeList", async () => {
189
+ const mockTransactionList = [
190
+ {
191
+ chainIndex: 100,
192
+ txHash: "0x123",
193
+ tokenInfo: {},
194
+ txTime: 1234567890,
195
+ },
196
+ ];
197
+ const mockChainInfo = {
198
+ chainId: 1,
199
+ name: "Ethereum",
200
+ icon: "icon.png",
201
+ platformType: "EVM",
202
+ nativeCurrency: { symbol: "ETH", decimals: 18 },
203
+ };
204
+
205
+ (wallet as any).networks.getNetworkByChainId = vi.fn().mockResolvedValue(mockChainInfo);
206
+ (wallet as any).networks.getNetworkByChainIndex = vi.fn().mockResolvedValue(mockChainInfo);
207
+ (wallet as any).transactions.getTransactions = vi.fn().mockResolvedValue({
208
+ cursor: "",
209
+ transactionList: mockTransactionList,
210
+ });
211
+
212
+ const result = await wallet.getTransactions({
213
+ typeList: ["send", "receive"],
214
+ pageLimit: 20,
215
+ });
216
+
217
+ expect((wallet as any).transactions.getTransactions).toHaveBeenCalledWith(
218
+ expect.objectContaining({
219
+ typeList: "send,receive",
220
+ }),
221
+ );
222
+ });
223
+
224
+ it("should handle error in getTransactions", async () => {
225
+ const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
226
+ (wallet as any).transactions.getTransactions = vi.fn().mockRejectedValue(new Error("API Error"));
227
+
228
+ await expect(
229
+ wallet.getTransactions({
230
+ pageLimit: 20,
231
+ }),
232
+ ).rejects.toThrow();
233
+
234
+ expect(consoleErrorSpy).toHaveBeenCalled();
235
+ consoleErrorSpy.mockRestore();
236
+ });
237
+
238
+ it("should handle transactions with cursor", async () => {
239
+ const mockTransactionList = [
240
+ {
241
+ chainIndex: 100,
242
+ txHash: "0x123",
243
+ tokenInfo: {},
244
+ txTime: 1234567890,
245
+ },
246
+ ];
247
+ const mockChainInfo = {
248
+ chainId: 1,
249
+ name: "Ethereum",
250
+ icon: "icon.png",
251
+ platformType: "EVM",
252
+ nativeCurrency: { symbol: "ETH", decimals: 18 },
253
+ };
254
+
255
+ (wallet as any).networks.getNetworkByChainId = vi.fn().mockResolvedValue(mockChainInfo);
256
+ (wallet as any).networks.getNetworkByChainIndex = vi.fn().mockResolvedValue(mockChainInfo);
257
+ (wallet as any).transactions.getTransactions = vi.fn().mockResolvedValue({
258
+ cursor: "next-page",
259
+ transactionList: mockTransactionList,
260
+ });
261
+
262
+ const result = await wallet.getTransactions({
263
+ cursor: "prev-cursor",
264
+ pageLimit: 20,
265
+ });
266
+
267
+ expect((wallet as any).transactions.getTransactions).toHaveBeenCalledWith(
268
+ expect.objectContaining({
269
+ cursor: "prev-cursor",
270
+ }),
271
+ );
272
+ expect(result.cursor).toBe("next-page");
273
+ });
274
+
275
+ it("should handle transactions with tokenAddress", async () => {
276
+ const mockTransactionList = [
277
+ {
278
+ chainIndex: 100,
279
+ txHash: "0x123",
280
+ tokenInfo: {},
281
+ txTime: 1234567890,
282
+ },
283
+ ];
284
+ const mockChainInfo = {
285
+ chainId: 1,
286
+ name: "Ethereum",
287
+ icon: "icon.png",
288
+ platformType: "EVM",
289
+ nativeCurrency: { symbol: "ETH", decimals: 18 },
290
+ chainIndex: 100,
291
+ };
292
+
293
+ (wallet as any).networks.getNetworkByChainId = vi.fn().mockResolvedValue(mockChainInfo);
294
+ (wallet as any).networks.getNetworkByChainIndex = vi.fn().mockResolvedValue(mockChainInfo);
295
+ (wallet as any).transactions.getTransactions = vi.fn().mockResolvedValue({
296
+ cursor: "",
297
+ transactionList: mockTransactionList,
298
+ });
299
+
300
+ const result = await wallet.getTransactions({
301
+ tokenAddress: "0xtoken123",
302
+ chainId: "1",
303
+ pageLimit: 20,
304
+ });
305
+
306
+ expect((wallet as any).transactions.getTransactions).toHaveBeenCalledWith(
307
+ expect.objectContaining({
308
+ tokenAddress: "0xtoken123",
309
+ chainIndex: 100,
310
+ }),
311
+ );
312
+ expect(result.transactionList).toHaveLength(1);
313
+ });
314
+
315
+ it("should handle getTransactions without chainId", async () => {
316
+ const mockTransactionList = [
317
+ {
318
+ chainIndex: 100,
319
+ txHash: "0x123",
320
+ tokenInfo: {},
321
+ txTime: 1234567890,
322
+ },
323
+ ];
324
+ const mockChainInfo = {
325
+ chainId: 1,
326
+ name: "Ethereum",
327
+ icon: "icon.png",
328
+ platformType: "EVM",
329
+ nativeCurrency: { symbol: "ETH", decimals: 18 },
330
+ };
331
+
332
+ (wallet as any).networks.getNetworkByChainIndex = vi.fn().mockResolvedValue(mockChainInfo);
333
+ (wallet as any).transactions.getTransactions = vi.fn().mockResolvedValue({
334
+ cursor: "",
335
+ transactionList: mockTransactionList,
336
+ });
337
+
338
+ const result = await wallet.getTransactions({
339
+ pageLimit: 20,
340
+ });
341
+
342
+ expect((wallet as any).transactions.getTransactions).toHaveBeenCalledWith(
343
+ expect.not.objectContaining({
344
+ chainIndex: expect.anything(),
345
+ }),
346
+ );
347
+ expect(result.transactionList).toHaveLength(1);
348
+ });
349
+ });
350
+ });
@@ -0,0 +1,146 @@
1
+ import { describe, it, expect, beforeEach, vi } from "vitest";
2
+ import { BasePublicService, BasePrivateService } from "../base";
3
+ import { CONFIG, SIGN_CONFIG } from "./config";
4
+
5
+ const requestUseHandlers: ((params: any) => any)[] = [];
6
+
7
+ vi.mock("axios", () => ({
8
+ default: {
9
+ create: vi.fn(() => {
10
+ const useFn = vi.fn((handler: (params: any) => any) => {
11
+ requestUseHandlers.push(handler);
12
+ });
13
+ return {
14
+ interceptors: {
15
+ request: { use: useFn },
16
+ response: { use: vi.fn() },
17
+ },
18
+ };
19
+ }),
20
+ },
21
+ }));
22
+
23
+ const signRequestMock = vi.fn((params: any) => params);
24
+ vi.mock("../utils", () => ({
25
+ signRequest: (...args: any[]) => signRequestMock(...args),
26
+ }));
27
+
28
+ describe("BasePublicService", () => {
29
+ const mockTomoAppInfo = {
30
+ tomoStage: "dev" as const,
31
+ tomoClientId: SIGN_CONFIG.clientId || "",
32
+ };
33
+
34
+ beforeEach(() => {
35
+ vi.clearAllMocks();
36
+ requestUseHandlers.length = 0;
37
+ });
38
+
39
+ describe("constructor", () => {
40
+ it("should create BasePublicService instance", () => {
41
+ const service = new BasePublicService(CONFIG, mockTomoAppInfo);
42
+ expect(service).toBeInstanceOf(BasePublicService);
43
+ expect(service.tokenApi).toBeDefined();
44
+ expect(service.txApi).toBeDefined();
45
+ expect(service.walletApi).toBeDefined();
46
+ });
47
+
48
+ it("should set up interceptors for all APIs", () => {
49
+ const service = new BasePublicService(CONFIG, mockTomoAppInfo);
50
+ expect(service.tokenApi.interceptors.request.use).toHaveBeenCalled();
51
+ expect(service.txApi.interceptors.request.use).toHaveBeenCalled();
52
+ expect(service.walletApi.interceptors.request.use).toHaveBeenCalled();
53
+ });
54
+
55
+ it("should run request interceptor and set baseURL per api", () => {
56
+ new BasePublicService(CONFIG, mockTomoAppInfo);
57
+ expect(requestUseHandlers.length).toBe(3);
58
+ const bases = [CONFIG.tokenBaseUrl, CONFIG.txBaseUrl, CONFIG.walletBaseUrl];
59
+ requestUseHandlers.forEach((handler, i) => {
60
+ const params = { method: "GET", url: "/test" };
61
+ const result = handler(params);
62
+ expect(params.baseURL).toBe(bases[i]);
63
+ expect(signRequestMock).toHaveBeenCalledWith(params, mockTomoAppInfo.tomoClientId, mockTomoAppInfo);
64
+ expect(result).toBe(params);
65
+ });
66
+ });
67
+
68
+ it("getSignConfig should return tomoAppInfo", () => {
69
+ const service = new BasePublicService(CONFIG, mockTomoAppInfo);
70
+ const config = (service as any).getSignConfig();
71
+ expect(config).toBe(mockTomoAppInfo);
72
+ });
73
+ });
74
+ });
75
+
76
+ describe("BasePrivateService", () => {
77
+ const mockTomoAppInfo = {
78
+ tomoStage: "dev" as const,
79
+ tomoClientId: SIGN_CONFIG.clientId || "",
80
+ jwtToken: "test-jwt-token",
81
+ };
82
+
83
+ beforeEach(() => {
84
+ vi.clearAllMocks();
85
+ requestUseHandlers.length = 0;
86
+ });
87
+
88
+ describe("constructor", () => {
89
+ it("should create BasePrivateService instance", () => {
90
+ const service = new BasePrivateService(
91
+ {
92
+ userBaseUrl: CONFIG.userBaseUrl,
93
+ },
94
+ mockTomoAppInfo,
95
+ );
96
+ expect(service).toBeInstanceOf(BasePrivateService);
97
+ expect(service.userApi).toBeDefined();
98
+ });
99
+
100
+ it("should set up interceptors for userApi", () => {
101
+ const service = new BasePrivateService(
102
+ {
103
+ userBaseUrl: CONFIG.userBaseUrl,
104
+ },
105
+ mockTomoAppInfo,
106
+ );
107
+ expect(service.userApi.interceptors.request.use).toHaveBeenCalled();
108
+ });
109
+
110
+ it("should run request interceptor and set baseURL for userApi", () => {
111
+ requestUseHandlers.length = 0;
112
+ const privateConfig = { userBaseUrl: CONFIG.userBaseUrl };
113
+ new BasePrivateService(privateConfig, mockTomoAppInfo);
114
+ expect(requestUseHandlers.length).toBe(1);
115
+ const params = { method: "POST", url: "/api/user" };
116
+ requestUseHandlers[0](params);
117
+ expect(params.baseURL).toBe(privateConfig.userBaseUrl);
118
+ expect(signRequestMock).toHaveBeenCalledWith(params, mockTomoAppInfo.tomoClientId, mockTomoAppInfo.jwtToken);
119
+ });
120
+
121
+ it("should use empty jwtToken when not provided", () => {
122
+ const service = new BasePrivateService(
123
+ {
124
+ userBaseUrl: CONFIG.userBaseUrl,
125
+ },
126
+ {
127
+ tomoStage: "dev" as const,
128
+ tomoClientId: SIGN_CONFIG.clientId || "",
129
+ },
130
+ );
131
+ expect(service.getJwtToken()).toBe("");
132
+ });
133
+ });
134
+
135
+ describe("getJwtToken", () => {
136
+ it("should return jwtToken", () => {
137
+ const service = new BasePrivateService(
138
+ {
139
+ userBaseUrl: CONFIG.userBaseUrl,
140
+ },
141
+ mockTomoAppInfo,
142
+ );
143
+ expect(service.getJwtToken()).toBe("test-jwt-token");
144
+ });
145
+ });
146
+ });
@@ -0,0 +1,51 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { tomoPublicApiService, tomoPrivateApiService } from "../index";
3
+ import { CONFIG, SIGN_CONFIG } from "./config";
4
+
5
+ vi.mock("../network", () => ({
6
+ NetworkAPIs: { getInstance: vi.fn(() => ({})) },
7
+ }));
8
+ vi.mock("../token", () => ({
9
+ TokenAPIs: { getInstance: vi.fn(() => ({})) },
10
+ }));
11
+ vi.mock("../transaction", () => ({
12
+ TransactionAPIs: { getInstance: vi.fn(() => ({})) },
13
+ }));
14
+ vi.mock("../wallet", () => ({
15
+ WalletAPIs: { getInstance: vi.fn(() => ({})) },
16
+ }));
17
+ vi.mock("../user", () => ({
18
+ UserAPIs: { getInstance: vi.fn(() => ({})) },
19
+ }));
20
+
21
+ describe("api index", () => {
22
+ const publicTomoAppInfo = {
23
+ tomoStage: "dev" as const,
24
+ tomoClientId: SIGN_CONFIG.clientId || "",
25
+ };
26
+ const privateTomoAppInfo = {
27
+ ...publicTomoAppInfo,
28
+ jwtToken: "test-jwt",
29
+ };
30
+
31
+ beforeEach(() => {
32
+ vi.clearAllMocks();
33
+ });
34
+
35
+ it("tomoPublicApiService should return tokenAPIs, transactionAPIs, networkAPIs, walletAPIs", () => {
36
+ const result = tomoPublicApiService(CONFIG, publicTomoAppInfo);
37
+ expect(result).toHaveProperty("tokenAPIs");
38
+ expect(result).toHaveProperty("transactionAPIs");
39
+ expect(result).toHaveProperty("networkAPIs");
40
+ expect(result).toHaveProperty("walletAPIs");
41
+ });
42
+
43
+ it("tomoPrivateApiService should return userAPIs", () => {
44
+ const result = tomoPrivateApiService(
45
+ { userBaseUrl: CONFIG.userBaseUrl },
46
+ privateTomoAppInfo,
47
+ );
48
+ expect(result).toHaveProperty("userAPIs");
49
+ expect(result.userAPIs).toBeDefined();
50
+ });
51
+ });
@@ -0,0 +1,153 @@
1
+ import { describe, it, expect, beforeEach, vi } from "vitest";
2
+ import { NetworkAPIs } from "../network";
3
+ import { ChainTypeEnum, SupportedChainTypes } from "@tomo-inc/wallet-utils";
4
+ import { CONFIG, SIGN_CONFIG } from "./config";
5
+ import * as walletUtils from "@tomo-inc/wallet-utils";
6
+
7
+ // Mock cache
8
+ vi.mock("@tomo-inc/wallet-utils", async (importOriginal) => {
9
+ const actual = await importOriginal<typeof import("@tomo-inc/wallet-utils")>();
10
+ return {
11
+ ...actual,
12
+ cache: {
13
+ get: vi.fn(),
14
+ set: vi.fn(),
15
+ },
16
+ };
17
+ });
18
+
19
+ describe("NetworkAPIs", () => {
20
+ const mockTomoAppInfo = {
21
+ tomoStage: "dev" as const,
22
+ tomoClientId: SIGN_CONFIG.clientId || "",
23
+ };
24
+
25
+ beforeEach(() => {
26
+ vi.clearAllMocks();
27
+ // Reset singleton instance
28
+ (NetworkAPIs as any).instance = undefined;
29
+ });
30
+
31
+ describe("getInstance", () => {
32
+ it("should return singleton instance", () => {
33
+ const instance1 = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
34
+ const instance2 = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
35
+ expect(instance1).toBe(instance2);
36
+ });
37
+ });
38
+
39
+ describe("getCacheId", () => {
40
+ it("should return cache id for chain type", () => {
41
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
42
+ const cacheId = instance.getCacheId(ChainTypeEnum.EVM);
43
+ expect(cacheId).toBe(`tomo-${ChainTypeEnum.EVM}-currentChainId`);
44
+ });
45
+
46
+ it("should return cache id for empty chain type", () => {
47
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
48
+ const cacheId = instance.getCacheId("");
49
+ expect(cacheId).toBe("tomo--currentChainId");
50
+ });
51
+ });
52
+
53
+ describe("getAllNetworks", () => {
54
+ it("should return all networks when chainType is empty", () => {
55
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
56
+ const networks = instance.getAllNetworks("");
57
+ expect(networks.length).toBeGreaterThan(0);
58
+ });
59
+
60
+ it("should return filtered networks for specific chain type", () => {
61
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
62
+ const networks = instance.getAllNetworks(ChainTypeEnum.EVM);
63
+ expect(networks.every((n) => n.platformType === "EVM")).toBe(true);
64
+ });
65
+
66
+ it("should return filtered networks for DOGE chain type", () => {
67
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
68
+ const networks = instance.getAllNetworks(ChainTypeEnum.DOGECOIN);
69
+ expect(networks.every((n) => n.platformType === "DOGE")).toBe(true);
70
+ });
71
+ });
72
+
73
+ describe("getCurrentNetwork", () => {
74
+ it("should return cached chainId if available", async () => {
75
+ const cache = vi.mocked(walletUtils.cache);
76
+ cache.get.mockReturnValue("56");
77
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
78
+ const chainId = await instance.getCurrentNetwork(ChainTypeEnum.EVM);
79
+ expect(chainId).toBe("56");
80
+ });
81
+
82
+ it("should return default chainId from SupportedChainTypes if cache is empty", async () => {
83
+ const cache = vi.mocked(walletUtils.cache);
84
+ cache.get.mockReturnValue(null);
85
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
86
+ const chainId = await instance.getCurrentNetwork(ChainTypeEnum.EVM);
87
+ expect(chainId).toBe(SupportedChainTypes[ChainTypeEnum.EVM]?.chainId || "1");
88
+ });
89
+
90
+ it("should throw error for unsupported chain type", async () => {
91
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
92
+ await expect(instance.getCurrentNetwork("UNSUPPORTED" as ChainTypeEnum)).rejects.toThrow(
93
+ "Chain UNSUPPORTED is not supported",
94
+ );
95
+ });
96
+ });
97
+
98
+ describe("setCurrentNetwork", () => {
99
+ it("should set current network and cache it", async () => {
100
+ const cache = vi.mocked(walletUtils.cache);
101
+ cache.set.mockImplementation(() => true);
102
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
103
+ await instance.setCurrentNetwork(ChainTypeEnum.EVM, "56");
104
+ expect(instance.currentChainId).toBe("56");
105
+ expect(cache.set).toHaveBeenCalled();
106
+ });
107
+
108
+ it("should set current network for empty chainType", async () => {
109
+ const cache = vi.mocked(walletUtils.cache);
110
+ cache.set.mockImplementation(() => true);
111
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
112
+ await instance.setCurrentNetwork("", "1");
113
+ expect(instance.currentChainId).toBe("1");
114
+ });
115
+
116
+ it("should throw error for unsupported chain type", async () => {
117
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
118
+ await expect(instance.setCurrentNetwork("UNSUPPORTED" as ChainTypeEnum, "1")).rejects.toThrow(
119
+ "Chain UNSUPPORTED is not supported",
120
+ );
121
+ });
122
+ });
123
+
124
+ describe("getNetworkByChainId", () => {
125
+ it("should return network by chainId", () => {
126
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
127
+ const network = instance.getNetworkByChainId("1");
128
+ expect(network).toBeDefined();
129
+ expect(network?.chainId).toBe(1);
130
+ });
131
+
132
+ it("should return undefined if chainId not found", () => {
133
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
134
+ const network = instance.getNetworkByChainId("99999");
135
+ expect(network).toBeUndefined();
136
+ });
137
+ });
138
+
139
+ describe("getNetworkByChainIndex", () => {
140
+ it("should return network by chainIndex", () => {
141
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
142
+ const network = instance.getNetworkByChainIndex(100);
143
+ expect(network).toBeDefined();
144
+ expect(network?.chainIndex).toBe(100);
145
+ });
146
+
147
+ it("should return undefined if chainIndex not found", () => {
148
+ const instance = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
149
+ const network = instance.getNetworkByChainIndex(99999);
150
+ expect(network).toBeUndefined();
151
+ });
152
+ });
153
+ });