@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 +3 -0
- package/README.md +162 -0
- package/__tests__/business.test.ts +316 -0
- package/package.json +35 -0
- package/src/api/index.ts +115 -0
- package/src/api/types.ts +196 -0
- package/src/chains/const.ts +776 -0
- package/src/chains/getBridgeSupportChains.ts +5 -0
- package/src/chains/getChains.ts +6 -0
- package/src/chains/getSwapSupportChains.ts +5 -0
- package/src/constant/abi.ts +191 -0
- package/src/constant/abis/permit2.json +901 -0
- package/src/constant/address.ts +3 -0
- package/src/constant/index.ts +3 -0
- package/src/constant/stage.json +10 -0
- package/src/index.ts +368 -0
- package/src/swap/bridgeBuilder.ts +46 -0
- package/src/swap/getApproveBuilder.ts +25 -0
- package/src/swap/getBridgeQuotes.ts +12 -0
- package/src/swap/getSwapQuotes.ts +11 -0
- package/src/swap/methods/builder.ts +19 -0
- package/src/swap/methods/chains/const.ts +13 -0
- package/src/swap/methods/chains/evm.ts +124 -0
- package/src/swap/methods/chains/sol.ts +19 -0
- package/src/swap/methods/chains/tron.ts +123 -0
- package/src/swap/methods/permit.ts +1 -0
- package/src/swap/methods/quote.ts +60 -0
- package/src/swap/methods/unsign.ts +26 -0
- package/src/swap/methods/utils.ts +70 -0
- package/src/swap/permitSign.ts +0 -0
- package/src/swap/swapBuilder.ts +46 -0
- package/src/types/chain.ts +86 -0
- package/src/types/index.ts +15 -0
- package/src/types/swap.ts +140 -0
package/CHANGELOG.md
ADDED
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
|
+
}
|
package/src/api/index.ts
ADDED
|
@@ -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
|
+
};
|