@tomo-inc/transaction-builder-sdk 0.0.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # @tomo-inc/transaction-builder-sdk
2
+
3
+ ## 0.0.1
package/README.md ADDED
@@ -0,0 +1,162 @@
1
+ # @tomo-inc/transaction-builder-sdk
2
+
3
+ Tomo Wallet Transaction Builder SDK is a comprehensive TypeScript library that provides developers with easy-to-use interfaces for integrating wallet functionalities, swap operations, and cross-chain transactions into their applications.
4
+
5
+ ## Features
6
+
7
+ - **Multi-chain Support**: Works with EVM, Solana, TRON, and other major blockchain platforms
8
+ - **Swap Operations**: Fetch quotes and build transactions for token swaps across DEXes
9
+ - **Cross-chain Bridge**: Support for cross-chain token transfers
10
+ - **Token Approvals**: Handle ERC20 token approvals and permit signatures
11
+ - **Transaction Building**: Generate ready-to-sign transactions for various operations
12
+ - **Chain Information**: Access detailed information about supported blockchain networks
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @tomo-inc/transaction-builder-sdk
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ First, initialize the SDK with your API credentials:
23
+
24
+ ```typescript
25
+ import { Business } from "@tomo-inc/transaction-builder-sdk";
26
+
27
+ const business = new Business({
28
+ config: {
29
+ API_KEY: "your-api-key",
30
+ API_SECRET: "your-api-secret",
31
+ SALT: "your-salt",
32
+ },
33
+ tomoStage: "prod", // or 'dev' for development
34
+ });
35
+ ```
36
+
37
+ ## Chain Information
38
+
39
+ Get information about supported chains for different operations:
40
+
41
+ ```typescript
42
+ // Get all supported chains
43
+ const allChains = business.getChains();
44
+
45
+ // Get chains that support swap operations
46
+ const swapChains = business.getSwapSupportChains();
47
+
48
+ // Get chains that support bridge operations
49
+ const bridgeChains = business.getBridgeSupportChains();
50
+ ```
51
+
52
+ ## Swap Operations
53
+
54
+ Fetch quotes and build transactions for token swaps on the same chain:
55
+
56
+ ```typescript
57
+ // Get swap quotes
58
+ const quotes = await business.getSwapQuotes({
59
+ sender: "0x...", // User's wallet address
60
+ recipient: "0x...", // Recipient address (usually same as sender)
61
+ fromToken: {
62
+ address: "0x...", // Token contract address (use '' for native token)
63
+ chain: {
64
+ chainId: 1, // Ethereum mainnet
65
+ // ... other chain properties
66
+ },
67
+ },
68
+ toToken: {
69
+ address: "0x...", // Token contract address
70
+ chain: {
71
+ chainId: 1, // Must be same chain for swaps
72
+ // ... other chain properties
73
+ },
74
+ },
75
+ amount: "1000000000000000000", // Amount in token's smallest unit (e.g., wei for ETH)
76
+ slippage: 5, // 5% slippage tolerance
77
+ });
78
+
79
+ // Build swap transaction
80
+ const swapTx = await business.swapBuilder(quotes[0], {
81
+ // Same parameters as getSwapQuotes
82
+ });
83
+ ```
84
+
85
+ ## Cross-chain Bridge
86
+
87
+ Support for cross-chain token transfers:
88
+
89
+ ```typescript
90
+ // Get bridge quotes
91
+ const bridgeQuotes = await business.getBridgeQuotes({
92
+ sender: "0x...", // User's wallet address on source chain
93
+ recipient: "0x...", // Recipient address on destination chain
94
+ fromToken: {
95
+ address: "0x...", // Token contract address on source chain
96
+ chain: {
97
+ chainId: 1, // Source chain (e.g., Ethereum)
98
+ // ... other chain properties
99
+ },
100
+ },
101
+ toToken: {
102
+ address: "0x...", // Token contract address on destination chain
103
+ chain: {
104
+ chainId: 56, // Destination chain (e.g., BSC)
105
+ // ... other chain properties
106
+ },
107
+ },
108
+ amount: "1000000000000000000", // Amount in token's smallest unit
109
+ slippage: 5, // 5% slippage tolerance
110
+ });
111
+
112
+ // Build bridge transaction
113
+ const bridgeTx = await business.bridgeBuilder(bridgeQuotes[0], {
114
+ // Same parameters as getBridgeQuotes
115
+ });
116
+ ```
117
+
118
+ ## Token Approvals
119
+
120
+ Handle ERC20 token approvals and permit signatures:
121
+
122
+ ```typescript
123
+ import { useSignTypedData } from "wagmi";
124
+ const { signTypedDataAsync } = useSignTypedData();
125
+
126
+ // Build approval transaction (for tokens that don't support permit)
127
+ const approveTx = await business.getApproveBuilder(quote, quoteParams);
128
+
129
+ // For tokens that support permit signatures (EIP-712)
130
+ const permitTypeData = await business.evm.getPermitTypeData(quoteParams, quote);
131
+ // Sign the permit data with wallet
132
+ const signature = await signTypedDataAsync(permitTypeData);
133
+ // Use the signature when building the transaction
134
+ const swapTx = await business.swapBuilder(quote, quoteParams, {
135
+ signature,
136
+ permitTypeData,
137
+ });
138
+ ```
139
+
140
+ ## API Reference
141
+
142
+ ### Core Methods
143
+
144
+ - getSwapQuotes(params) - Fetch quotes for token swaps on the same chain
145
+ - getBridgeQuotes(params) - Fetch quotes for cross-chain transfers
146
+ - swapBuilder(quote, quoteParams, permitParams?) - Build swap transaction data
147
+ - bridgeBuilder(quote, quoteParams, permitParams?) - Build bridge transaction data
148
+ - getApproveBuilder(quote, quoteParams) - Build token approval transaction
149
+
150
+ ### EVM-specific Methods
151
+
152
+ - evm.getPermitTypeData(quoteParams, quote) - Generate EIP-712 typed data for permit signatures
153
+
154
+ ### Chain Functions
155
+
156
+ - getChains() - Returns all supported chains
157
+ - getSwapSupportChains() - Returns chains that support swap operations
158
+ - getBridgeSupportChains() - Returns chains that support bridge operations
159
+
160
+ ## License
161
+
162
+ MIT
@@ -0,0 +1,316 @@
1
+ // __tests__/business.integration.test.ts
2
+ import { beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { Business } from "../src";
4
+ import { Chain, IPlatformType } from "../src/types/chain";
5
+ import { TransactionQuoteParams, TransactionQuoteResult, TransactionToken } from "../src/types/swap";
6
+
7
+ describe("Business Integration", () => {
8
+ let business: Business;
9
+ let mockEthChain: Chain;
10
+ let mockBscChain: Chain;
11
+ let mockEthToken: TransactionToken;
12
+ let mockBnbToken: TransactionToken;
13
+ let mockDaiToken: TransactionToken;
14
+ let mockBridgeQuoteParams: TransactionQuoteParams;
15
+ let mockSwapQuoteParams: TransactionQuoteParams;
16
+
17
+ beforeEach(() => {
18
+ business = new Business({
19
+ config: {
20
+ API_KEY: "j7sjxxdv5iaqnq69ull0t1rbcc3y6yx5",
21
+ API_SECRET: "7bk63t810l3czi7b6vvd6gpxlqsq8ype",
22
+ SALT: "Tomo@2025!@#",
23
+ CLIENT_ID: "",
24
+ },
25
+ tomoStage: "dev",
26
+ });
27
+
28
+ // Setup mock chains
29
+ mockEthChain = {
30
+ chainName: "Ethereum",
31
+ chainId: 1,
32
+ chainIndex: 100,
33
+ orderStatusSupport: true,
34
+ support: true,
35
+ isListed: true,
36
+ type: 1,
37
+ name: "ETH",
38
+ nativeCurrencyName: "ETH",
39
+ nativeCurrencySymbol: "ETH",
40
+ nativeCurrencyDecimals: 18,
41
+ platformType: IPlatformType.EVM,
42
+ icon: "https://example.com/eth.png",
43
+ supportSwap: true,
44
+ isTestnet: false,
45
+ };
46
+
47
+ mockBscChain = {
48
+ chainName: "BNB Smart Chain",
49
+ chainId: 56,
50
+ chainIndex: 5600,
51
+ orderStatusSupport: true,
52
+ support: true,
53
+ isListed: true,
54
+ type: 1,
55
+ name: "BSC",
56
+ nativeCurrencyName: "BNB",
57
+ nativeCurrencySymbol: "BNB",
58
+ nativeCurrencyDecimals: 18,
59
+ platformType: IPlatformType.EVM,
60
+ icon: "https://example.com/bnb.png",
61
+ supportSwap: true,
62
+ isTestnet: false,
63
+ };
64
+
65
+ // Setup mock tokens
66
+ mockEthToken = {
67
+ address: "",
68
+ chain: mockEthChain,
69
+ };
70
+
71
+ mockBnbToken = {
72
+ address: "",
73
+ chain: mockBscChain,
74
+ };
75
+
76
+ mockDaiToken = {
77
+ address: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
78
+ chain: mockEthChain,
79
+ };
80
+
81
+ // Setup mock quote parameters
82
+ mockBridgeQuoteParams = {
83
+ sender: "0x1234567890123456789012345678901234567890",
84
+ recipient: "0x1234567890123456789012345678901234567890",
85
+ fromToken: mockEthToken,
86
+ toToken: mockBnbToken,
87
+ slippage: 0.5,
88
+ amount: "1000000000000000000", // 1 ETH
89
+ };
90
+
91
+ mockSwapQuoteParams = {
92
+ sender: "0x1234567890123456789012345678901234567890",
93
+ recipient: "0x1234567890123456789012345678901234567890",
94
+ fromToken: mockEthToken,
95
+ toToken: mockDaiToken,
96
+ slippage: 0.5,
97
+ amount: "1000000000000000000", // 1 ETH
98
+ };
99
+ });
100
+
101
+ describe("Bridge Flow", () => {
102
+ it("should get bridge quotes and build bridge transaction", async () => {
103
+ // Mock the getBridgeQuotes implementation to return a quote result
104
+ const mockBridgeQuotes: TransactionQuoteResult[] = [
105
+ {
106
+ quoteID: "bridge-quote-123",
107
+ amountIn: "1000000000000000000",
108
+ amountOut: "995000000000000000",
109
+ contract: "0xBridgeContractAddress",
110
+ amountOutMin: "990000000000000000",
111
+ gasNetWorkFee: "21000",
112
+ approveGasFee: "50000",
113
+ crossNetworkFee: {
114
+ fromChainNetworkFee: "0",
115
+ toChainNetworkFee: "0",
116
+ providerServiceFee: "0",
117
+ },
118
+ dexInfo: {
119
+ name: "MockBridge",
120
+ displayName: "Mock Bridge",
121
+ logo: "https://example.com/bridge.png",
122
+ },
123
+ fromToken: {
124
+ chainID: "1",
125
+ address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
126
+ symbol: "ETH",
127
+ decimals: 18,
128
+ },
129
+ toToken: {
130
+ chainID: "56",
131
+ address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
132
+ symbol: "BNB",
133
+ decimals: 18,
134
+ },
135
+ extended: {
136
+ requestID: "bridge-request-123",
137
+ },
138
+ routes: [
139
+ {
140
+ amountIn: "1000000000000000000",
141
+ amountOut: "995000000000000000",
142
+ paths: [
143
+ {
144
+ dexProvider: "MockBridge",
145
+ fromToken: {
146
+ chainID: "1",
147
+ address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
148
+ symbol: "ETH",
149
+ decimals: 18,
150
+ },
151
+ toToken: {
152
+ chainID: "56",
153
+ address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
154
+ symbol: "BNB",
155
+ decimals: 18,
156
+ },
157
+ amountIn: "1000000000000000000",
158
+ amountOut: "995000000000000000",
159
+ },
160
+ ],
161
+ },
162
+ ],
163
+ estimatedGas: {
164
+ feeUSD: "1.50",
165
+ gasLimit: "21000",
166
+ baseFee: "30",
167
+ priorityFee: {
168
+ low: "1",
169
+ medium: "2",
170
+ high: "3",
171
+ },
172
+ },
173
+ },
174
+ ];
175
+
176
+ // Mock the bridgeBuilder implementation to return a transaction
177
+ const mockBridgeTransaction = {
178
+ chainId: "0x1",
179
+ data: "0xBridgeTransactionData",
180
+ gasLimit: "21000",
181
+ to: "0xBridgeContractAddress",
182
+ from: "0xUserAddress",
183
+ value: "0x0",
184
+ nonce: "0x1",
185
+ };
186
+
187
+ // Mock the implementations
188
+ vi.spyOn(business, "getBridgeQuotes").mockResolvedValue(mockBridgeQuotes);
189
+ vi.spyOn(business, "bridgeBuilder").mockResolvedValue(mockBridgeTransaction);
190
+
191
+ // Execute the flow: get quotes then build transaction
192
+ const quotes = await business.getBridgeQuotes(mockBridgeQuoteParams);
193
+ const transaction = await business.bridgeBuilder(quotes[0], mockBridgeQuoteParams);
194
+
195
+ // Verify the flow
196
+ expect(quotes).toHaveLength(1);
197
+ expect(quotes[0]).toHaveProperty("quoteID", "bridge-quote-123");
198
+ expect(quotes[0]).toHaveProperty("amountIn", "1000000000000000000");
199
+
200
+ expect(transaction).toHaveProperty("data", "0xBridgeTransactionData");
201
+ expect(transaction).toHaveProperty("to", "0xBridgeContractAddress");
202
+
203
+ // Verify both methods were called
204
+ expect(business.getBridgeQuotes).toHaveBeenCalledWith(mockBridgeQuoteParams);
205
+ expect(business.bridgeBuilder).toHaveBeenCalledWith(quotes[0], mockBridgeQuoteParams);
206
+ });
207
+ });
208
+
209
+ describe("Swap Flow", () => {
210
+ it("should get swap quotes and build swap transaction", async () => {
211
+ // Mock the getSwapQuotes implementation to return a quote result
212
+ const mockSwapQuotes: TransactionQuoteResult[] = [
213
+ {
214
+ quoteID: "swap-quote-123",
215
+ amountIn: "1000000000000000000",
216
+ amountOut: "3000000000000000000000",
217
+ contract: "0xSwapContractAddress",
218
+ amountOutMin: "2985000000000000000000",
219
+ gasNetWorkFee: "150000",
220
+ approveGasFee: "50000",
221
+ crossNetworkFee: {
222
+ fromChainNetworkFee: "0",
223
+ toChainNetworkFee: "0",
224
+ providerServiceFee: "0",
225
+ },
226
+ dexInfo: {
227
+ name: "Uniswap",
228
+ displayName: "Uniswap V3",
229
+ logo: "https://example.com/uniswap.png",
230
+ },
231
+ fromToken: {
232
+ chainID: "1",
233
+ address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
234
+ symbol: "ETH",
235
+ decimals: 18,
236
+ },
237
+ toToken: {
238
+ chainID: "1",
239
+ address: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
240
+ symbol: "DAI",
241
+ decimals: 18,
242
+ },
243
+ extended: {
244
+ requestID: "swap-request-123",
245
+ },
246
+ routes: [
247
+ {
248
+ amountIn: "1000000000000000000",
249
+ amountOut: "3000000000000000000000",
250
+ paths: [
251
+ {
252
+ dexProvider: "Uniswap",
253
+ fromToken: {
254
+ chainID: "1",
255
+ address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
256
+ symbol: "ETH",
257
+ decimals: 18,
258
+ },
259
+ toToken: {
260
+ chainID: "1",
261
+ address: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
262
+ symbol: "DAI",
263
+ decimals: 18,
264
+ },
265
+ amountIn: "1000000000000000000",
266
+ amountOut: "3000000000000000000000",
267
+ },
268
+ ],
269
+ },
270
+ ],
271
+ estimatedGas: {
272
+ feeUSD: "5.25",
273
+ gasLimit: "150000",
274
+ baseFee: "35",
275
+ priorityFee: {
276
+ low: "1",
277
+ medium: "2",
278
+ high: "3",
279
+ },
280
+ },
281
+ },
282
+ ];
283
+
284
+ // Mock the swapBuilder implementation to return a transaction
285
+ const mockSwapTransaction = {
286
+ chainId: "0x1",
287
+ data: "0xSwapTransactionData",
288
+ gasLimit: "150000",
289
+ to: "0xSwapContractAddress",
290
+ from: "0xUserAddress",
291
+ value: "0x0",
292
+ nonce: "0x2",
293
+ };
294
+
295
+ // Mock the implementations
296
+ vi.spyOn(business, "getSwapQuotes").mockResolvedValue(mockSwapQuotes);
297
+ vi.spyOn(business, "swapBuilder").mockResolvedValue(mockSwapTransaction);
298
+
299
+ // Execute the flow: get quotes then build transaction
300
+ const quotes = await business.getSwapQuotes(mockSwapQuoteParams);
301
+ const transaction = await business.swapBuilder(quotes[0], mockSwapQuoteParams);
302
+
303
+ // Verify the flow
304
+ expect(quotes).toHaveLength(1);
305
+ expect(quotes[0]).toHaveProperty("quoteID", "swap-quote-123");
306
+ expect(quotes[0]).toHaveProperty("amountOut", "3000000000000000000000");
307
+
308
+ expect(transaction).toHaveProperty("data", "0xSwapTransactionData");
309
+ expect(transaction).toHaveProperty("gasLimit", "150000");
310
+
311
+ // Verify both methods were called
312
+ expect(business.getSwapQuotes).toHaveBeenCalledWith(mockSwapQuoteParams);
313
+ expect(business.swapBuilder).toHaveBeenCalledWith(quotes[0], mockSwapQuoteParams);
314
+ });
315
+ });
316
+ });
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@tomo-inc/transaction-builder-sdk",
3
+ "version": "0.0.1-alpha.0",
4
+ "author": "tomo.inc",
5
+ "private": false,
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.js"
14
+ }
15
+ },
16
+ "scripts": {
17
+ "build": "tsup src/index.ts --format esm,cjs --dts --treeshake",
18
+ "dev": "tsup src/index.ts --format esm,cjs --watch --dts",
19
+ "lint": "eslint src/**/*.ts",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest"
22
+ },
23
+ "dependencies": {
24
+ "@tomo-inc/tomo-api": "workspace:*",
25
+ "axios": "^1.11.0",
26
+ "viem": "^2.0.0",
27
+ "tronweb": "^6.0.3"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^20.0.0",
31
+ "tsup": "^8.0.0",
32
+ "typescript": "^5.0.0",
33
+ "vitest": "^1.0.0"
34
+ }
35
+ }
@@ -0,0 +1,115 @@
1
+ import { utils } from "@tomo-inc/tomo-api";
2
+ import axios from "axios";
3
+ import { ApiConfig, Config, TomoStage } from "../types";
4
+ import { TransactionQuoteResult } from "../types/swap";
5
+ import stageConfig from "./../constant/stage.json";
6
+ import { IRawApiToken, SwapApiRouterTxRequestV2, SwapApiRouterTxResponseV2, SwapApiRoutesRequestV2 } from "./types";
7
+
8
+ const QUOTE_API = "/api/v2/quote";
9
+ const BUILD_TRANSACTION_API = "/api/v2/buildTransaction";
10
+
11
+ const createApiConfig = (stage: TomoStage) => {
12
+ const routerApiUrl = stageConfig[stage].routerApiUrl;
13
+ const marketApiUrl = stageConfig[stage].marketApiUrl;
14
+
15
+ return {
16
+ routeApi: axios.create({
17
+ baseURL: routerApiUrl,
18
+ headers: {
19
+ "X-App-Version": "20250427",
20
+ },
21
+ }),
22
+ marketApi: axios.create({
23
+ baseURL: marketApiUrl,
24
+ }),
25
+ };
26
+ };
27
+
28
+ export const reinitializeApi = (stage: TomoStage, apiConfig: Config | null) => {
29
+ const { routeApi, marketApi } = createApiConfig(stage);
30
+
31
+ if (!apiConfig) {
32
+ throw new Error("Config was not provided");
33
+ }
34
+
35
+ [routeApi, marketApi].forEach((api) => {
36
+ api.interceptors.request.use(
37
+ (config) => {
38
+ const params = {
39
+ method: config.method?.toLocaleUpperCase() || "",
40
+ url: config.url || "",
41
+ data: config.data,
42
+ apiKey: apiConfig.API_KEY,
43
+ apiSecret: apiConfig.API_SECRET,
44
+ salt: apiConfig.SALT,
45
+ // debug: true,
46
+ };
47
+ const { signature, timestamp } = utils.signature.generateSignature(params);
48
+
49
+ Object.assign(config.headers ?? {}, {
50
+ "client-id": apiConfig.CLIENT_ID,
51
+ "X-APP-Key": apiConfig.API_KEY,
52
+ "X-Signature": signature,
53
+ "X-Timestamp": timestamp,
54
+ });
55
+ return config;
56
+ },
57
+ (error) => {
58
+ if (error?.response?.status === 401) {
59
+ return Promise.reject(error);
60
+ }
61
+ },
62
+ );
63
+ });
64
+
65
+ return {
66
+ routeApi,
67
+ marketApi,
68
+ };
69
+ };
70
+
71
+ export const reinitializeApiMethods = (apiConfig: ApiConfig) => {
72
+ const { routeApi, marketApi } = apiConfig;
73
+ if (!marketApi || !routeApi) {
74
+ throw new Error("API instances are not initialized");
75
+ }
76
+
77
+ const getTokenInfo = async (queryParameters: {
78
+ content: string;
79
+ chainIndex?: number;
80
+ }): Promise<{
81
+ data: IRawApiToken[];
82
+ }> => {
83
+ const res = await marketApi.get(`/v1/market/token/search`, {
84
+ params: queryParameters,
85
+ });
86
+ return res.data;
87
+ };
88
+
89
+ const getSwapRoutesV2 = async (params: SwapApiRoutesRequestV2): Promise<TransactionQuoteResult[]> => {
90
+ const res = await routeApi.post(QUOTE_API, params);
91
+ const data = res?.data?.result?.data;
92
+ if (!data) {
93
+ const e = new Error("No route found");
94
+ e.cause = res?.data;
95
+ throw e;
96
+ }
97
+ return data;
98
+ };
99
+
100
+ const getSwapRoutesTxV2 = async (params: SwapApiRouterTxRequestV2): Promise<SwapApiRouterTxResponseV2> => {
101
+ const res = await routeApi.post(BUILD_TRANSACTION_API, params);
102
+ const data = res?.data?.result;
103
+ if (data?.transactions[0]?.gasInfo) {
104
+ return res.data.result;
105
+ } else {
106
+ throw new Error("Transaction failed");
107
+ }
108
+ };
109
+
110
+ return {
111
+ getTokenInfo,
112
+ getSwapRoutesV2,
113
+ getSwapRoutesTxV2,
114
+ };
115
+ };