@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.
- package/dist/index.cjs +42 -24
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +43 -25
- package/package.json +3 -2
- package/project.json +1 -1
- package/src/__tests__/config.test.ts +46 -0
- package/src/__tests__/dogecoin-utils.test.ts +147 -0
- package/src/__tests__/evm-utils.test.ts +133 -0
- package/src/__tests__/index.test.ts +40 -0
- package/src/__tests__/services.test.ts +285 -0
- package/src/__tests__/solana-utils.test.ts +131 -0
- package/src/__tests__/utils.test.ts +52 -0
- package/src/__tests__/wallet.test.ts +350 -0
- package/src/api/__tests__/base.test.ts +146 -0
- package/src/api/__tests__/index.test.ts +51 -0
- package/src/api/__tests__/network.test.ts +153 -0
- package/src/api/__tests__/token.test.ts +231 -2
- package/src/api/__tests__/transaction.test.ts +121 -6
- package/src/api/__tests__/user.test.ts +237 -3
- package/src/api/__tests__/wallet.test.ts +174 -4
- package/src/api/network.ts +9 -1
- package/src/api/utils/__tests__/index.test.ts +91 -0
- package/src/api/utils/__tests__/signature.test.ts +124 -0
- package/src/api/utils/index.ts +6 -2
- package/src/base/__tests__/network.test.ts +119 -0
- package/src/base/__tests__/service.test.ts +68 -0
- package/src/base/__tests__/token.test.ts +123 -0
- package/src/base/__tests__/transaction.test.ts +210 -0
- package/src/config.ts +2 -1
- package/src/dogecoin/__tests__/base.test.ts +76 -0
- package/src/dogecoin/__tests__/rpc.test.ts +465 -0
- package/src/dogecoin/__tests__/service-extended.test.ts +420 -0
- package/src/dogecoin/__tests__/utils-doge.test.ts +244 -0
- package/src/dogecoin/__tests__/utils-extended.test.ts +323 -0
- package/src/dogecoin/base.ts +1 -0
- package/src/dogecoin/config.ts +2 -2
- package/src/dogecoin/rpc.ts +10 -1
- package/src/dogecoin/service.ts +9 -5
- package/src/evm/__tests__/rpc.test.ts +132 -0
- package/src/evm/__tests__/service.test.ts +535 -0
- package/src/evm/__tests__/utils.test.ts +170 -0
- package/src/evm/service.ts +11 -0
- package/src/evm/utils.ts +2 -2
- package/src/solana/__tests__/service.test.ts +425 -0
- package/src/solana/__tests__/utils.test.ts +937 -0
- package/src/solana/config.ts +1 -1
- package/src/solana/service.ts +2 -0
- package/src/solana/utils.ts +2 -16
- package/src/utils/index.ts +1 -1
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { generateSignature, generateCubeSignature } from "../signature";
|
|
3
|
+
|
|
4
|
+
describe("signature utils", () => {
|
|
5
|
+
describe("generateSignature", () => {
|
|
6
|
+
it("should generate signature for GET request", () => {
|
|
7
|
+
const config = {
|
|
8
|
+
method: "GET",
|
|
9
|
+
url: "/api/test",
|
|
10
|
+
apiKey: "test-api-key",
|
|
11
|
+
apiSecret: "test-api-secret",
|
|
12
|
+
salt: "test-salt",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const result = generateSignature(config);
|
|
16
|
+
expect(result).toHaveProperty("signature");
|
|
17
|
+
expect(result).toHaveProperty("timestamp");
|
|
18
|
+
expect(typeof result.signature).toBe("string");
|
|
19
|
+
expect(typeof result.timestamp).toBe("string");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should generate signature for POST request with data", () => {
|
|
23
|
+
const config = {
|
|
24
|
+
method: "POST",
|
|
25
|
+
url: "/api/test",
|
|
26
|
+
data: { key: "value" },
|
|
27
|
+
apiKey: "test-api-key",
|
|
28
|
+
apiSecret: "test-api-secret",
|
|
29
|
+
salt: "test-salt",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const result = generateSignature(config);
|
|
33
|
+
expect(result).toHaveProperty("signature");
|
|
34
|
+
expect(result).toHaveProperty("timestamp");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should generate signature for POST request with string data", () => {
|
|
38
|
+
const config = {
|
|
39
|
+
method: "POST",
|
|
40
|
+
url: "/api/test",
|
|
41
|
+
data: '{"key":"value"}',
|
|
42
|
+
apiKey: "test-api-key",
|
|
43
|
+
apiSecret: "test-api-secret",
|
|
44
|
+
salt: "test-salt",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const result = generateSignature(config);
|
|
48
|
+
expect(result).toHaveProperty("signature");
|
|
49
|
+
expect(result).toHaveProperty("timestamp");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should generate signature for POST request without data", () => {
|
|
53
|
+
const config = {
|
|
54
|
+
method: "POST",
|
|
55
|
+
url: "/api/test",
|
|
56
|
+
apiKey: "test-api-key",
|
|
57
|
+
apiSecret: "test-api-secret",
|
|
58
|
+
salt: "test-salt",
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const result = generateSignature(config);
|
|
62
|
+
expect(result).toHaveProperty("signature");
|
|
63
|
+
expect(result).toHaveProperty("timestamp");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should generate different signatures for different timestamps", () => {
|
|
67
|
+
const config = {
|
|
68
|
+
method: "GET",
|
|
69
|
+
url: "/api/test",
|
|
70
|
+
apiKey: "test-api-key",
|
|
71
|
+
apiSecret: "test-api-secret",
|
|
72
|
+
salt: "test-salt",
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const result1 = generateSignature(config);
|
|
76
|
+
// Wait a bit to ensure different timestamp
|
|
77
|
+
vi.useFakeTimers();
|
|
78
|
+
vi.advanceTimersByTime(1000);
|
|
79
|
+
const result2 = generateSignature(config);
|
|
80
|
+
vi.useRealTimers();
|
|
81
|
+
|
|
82
|
+
// Signatures should be different due to different timestamps
|
|
83
|
+
expect(result1.signature).not.toBe(result2.signature);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("generateCubeSignature", () => {
|
|
88
|
+
it("should generate cube signature", async () => {
|
|
89
|
+
const data = { key: "value" };
|
|
90
|
+
const salt = "test-salt";
|
|
91
|
+
|
|
92
|
+
const result = await generateCubeSignature(data, salt);
|
|
93
|
+
expect(result).toHaveProperty("timestamp");
|
|
94
|
+
expect(result).toHaveProperty("signature");
|
|
95
|
+
expect(typeof result.timestamp).toBe("string");
|
|
96
|
+
expect(typeof result.signature).toBe("string");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should throw error when salt is missing", async () => {
|
|
100
|
+
const data = { key: "value" };
|
|
101
|
+
await expect(generateCubeSignature(data, "")).rejects.toThrow("salt is required");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should include timestamp in result", async () => {
|
|
105
|
+
const data = { key: "value" };
|
|
106
|
+
const salt = "test-salt";
|
|
107
|
+
|
|
108
|
+
const result = await generateCubeSignature(data, salt);
|
|
109
|
+
expect(result.timestamp).toBeDefined();
|
|
110
|
+
expect(Number(result.timestamp)).toBeGreaterThan(0);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should generate different signatures for different data", async () => {
|
|
114
|
+
const salt = "test-salt";
|
|
115
|
+
const data1 = { key: "value1" };
|
|
116
|
+
const data2 = { key: "value2" };
|
|
117
|
+
|
|
118
|
+
const result1 = await generateCubeSignature(data1, salt);
|
|
119
|
+
const result2 = await generateCubeSignature(data2, salt);
|
|
120
|
+
|
|
121
|
+
expect(result1.signature).not.toBe(result2.signature);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|
package/src/api/utils/index.ts
CHANGED
|
@@ -4,9 +4,13 @@ export { signUtils as signature };
|
|
|
4
4
|
export const formatJwtToken = (jwtToken: string) => `Bearer ${jwtToken}`;
|
|
5
5
|
|
|
6
6
|
export function signRequest(params: any, clientId: string, signParams: ISignConfig | string): any {
|
|
7
|
+
if (!params.headers) {
|
|
8
|
+
params.headers = {};
|
|
9
|
+
}
|
|
10
|
+
|
|
7
11
|
if (typeof signParams === "string") {
|
|
8
12
|
const jwtToken = signParams;
|
|
9
|
-
Object.assign(params.headers
|
|
13
|
+
Object.assign(params.headers, {
|
|
10
14
|
"client-id": clientId,
|
|
11
15
|
Authorization: formatJwtToken(jwtToken),
|
|
12
16
|
});
|
|
@@ -23,7 +27,7 @@ export function signRequest(params: any, clientId: string, signParams: ISignConf
|
|
|
23
27
|
salt: salt,
|
|
24
28
|
});
|
|
25
29
|
|
|
26
|
-
Object.assign(params.headers
|
|
30
|
+
Object.assign(params.headers, {
|
|
27
31
|
"X-APP-Key": apiKey,
|
|
28
32
|
"X-Signature": signature,
|
|
29
33
|
"X-Timestamp": timestamp,
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
|
+
import { Networks } from "../network";
|
|
3
|
+
import { NetworkAPIs } from "../../api/network";
|
|
4
|
+
import { ChainTypeEnum } from "@tomo-inc/wallet-utils";
|
|
5
|
+
import { CONFIG, SIGN_CONFIG } from "../../api/__tests__/config";
|
|
6
|
+
|
|
7
|
+
describe("Networks", () => {
|
|
8
|
+
let networks: Networks;
|
|
9
|
+
let mockNetworkAPIs: NetworkAPIs;
|
|
10
|
+
|
|
11
|
+
const mockTomoAppInfo = {
|
|
12
|
+
tomoStage: "dev" as const,
|
|
13
|
+
tomoClientId: SIGN_CONFIG.clientId || "",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
mockNetworkAPIs = NetworkAPIs.getInstance(CONFIG, mockTomoAppInfo);
|
|
18
|
+
networks = new Networks(mockNetworkAPIs);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("constructor", () => {
|
|
22
|
+
it("should create Networks instance with default chainType", () => {
|
|
23
|
+
const networksInstance = new Networks(mockNetworkAPIs);
|
|
24
|
+
expect(networksInstance).toBeInstanceOf(Networks);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should create Networks instance with specific chainType", () => {
|
|
28
|
+
const networksInstance = new Networks(mockNetworkAPIs, ChainTypeEnum.EVM);
|
|
29
|
+
expect(networksInstance).toBeInstanceOf(Networks);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("setChainType", () => {
|
|
34
|
+
it("should set chain type", () => {
|
|
35
|
+
networks.setChainType(ChainTypeEnum.EVM);
|
|
36
|
+
expect((networks as any).chainType).toBe(ChainTypeEnum.EVM);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("getNetworks", () => {
|
|
41
|
+
it("should return networks for specific chain type", async () => {
|
|
42
|
+
const result = await networks.getNetworks(ChainTypeEnum.EVM);
|
|
43
|
+
expect(Array.isArray(result)).toBe(true);
|
|
44
|
+
expect(result.every((n) => n.platformType === "EVM")).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should return all networks when chainType is empty", async () => {
|
|
48
|
+
const result = await networks.getNetworks("");
|
|
49
|
+
expect(Array.isArray(result)).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should use default chainType when not provided", async () => {
|
|
53
|
+
const networksInstance = new Networks(mockNetworkAPIs, ChainTypeEnum.EVM);
|
|
54
|
+
const result = await networksInstance.getNetworks();
|
|
55
|
+
expect(Array.isArray(result)).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("getNetworkByChainId", () => {
|
|
60
|
+
it("should return network by chainId", async () => {
|
|
61
|
+
const network = await networks.getNetworkByChainId("1");
|
|
62
|
+
expect(network).toBeDefined();
|
|
63
|
+
expect(network.chainId).toBe(1);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should throw error if network not found", async () => {
|
|
67
|
+
await expect(networks.getNetworkByChainId("99999")).rejects.toThrow("Network not found");
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe("getNetworkByChainIndex", () => {
|
|
72
|
+
it("should return network by chainIndex", async () => {
|
|
73
|
+
const network = await networks.getNetworkByChainIndex(100);
|
|
74
|
+
expect(network).toBeDefined();
|
|
75
|
+
expect(network.chainIndex).toBe(100);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("should throw error if network not found", async () => {
|
|
79
|
+
await expect(networks.getNetworkByChainIndex(99999)).rejects.toThrow("Network not found");
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe("getCurrentNetwork", () => {
|
|
84
|
+
it("should return current network", async () => {
|
|
85
|
+
networks.setChainType(ChainTypeEnum.EVM);
|
|
86
|
+
const network = await networks.getCurrentNetwork();
|
|
87
|
+
expect(network).toBeDefined();
|
|
88
|
+
expect(network.platformType).toBe("EVM");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should use default chainId from SupportedChainTypes if cache is empty", async () => {
|
|
92
|
+
networks.setChainType(ChainTypeEnum.EVM);
|
|
93
|
+
const network = await networks.getCurrentNetwork();
|
|
94
|
+
expect(network).toBeDefined();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should use SupportedChainTypes fallback when getCurrentNetwork returns empty string", async () => {
|
|
98
|
+
networks.setChainType(ChainTypeEnum.EVM);
|
|
99
|
+
vi.spyOn(networks.networkAPIs, "getCurrentNetwork").mockResolvedValue("");
|
|
100
|
+
const network = await networks.getCurrentNetwork();
|
|
101
|
+
expect(network).toBeDefined();
|
|
102
|
+
expect(network.platformType).toBe("EVM");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe("setCurrentNetwork", () => {
|
|
107
|
+
it("should set current network", async () => {
|
|
108
|
+
networks.setChainType(ChainTypeEnum.EVM);
|
|
109
|
+
const result = await networks.setCurrentNetwork("56");
|
|
110
|
+
expect(result).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should set current network with empty chainType", async () => {
|
|
114
|
+
const networksInstance = new Networks(mockNetworkAPIs);
|
|
115
|
+
const result = await networksInstance.setCurrentNetwork("1");
|
|
116
|
+
expect(result).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { BaseService } from "../service";
|
|
3
|
+
import { SIGN_CONFIG } from "../../api/__tests__/config";
|
|
4
|
+
|
|
5
|
+
// Mock tomoPublicApiService
|
|
6
|
+
vi.mock("../../api", () => {
|
|
7
|
+
return {
|
|
8
|
+
tomoPublicApiService: vi.fn(() => ({
|
|
9
|
+
tokenAPIs: {},
|
|
10
|
+
transactionAPIs: {},
|
|
11
|
+
networkAPIs: {},
|
|
12
|
+
})),
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe("BaseService", () => {
|
|
17
|
+
const mockTomoAppInfo = {
|
|
18
|
+
tomoStage: "dev" as const,
|
|
19
|
+
tomoClientId: SIGN_CONFIG.clientId || "",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const mockAccountInfo = {
|
|
23
|
+
getCurrent: vi.fn(),
|
|
24
|
+
signMessage: vi.fn(),
|
|
25
|
+
signTransaction: vi.fn(),
|
|
26
|
+
signTypedData: vi.fn(),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
describe("constructor", () => {
|
|
30
|
+
it("should create BaseService instance with valid tomoStage", () => {
|
|
31
|
+
const service = new BaseService(mockTomoAppInfo);
|
|
32
|
+
expect(service).toBeInstanceOf(BaseService);
|
|
33
|
+
expect(service.networks).toBeDefined();
|
|
34
|
+
expect(service.tokens).toBeDefined();
|
|
35
|
+
expect(service.transactions).toBeDefined();
|
|
36
|
+
expect(service.isDappConnected).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should create BaseService instance with accountInfo", () => {
|
|
40
|
+
const service = new BaseService(mockTomoAppInfo, mockAccountInfo as any);
|
|
41
|
+
expect(service.accountInfo).toBe(mockAccountInfo);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should throw error for invalid tomoStage", () => {
|
|
45
|
+
const invalidAppInfo = {
|
|
46
|
+
tomoStage: "invalid" as any,
|
|
47
|
+
tomoClientId: SIGN_CONFIG.clientId || "",
|
|
48
|
+
};
|
|
49
|
+
expect(() => new BaseService(invalidAppInfo)).toThrow("Tomo stage is required");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should throw error when tomoStage is missing", () => {
|
|
53
|
+
const invalidAppInfo = {
|
|
54
|
+
tomoClientId: SIGN_CONFIG.clientId || "",
|
|
55
|
+
} as any;
|
|
56
|
+
expect(() => new BaseService(invalidAppInfo)).toThrow("Tomo stage is required");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("setApproveParams", () => {
|
|
61
|
+
it("should set approve params", () => {
|
|
62
|
+
const service = new BaseService(mockTomoAppInfo);
|
|
63
|
+
const params = { token: "0x123", amount: "100" };
|
|
64
|
+
service.setApproveParams(params);
|
|
65
|
+
expect(service.approveParams).toEqual(params);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
|
+
import { Tokens } from "../token";
|
|
3
|
+
import { TokenAPIs } from "../../api/token";
|
|
4
|
+
import { CONFIG, SIGN_CONFIG } from "../../api/__tests__/config";
|
|
5
|
+
|
|
6
|
+
describe("Tokens", () => {
|
|
7
|
+
let tokens: Tokens;
|
|
8
|
+
let mockTokenAPIs: TokenAPIs;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
mockTokenAPIs = TokenAPIs.getInstance(CONFIG, {
|
|
12
|
+
tomoStage: "dev" as const,
|
|
13
|
+
tomoClientId: SIGN_CONFIG.clientId || "",
|
|
14
|
+
});
|
|
15
|
+
tokens = new Tokens(mockTokenAPIs);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("constructor", () => {
|
|
19
|
+
it("should create Tokens instance", () => {
|
|
20
|
+
expect(tokens).toBeInstanceOf(Tokens);
|
|
21
|
+
expect(tokens.tokenAPIs).toBe(mockTokenAPIs);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("searchTokens", () => {
|
|
26
|
+
it("should search tokens by keyword", async () => {
|
|
27
|
+
const mockTokens = [
|
|
28
|
+
{
|
|
29
|
+
address: "0x123",
|
|
30
|
+
symbol: "TEST",
|
|
31
|
+
name: "Test Token",
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
vi.spyOn(mockTokenAPIs, "queryRemoteTokens").mockResolvedValue({
|
|
35
|
+
data: mockTokens,
|
|
36
|
+
} as any);
|
|
37
|
+
|
|
38
|
+
const result = await tokens.searchTokens({ keyword: "TEST" });
|
|
39
|
+
expect(result).toEqual(mockTokens);
|
|
40
|
+
expect(mockTokenAPIs.queryRemoteTokens).toHaveBeenCalledWith({ keyword: "TEST", chainIndex: undefined });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should search tokens with chainIndex", async () => {
|
|
44
|
+
const mockTokens = [
|
|
45
|
+
{
|
|
46
|
+
address: "0x123",
|
|
47
|
+
symbol: "TEST",
|
|
48
|
+
name: "Test Token",
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
vi.spyOn(mockTokenAPIs, "queryRemoteTokens").mockResolvedValue({
|
|
52
|
+
data: mockTokens,
|
|
53
|
+
} as any);
|
|
54
|
+
|
|
55
|
+
const result = await tokens.searchTokens({ keyword: "TEST", chainIndex: 100 });
|
|
56
|
+
expect(result).toEqual(mockTokens);
|
|
57
|
+
expect(mockTokenAPIs.queryRemoteTokens).toHaveBeenCalledWith({ keyword: "TEST", chainIndex: 100 });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should return empty array when no tokens found", async () => {
|
|
61
|
+
vi.spyOn(mockTokenAPIs, "queryRemoteTokens").mockResolvedValue({
|
|
62
|
+
data: [],
|
|
63
|
+
} as any);
|
|
64
|
+
|
|
65
|
+
const result = await tokens.searchTokens({ keyword: "NONEXISTENT" });
|
|
66
|
+
expect(result).toEqual([]);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("getTokenInfo", () => {
|
|
71
|
+
it("should get token info", async () => {
|
|
72
|
+
const mockTokenInfo = {
|
|
73
|
+
address: "0x123",
|
|
74
|
+
symbol: "TEST",
|
|
75
|
+
name: "Test Token",
|
|
76
|
+
decimals: 18,
|
|
77
|
+
};
|
|
78
|
+
vi.spyOn(mockTokenAPIs, "getTokenInfo").mockResolvedValue({
|
|
79
|
+
data: {
|
|
80
|
+
data: mockTokenInfo,
|
|
81
|
+
},
|
|
82
|
+
} as any);
|
|
83
|
+
|
|
84
|
+
const result = await tokens.getTokenInfo({ address: "0x123", chainIndex: 100 });
|
|
85
|
+
expect(result).toEqual(mockTokenInfo);
|
|
86
|
+
expect(mockTokenAPIs.getTokenInfo).toHaveBeenCalledWith({ address: "0x123", chainIndex: 100 });
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("getTokenRiskInfo", () => {
|
|
91
|
+
it("should get token risk info", async () => {
|
|
92
|
+
const mockRiskInfo = {
|
|
93
|
+
riskLevel: "low",
|
|
94
|
+
riskScore: 10,
|
|
95
|
+
};
|
|
96
|
+
vi.spyOn(mockTokenAPIs, "getTokenRisk").mockResolvedValue({
|
|
97
|
+
data: mockRiskInfo,
|
|
98
|
+
} as any);
|
|
99
|
+
|
|
100
|
+
const result = await tokens.getTokenRiskInfo({ address: "0x123", chainIndex: 100 });
|
|
101
|
+
expect(result).toEqual(mockRiskInfo);
|
|
102
|
+
expect(mockTokenAPIs.getTokenRisk).toHaveBeenCalledWith({ address: "0x123", chainIndex: 100 });
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe("getTokenDetail", () => {
|
|
107
|
+
it("should get token detail", async () => {
|
|
108
|
+
const mockDetail = {
|
|
109
|
+
address: "0x123",
|
|
110
|
+
symbol: "TEST",
|
|
111
|
+
name: "Test Token",
|
|
112
|
+
totalSupply: "1000000",
|
|
113
|
+
};
|
|
114
|
+
vi.spyOn(mockTokenAPIs, "getTokenDetail").mockResolvedValue({
|
|
115
|
+
data: mockDetail,
|
|
116
|
+
} as any);
|
|
117
|
+
|
|
118
|
+
const result = await tokens.getTokenDetail({ address: "0x123", chainIndex: 100 });
|
|
119
|
+
expect(result).toEqual(mockDetail);
|
|
120
|
+
expect(mockTokenAPIs.getTokenDetail).toHaveBeenCalledWith({ address: "0x123", chainIndex: 100 });
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { Transactions } from "../transaction";
|
|
3
|
+
import { ChainTypeEnum } from "@tomo-inc/wallet-utils";
|
|
4
|
+
|
|
5
|
+
describe("Transactions", () => {
|
|
6
|
+
let transactions: Transactions;
|
|
7
|
+
let mockTransactionAPIs: any;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
mockTransactionAPIs = {
|
|
11
|
+
queryGasInfo: vi.fn(),
|
|
12
|
+
getTransactions: vi.fn(),
|
|
13
|
+
getTransaction: vi.fn(),
|
|
14
|
+
};
|
|
15
|
+
transactions = new Transactions(mockTransactionAPIs as any);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("queryGasInfo", () => {
|
|
19
|
+
it("should query gas info successfully", async () => {
|
|
20
|
+
const mockResponse = {
|
|
21
|
+
code: 0,
|
|
22
|
+
result: {
|
|
23
|
+
baseFee: 1000000000,
|
|
24
|
+
gasLimit: 21000,
|
|
25
|
+
priorityFeeHigh: 2000000000,
|
|
26
|
+
priorityFeeLow: 1000000000,
|
|
27
|
+
priorityFeeMedium: 1500000000,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
mockTransactionAPIs.queryGasInfo.mockResolvedValue(mockResponse);
|
|
31
|
+
|
|
32
|
+
const result = await transactions.queryGasInfo({
|
|
33
|
+
chainType: ChainTypeEnum.EVM,
|
|
34
|
+
params: { chainId: "1" },
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(result.success).toBe(true);
|
|
38
|
+
expect(result.data).toEqual(mockResponse.result);
|
|
39
|
+
expect(mockTransactionAPIs.queryGasInfo).toHaveBeenCalledWith(ChainTypeEnum.EVM, { chainId: "1" });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should handle query gas info error", async () => {
|
|
43
|
+
mockTransactionAPIs.queryGasInfo.mockRejectedValue(new Error("Network error"));
|
|
44
|
+
|
|
45
|
+
const result = await transactions.queryGasInfo({
|
|
46
|
+
chainType: ChainTypeEnum.EVM,
|
|
47
|
+
params: { chainId: "1" },
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
expect(result.success).toBe(false);
|
|
51
|
+
expect(result.message).toBe("Network error");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("should handle query gas info with non-zero code", async () => {
|
|
55
|
+
const mockResponse = {
|
|
56
|
+
code: 1,
|
|
57
|
+
message: "Invalid chain",
|
|
58
|
+
};
|
|
59
|
+
mockTransactionAPIs.queryGasInfo.mockResolvedValue(mockResponse);
|
|
60
|
+
|
|
61
|
+
const result = await transactions.queryGasInfo({
|
|
62
|
+
chainType: ChainTypeEnum.EVM,
|
|
63
|
+
params: { chainId: "999" },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(result.success).toBe(false);
|
|
67
|
+
expect(result.data).toEqual({});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should handle query gas info with missing error message", async () => {
|
|
71
|
+
mockTransactionAPIs.queryGasInfo.mockRejectedValue({});
|
|
72
|
+
|
|
73
|
+
const result = await transactions.queryGasInfo({
|
|
74
|
+
chainType: ChainTypeEnum.EVM,
|
|
75
|
+
params: { chainId: "1" },
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
expect(result.success).toBe(false);
|
|
79
|
+
expect(result.message).toBe("Failed to query gas info");
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe("getTransactions", () => {
|
|
84
|
+
it("should get transactions successfully", async () => {
|
|
85
|
+
const mockResponse = {
|
|
86
|
+
cursor: "next-cursor",
|
|
87
|
+
transactionList: [
|
|
88
|
+
{
|
|
89
|
+
txHash: "0x123",
|
|
90
|
+
chainIndex: 100,
|
|
91
|
+
tokenInfo: {},
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
};
|
|
95
|
+
mockTransactionAPIs.getTransactions.mockResolvedValue(mockResponse);
|
|
96
|
+
|
|
97
|
+
const result = await transactions.getTransactions({
|
|
98
|
+
walletId: "test-wallet-id",
|
|
99
|
+
chainIndex: 100,
|
|
100
|
+
pageLimit: 20,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
expect(result.cursor).toBe("next-cursor");
|
|
104
|
+
expect(result.transactionList).toEqual(mockResponse.transactionList);
|
|
105
|
+
expect(mockTransactionAPIs.getTransactions).toHaveBeenCalledWith({
|
|
106
|
+
walletId: "test-wallet-id",
|
|
107
|
+
chainIndex: 100,
|
|
108
|
+
pageLimit: 20,
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("should handle getTransactions with cursor", async () => {
|
|
113
|
+
const mockResponse = {
|
|
114
|
+
cursor: "next-page",
|
|
115
|
+
transactionList: [],
|
|
116
|
+
};
|
|
117
|
+
mockTransactionAPIs.getTransactions.mockResolvedValue(mockResponse);
|
|
118
|
+
|
|
119
|
+
const result = await transactions.getTransactions({
|
|
120
|
+
walletId: "test-wallet-id",
|
|
121
|
+
chainIndex: 100,
|
|
122
|
+
cursor: "prev-cursor",
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
expect(result.cursor).toBe("next-page");
|
|
126
|
+
expect(mockTransactionAPIs.getTransactions).toHaveBeenCalledWith({
|
|
127
|
+
walletId: "test-wallet-id",
|
|
128
|
+
chainIndex: 100,
|
|
129
|
+
cursor: "prev-cursor",
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should handle getTransactions with empty response", async () => {
|
|
134
|
+
mockTransactionAPIs.getTransactions.mockResolvedValue(null);
|
|
135
|
+
|
|
136
|
+
const result = await transactions.getTransactions({
|
|
137
|
+
walletId: "test-wallet-id",
|
|
138
|
+
chainIndex: 100,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
expect(result.cursor).toBe("");
|
|
142
|
+
expect(result.transactionList).toEqual([]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("should handle getTransactions with missing cursor", async () => {
|
|
146
|
+
const mockResponse = {
|
|
147
|
+
transactionList: [],
|
|
148
|
+
};
|
|
149
|
+
mockTransactionAPIs.getTransactions.mockResolvedValue(mockResponse);
|
|
150
|
+
|
|
151
|
+
const result = await transactions.getTransactions({
|
|
152
|
+
walletId: "test-wallet-id",
|
|
153
|
+
chainIndex: 100,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
expect(result.cursor).toBe("");
|
|
157
|
+
expect(result.transactionList).toEqual([]);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe("getTransaction", () => {
|
|
162
|
+
it("should get transaction successfully", async () => {
|
|
163
|
+
const mockResponse = {
|
|
164
|
+
data: {
|
|
165
|
+
txHash: "0x123",
|
|
166
|
+
chainIndex: 100,
|
|
167
|
+
tokenInfo: {},
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
mockTransactionAPIs.getTransaction.mockResolvedValue(mockResponse);
|
|
171
|
+
|
|
172
|
+
const result = await transactions.getTransaction({
|
|
173
|
+
walletId: "test-wallet-id",
|
|
174
|
+
txHash: "0x123",
|
|
175
|
+
chainIndex: 100,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
expect(result).toEqual(mockResponse.data);
|
|
179
|
+
expect(mockTransactionAPIs.getTransaction).toHaveBeenCalledWith({
|
|
180
|
+
walletId: "test-wallet-id",
|
|
181
|
+
txHash: "0x123",
|
|
182
|
+
chainIndex: 100,
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("should handle getTransaction with empty response", async () => {
|
|
187
|
+
mockTransactionAPIs.getTransaction.mockResolvedValue({});
|
|
188
|
+
|
|
189
|
+
const result = await transactions.getTransaction({
|
|
190
|
+
walletId: "test-wallet-id",
|
|
191
|
+
txHash: "0x123",
|
|
192
|
+
chainIndex: 100,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(result).toEqual({});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("should handle getTransaction with missing data", async () => {
|
|
199
|
+
mockTransactionAPIs.getTransaction.mockResolvedValue(null);
|
|
200
|
+
|
|
201
|
+
const result = await transactions.getTransaction({
|
|
202
|
+
walletId: "test-wallet-id",
|
|
203
|
+
txHash: "0x123",
|
|
204
|
+
chainIndex: 100,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
expect(result).toEqual({});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
});
|
package/src/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TomoApiDomains, TomoStage } from "@tomo-inc/wallet-utils";
|
|
2
2
|
|
|
3
|
-
function getConfig(tomoStage: TomoStage) {
|
|
3
|
+
export function getConfig(tomoStage: TomoStage) {
|
|
4
4
|
const domain = TomoApiDomains[tomoStage];
|
|
5
5
|
if (!domain) {
|
|
6
6
|
throw new Error("Invalid tomo stage");
|
|
@@ -18,4 +18,5 @@ export const CONFIG = {
|
|
|
18
18
|
prod: getConfig("prod"),
|
|
19
19
|
pre: getConfig("pre"),
|
|
20
20
|
dev: getConfig("dev"),
|
|
21
|
+
"prod-dogeos": getConfig("prod-dogeos"),
|
|
21
22
|
};
|