@bitflowlabs/core-sdk 2.3.2 → 2.4.1

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 (66) hide show
  1. package/README.md +43 -36
  2. package/dist/src/helpers/callReadOnlyHelper.js +68 -52
  3. package/dist/src/helpers/callReadOnlyHelper.js.map +1 -1
  4. package/dist/src/helpers/convertValuesHelper.js +24 -4
  5. package/dist/src/helpers/convertValuesHelper.js.map +1 -1
  6. package/dist/src/helpers/handleResultHelper.js +10 -10
  7. package/dist/src/helpers/handleResultHelper.js.map +1 -1
  8. package/dist/src/helpers/newPostConditionsHelper.js +6 -1
  9. package/dist/src/helpers/newPostConditionsHelper.js.map +1 -1
  10. package/dist/src/helpers/postConditionsHelper.js +6 -1
  11. package/dist/src/helpers/postConditionsHelper.js.map +1 -1
  12. package/dist/test/BitflowSDK.test.d.ts +1 -0
  13. package/dist/test/BitflowSDK.test.js +1143 -0
  14. package/dist/test/BitflowSDK.test.js.map +1 -0
  15. package/dist/test/config.test.d.ts +1 -0
  16. package/dist/test/config.test.js +89 -0
  17. package/dist/test/config.test.js.map +1 -0
  18. package/dist/test/helpers/callGetSwapParams.test.d.ts +1 -0
  19. package/dist/test/helpers/callGetSwapParams.test.js +85 -0
  20. package/dist/test/helpers/callGetSwapParams.test.js.map +1 -0
  21. package/dist/test/helpers/callReadOnlyHelper.test.d.ts +1 -0
  22. package/dist/test/helpers/callReadOnlyHelper.test.js +345 -0
  23. package/dist/test/helpers/callReadOnlyHelper.test.js.map +1 -0
  24. package/dist/test/helpers/callSwapHelper.test.d.ts +1 -0
  25. package/dist/test/helpers/callSwapHelper.test.js +209 -0
  26. package/dist/test/helpers/callSwapHelper.test.js.map +1 -0
  27. package/dist/test/helpers/constructFunctionArgs.test.d.ts +1 -0
  28. package/dist/test/helpers/constructFunctionArgs.test.js +63 -0
  29. package/dist/test/helpers/constructFunctionArgs.test.js.map +1 -0
  30. package/dist/test/helpers/convertValuesHelper.test.d.ts +1 -0
  31. package/dist/test/helpers/convertValuesHelper.test.js +207 -0
  32. package/dist/test/helpers/convertValuesHelper.test.js.map +1 -0
  33. package/dist/test/helpers/fetchContractInterfaceHelper.test.d.ts +1 -0
  34. package/dist/test/helpers/fetchContractInterfaceHelper.test.js +70 -0
  35. package/dist/test/helpers/fetchContractInterfaceHelper.test.js.map +1 -0
  36. package/dist/test/helpers/fetchDataHelper.test.d.ts +1 -0
  37. package/dist/test/helpers/fetchDataHelper.test.js +162 -0
  38. package/dist/test/helpers/fetchDataHelper.test.js.map +1 -0
  39. package/dist/test/helpers/fetchPossibleSwap.test.d.ts +1 -0
  40. package/dist/test/helpers/fetchPossibleSwap.test.js +154 -0
  41. package/dist/test/helpers/fetchPossibleSwap.test.js.map +1 -0
  42. package/dist/test/helpers/getContractInterfaceAndFunction.test.d.ts +1 -0
  43. package/dist/test/helpers/getContractInterfaceAndFunction.test.js +25 -0
  44. package/dist/test/helpers/getContractInterfaceAndFunction.test.js.map +1 -0
  45. package/dist/test/helpers/getFunctionArgs.test.d.ts +1 -0
  46. package/dist/test/helpers/getFunctionArgs.test.js +25 -0
  47. package/dist/test/helpers/getFunctionArgs.test.js.map +1 -0
  48. package/dist/test/helpers/getTokenDecimalsHelper.test.d.ts +1 -0
  49. package/dist/test/helpers/getTokenDecimalsHelper.test.js +229 -0
  50. package/dist/test/helpers/getTokenDecimalsHelper.test.js.map +1 -0
  51. package/dist/test/helpers/getTokenNameHelper.test.d.ts +1 -0
  52. package/dist/test/helpers/getTokenNameHelper.test.js +258 -0
  53. package/dist/test/helpers/getTokenNameHelper.test.js.map +1 -0
  54. package/dist/test/helpers/handleResultHelper.test.d.ts +1 -0
  55. package/dist/test/helpers/handleResultHelper.test.js +72 -0
  56. package/dist/test/helpers/handleResultHelper.test.js.map +1 -0
  57. package/dist/test/helpers/newPostConditionsHelper.test.d.ts +1 -0
  58. package/dist/test/helpers/newPostConditionsHelper.test.js +348 -0
  59. package/dist/test/helpers/newPostConditionsHelper.test.js.map +1 -0
  60. package/dist/test/helpers/postConditionsHelper.test.d.ts +1 -0
  61. package/dist/test/helpers/postConditionsHelper.test.js +262 -0
  62. package/dist/test/helpers/postConditionsHelper.test.js.map +1 -0
  63. package/dist/test/keeper/keeperAPI.test.d.ts +1 -0
  64. package/dist/test/keeper/keeperAPI.test.js +283 -0
  65. package/dist/test/keeper/keeperAPI.test.js.map +1 -0
  66. package/package.json +1 -1
@@ -0,0 +1,1143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const BitflowSDK_1 = require("../src/BitflowSDK");
5
+ const config_1 = require("../src/config");
6
+ const callReadOnlyHelper = tslib_1.__importStar(require("../src/helpers/callReadOnlyHelper"));
7
+ const fetchDataHelper = tslib_1.__importStar(require("../src/helpers/fetchDataHelper"));
8
+ const fetchPossibleSwap = tslib_1.__importStar(require("../src/helpers/fetchPossibleSwap"));
9
+ const getContractInterfaceAndFunctionHelper = tslib_1.__importStar(require("../src/helpers/getContractInterfaceAndFunction"));
10
+ const types_1 = require("../src/keeper/types");
11
+ const callSwapHelper = tslib_1.__importStar(require("../src/helpers/callSwapHelper"));
12
+ const keeperAPI = tslib_1.__importStar(require("../src/keeper/keeperAPI"));
13
+ const callGetSwapParams = tslib_1.__importStar(require("../src/helpers/callGetSwapParams"));
14
+ // Mock entire modules to control their behavior for our tests
15
+ jest.mock('../src/config');
16
+ jest.mock('../src/helpers/fetchDataHelper');
17
+ jest.mock('../src/helpers/fetchPossibleSwap');
18
+ jest.mock('../src/helpers/callReadOnlyHelper');
19
+ jest.mock('../src/helpers/getContractInterfaceAndFunction');
20
+ jest.mock('@stacks/transactions', () => ({
21
+ ...jest.requireActual('@stacks/transactions'), // Keep original module behavior
22
+ validateStacksAddress: jest.fn(() => true), // but mock this one function
23
+ }));
24
+ jest.mock('../src/helpers/callSwapHelper');
25
+ jest.mock('../src/keeper/keeperAPI');
26
+ jest.mock('../src/helpers/callGetSwapParams');
27
+ const MOCK_PROVIDER_ADDRESS = 'SP2J6S063B42M61D5T1G295SA4238A7147A4N17X';
28
+ // This is a helper to create fake API route data for our tests.
29
+ // We can control exactly what the 'provider' field looks like.
30
+ const createMockRoute = (providerValue) => {
31
+ const route = {
32
+ dex_path: ['DEX1'],
33
+ token_path: ['token-x', 'token-y'],
34
+ quoteData: {
35
+ contract: 'SP...contract',
36
+ function: 'get-quote',
37
+ parameters: {
38
+ // This is the key part: we will test what happens when
39
+ // the provider field is null, undefined, or a real address.
40
+ },
41
+ },
42
+ swapData: { contract: 'SP...swap-contract', function: 'swap', parameters: {} },
43
+ postConditions: {},
44
+ tokenXDecimals: 6,
45
+ tokenYDecimals: 6,
46
+ };
47
+ // If we pass 'undefined' to this helper, the 'provider' key won't even exist.
48
+ // This simulates an API response where the key is missing.
49
+ if (providerValue !== undefined) {
50
+ route.quoteData.parameters.provider = providerValue;
51
+ }
52
+ return route;
53
+ };
54
+ describe('BitflowSDK - Provider Logic in getQuoteForRoute', () => {
55
+ let sdk;
56
+ // Create mock functions that we can spy on
57
+ const mockedFetchAllTokens = fetchDataHelper.fetchAllTokensFromAPI;
58
+ const mockedFetchPossibleSwaps = fetchPossibleSwap.fetchPossibleSwapsFromAPI;
59
+ const mockedCallReadOnly = callReadOnlyHelper.callReadOnlyFunctionHelper;
60
+ const mockedGetInterface = getContractInterfaceAndFunctionHelper.getContractInterfaceAndFunction;
61
+ beforeEach(() => {
62
+ // Reset mocks and configurations before each test to ensure a clean slate
63
+ jest.clearAllMocks();
64
+ // Mock functions that are called during SDK initialization or the test itself
65
+ mockedFetchAllTokens.mockResolvedValue([]);
66
+ // This is the key change: we now simulate a contract function that expects a 'provider'.
67
+ mockedGetInterface.mockResolvedValue({
68
+ contractInterface: {},
69
+ functionArgs: [{ name: 'provider', type: '{optional: principal}' }]
70
+ });
71
+ mockedCallReadOnly.mockResolvedValue({ convertedResult: 100, rawResult: 1000, tokenXDecimals: 6, tokenYDecimals: 6 });
72
+ // Set up the default provider address for the SDK instance
73
+ config_1.configs.BITFLOW_PROVIDER_ADDRESS = MOCK_PROVIDER_ADDRESS;
74
+ // Initialize the SDK for each test
75
+ sdk = new BitflowSDK_1.BitflowSDK();
76
+ });
77
+ it('Scenario 1: Should use default provider when the route provider is null', async () => {
78
+ // ARRANGE: Create a fake route where the provider is explicitly `null`
79
+ const mockRouteWithNullProvider = createMockRoute(null);
80
+ mockedFetchPossibleSwaps.mockResolvedValue({ 'token-y': [mockRouteWithNullProvider] });
81
+ // ACT: Run the function we want to test
82
+ await sdk.getQuoteForRoute('token-x', 'token-y', 100);
83
+ // ASSERT: Check if our logic worked correctly
84
+ expect(mockedCallReadOnly).toHaveBeenCalledTimes(1);
85
+ const actualParams = mockedCallReadOnly.mock.calls[0][3]; // Get the 'params' argument
86
+ expect(actualParams.provider).toBe(MOCK_PROVIDER_ADDRESS); // It should have been added
87
+ });
88
+ it('Scenario 2: Should use default provider when the route provider is missing (undefined)', async () => {
89
+ // ARRANGE: Create a fake route where the 'provider' key doesn't exist
90
+ const mockRouteWithUndefinedProvider = createMockRoute(undefined);
91
+ mockedFetchPossibleSwaps.mockResolvedValue({ 'token-y': [mockRouteWithUndefinedProvider] });
92
+ // ACT
93
+ await sdk.getQuoteForRoute('token-x', 'token-y', 100);
94
+ // ASSERT
95
+ expect(mockedCallReadOnly).toHaveBeenCalledTimes(1);
96
+ const actualParams = mockedCallReadOnly.mock.calls[0][3];
97
+ expect(actualParams.provider).toBe(MOCK_PROVIDER_ADDRESS); // It should have been added
98
+ });
99
+ it('Scenario 3: Should NOT override the provider if a valid one is already present', async () => {
100
+ // ARRANGE: Create a fake route that already has a valid provider address
101
+ const routeSpecificProvider = 'SP3B15B6STM80A752A80W3P7J53KSAQ7J01PJ80B9';
102
+ const mockRouteWithSpecificProvider = createMockRoute(routeSpecificProvider);
103
+ mockedFetchPossibleSwaps.mockResolvedValue({ 'token-y': [mockRouteWithSpecificProvider] });
104
+ // ACT
105
+ await sdk.getQuoteForRoute('token-x', 'token-y', 100);
106
+ // ASSERT
107
+ expect(mockedCallReadOnly).toHaveBeenCalledTimes(1);
108
+ const actualParams = mockedCallReadOnly.mock.calls[0][3];
109
+ expect(actualParams.provider).toBe(routeSpecificProvider); // Should be the one from the route
110
+ expect(actualParams.provider).not.toBe(MOCK_PROVIDER_ADDRESS); // And NOT our default
111
+ });
112
+ it('Scenario 4: Should NOT add a provider if the function does not expect one', async () => {
113
+ // ARRANGE: For this test only, override the mock to simulate a function WITHOUT a provider.
114
+ mockedGetInterface.mockResolvedValue({ contractInterface: {}, functionArgs: [{ name: 'amount', type: 'uint' }] });
115
+ const mockRouteWithoutProvider = createMockRoute(undefined); // Provider is missing
116
+ mockedFetchPossibleSwaps.mockResolvedValue({ 'token-y': [mockRouteWithoutProvider] });
117
+ // ACT
118
+ await sdk.getQuoteForRoute('token-x', 'token-y', 100);
119
+ // ASSERT
120
+ expect(mockedCallReadOnly).toHaveBeenCalledTimes(1);
121
+ const actualParams = mockedCallReadOnly.mock.calls[0][3];
122
+ expect(actualParams.provider).toBeUndefined(); // Provider should NOT have been added
123
+ });
124
+ });
125
+ describe('BitflowSDK - Token and Swap Retrieval Methods', () => {
126
+ let sdk;
127
+ const mockedFetchAllTokens = fetchDataHelper.fetchAllTokensFromAPI;
128
+ const mockedFetchPossibleSwaps = fetchPossibleSwap.fetchPossibleSwapsFromAPI;
129
+ beforeEach(() => {
130
+ jest.clearAllMocks();
131
+ mockedFetchAllTokens.mockResolvedValue([
132
+ { tokenId: 'token-x', isKeeperToken: false },
133
+ { tokenId: 'token-keeper', isKeeperToken: true },
134
+ ]);
135
+ mockedFetchPossibleSwaps.mockResolvedValue({ 'token-keeper': ['swap1', 'swap2'] });
136
+ sdk = new BitflowSDK_1.BitflowSDK();
137
+ });
138
+ it('should return all available tokens', async () => {
139
+ const tokens = await sdk.getAvailableTokens();
140
+ expect(tokens).toEqual([
141
+ { tokenId: 'token-x', isKeeperToken: false },
142
+ { tokenId: 'token-keeper', isKeeperToken: true },
143
+ ]);
144
+ expect(mockedFetchAllTokens).toHaveBeenCalled();
145
+ });
146
+ it('should return only keeper tokens', async () => {
147
+ const tokens = await sdk.getKeeperTokens();
148
+ expect(tokens).toEqual([
149
+ { tokenId: 'token-keeper', isKeeperToken: true }
150
+ ]);
151
+ });
152
+ it('should return possible swaps for a token', async () => {
153
+ const swaps = await sdk.getPossibleSwaps('token-keeper');
154
+ expect(swaps).toEqual({ 'token-keeper': ['swap1', 'swap2'] });
155
+ expect(mockedFetchPossibleSwaps).toHaveBeenCalledWith('token-keeper');
156
+ });
157
+ });
158
+ describe('BitflowSDK - Token Y and Route Methods', () => {
159
+ let sdk;
160
+ const mockedFetchAllTokens = fetchDataHelper.fetchAllTokensFromAPI;
161
+ const mockedFetchPossibleSwaps = fetchPossibleSwap.fetchPossibleSwapsFromAPI;
162
+ beforeEach(() => {
163
+ jest.clearAllMocks();
164
+ mockedFetchAllTokens.mockResolvedValue([
165
+ { tokenId: 'token-x', isKeeperToken: false },
166
+ { tokenId: 'token-y', isKeeperToken: true },
167
+ ]);
168
+ mockedFetchPossibleSwaps.mockResolvedValue({ 'token-y': [{ foo: 'bar' }], 'token-z': [{ baz: 'qux' }] });
169
+ sdk = new BitflowSDK_1.BitflowSDK();
170
+ // Pre-populate swapOptions for keeper methods
171
+ sdk.context.swapOptions = {
172
+ 'token-x': { 'token-y': [{ foo: 'bar' }], 'token-z': [{ baz: 'qux' }] },
173
+ };
174
+ });
175
+ it('should return all possible token Y for a given token X', async () => {
176
+ sdk.context.swapOptions['token-x'] = { 'token-y': [], 'token-z': [] };
177
+ const result = await sdk.getAllPossibleTokenY('token-x');
178
+ expect(result).toEqual(['token-y', 'token-z']);
179
+ });
180
+ it('should return all keeper possible token Y for a given token X', async () => {
181
+ // Simulate getKeeperPossibleSwaps returns a similar structure
182
+ sdk.getKeeperPossibleSwaps = jest.fn().mockResolvedValue({ 'token-y': [], 'token-z': [] });
183
+ const result = await sdk.getAllKeeperPossibleTokenY('token-x');
184
+ expect(result).toEqual(['token-y', 'token-z']);
185
+ });
186
+ it('should return all possible token Y routes for a given token X and Y', async () => {
187
+ sdk.context.swapOptions['token-x'] = { 'token-y': [{ foo: 'bar' }], 'token-z': [{ baz: 'qux' }] };
188
+ const result = await sdk.getAllPossibleTokenYRoutes('token-x', 'token-y');
189
+ expect(result).toEqual([{ foo: 'bar' }]);
190
+ });
191
+ it('should return all keeper possible token Y routes for a given token X and Y', async () => {
192
+ sdk.getKeeperPossibleSwaps = jest.fn().mockResolvedValue({ 'token-y': [{ foo: 'bar' }], 'token-z': [{ baz: 'qux' }] });
193
+ const result = await sdk.getAllKeeperPossibleTokenYRoutes('token-x', 'token-y');
194
+ expect(result).toEqual([{ foo: 'bar' }]);
195
+ });
196
+ });
197
+ describe('BitflowSDK - Route Quote Methods', () => {
198
+ let sdk;
199
+ const mockedFetchAllTokens = fetchDataHelper.fetchAllTokensFromAPI;
200
+ const mockedFetchPossibleSwaps = fetchPossibleSwap.fetchPossibleSwapsFromAPI;
201
+ const mockedCallReadOnly = callReadOnlyHelper.callReadOnlyFunctionHelper;
202
+ const mockedCallReadOnlyNoScale = callReadOnlyHelper.callReadOnlyFunctionHelperWithoutScaling;
203
+ const mockedGetInterface = getContractInterfaceAndFunctionHelper.getContractInterfaceAndFunction;
204
+ beforeEach(() => {
205
+ jest.clearAllMocks();
206
+ mockedFetchAllTokens.mockResolvedValue([
207
+ { tokenId: 'token-x', isKeeperToken: false },
208
+ { tokenId: 'token-y', isKeeperToken: true },
209
+ ]);
210
+ mockedFetchPossibleSwaps.mockResolvedValue({ 'token-y': [{
211
+ quoteData: {
212
+ contract: 'SP...contract',
213
+ function: 'get-quote',
214
+ parameters: { amount: null },
215
+ },
216
+ swapData: { contract: 'SP...swap-contract', function: 'swap', parameters: {} },
217
+ dex_path: ['BITFLOW_STABLE_XY_2'],
218
+ tokenPath: ['token-x', 'token-y'],
219
+ }] });
220
+ mockedGetInterface.mockResolvedValue({ contractInterface: {}, functionArgs: [{ name: 'amount', type: 'uint' }] });
221
+ mockedCallReadOnly.mockResolvedValue({ convertedResult: 100, rawResult: 1000, tokenXDecimals: 6, tokenYDecimals: 6, allRoutes: [], bestRoute: null });
222
+ mockedCallReadOnlyNoScale.mockResolvedValue({ convertedResult: 100, rawResult: 1000, tokenXDecimals: 6, tokenYDecimals: 6, allRoutes: [], bestRoute: null });
223
+ sdk = new BitflowSDK_1.BitflowSDK();
224
+ sdk.context.swapOptions = {
225
+ 'token-x': { 'token-y': [{
226
+ quoteData: {
227
+ contract: 'SP...contract',
228
+ function: 'get-quote',
229
+ parameters: { amount: null },
230
+ },
231
+ swapData: { contract: 'SP...swap-contract', function: 'swap', parameters: {} },
232
+ dex_path: ['BITFLOW_STABLE_XY_2'],
233
+ tokenPath: ['token-x', 'token-y'],
234
+ }] },
235
+ };
236
+ });
237
+ it('should get a quote for a route', async () => {
238
+ const result = await sdk.getQuoteForRoute('token-x', 'token-y', 100);
239
+ expect(result.bestRoute).toBeDefined();
240
+ expect(result.allRoutes.length).toBeGreaterThan(0);
241
+ expect(result.inputData.tokenX).toBe('token-x');
242
+ expect(mockedCallReadOnly).toHaveBeenCalled();
243
+ });
244
+ it('should get a keeper quote for a route', async () => {
245
+ // The keeper method filters for compatible DEX paths
246
+ const result = await sdk.getKeeperQuoteForRoute('token-x', 'token-y', 100);
247
+ expect(result.bestRoute).toBeDefined();
248
+ expect(result.allRoutes.length).toBeGreaterThan(0);
249
+ expect(result.inputData.tokenX).toBe('token-x');
250
+ expect(mockedCallReadOnly).toHaveBeenCalled();
251
+ });
252
+ it('should get a keeper quote for a route without scaling', async () => {
253
+ const result = await sdk.getKeeperQuoteForRouteWithoutScaling('token-x', 'token-y', 100);
254
+ expect(result.bestRoute).toBeDefined();
255
+ expect(result.allRoutes.length).toBeGreaterThan(0);
256
+ expect(result.inputData.tokenX).toBe('token-x');
257
+ expect(mockedCallReadOnlyNoScale).toHaveBeenCalled();
258
+ });
259
+ });
260
+ describe('BitflowSDK - Execution and Keeper Methods', () => {
261
+ let sdk;
262
+ const mockedFetchAllTokens = fetchDataHelper.fetchAllTokensFromAPI;
263
+ const mockedExecuteSwapHelper = callSwapHelper.executeSwapHelper;
264
+ const mockedGetOrCreateKeeperContractAPI = keeperAPI.getOrCreateKeeperContractAPI;
265
+ const mockedGetUserAPI = keeperAPI.getUserAPI;
266
+ const mockedGetQuoteAPI = keeperAPI.getQuoteAPI;
267
+ beforeEach(() => {
268
+ jest.clearAllMocks();
269
+ mockedFetchAllTokens.mockResolvedValue([
270
+ { tokenId: 'token-x', isKeeperToken: false },
271
+ { tokenId: 'token-y', isKeeperToken: true },
272
+ ]);
273
+ sdk = new BitflowSDK_1.BitflowSDK();
274
+ });
275
+ it('should call executeSwapHelper in executeSwap', async () => {
276
+ mockedExecuteSwapHelper.mockResolvedValue(undefined);
277
+ const swapExecutionData = {
278
+ route: {
279
+ dex_path: [],
280
+ postConditions: {},
281
+ quoteData: { contract: '', function: '', parameters: {} },
282
+ swapData: { contract: '', function: '', parameters: {} },
283
+ token_path: [],
284
+ tokenXDecimals: 6,
285
+ tokenYDecimals: 6,
286
+ },
287
+ amount: 1,
288
+ tokenXDecimals: 6,
289
+ tokenYDecimals: 6,
290
+ };
291
+ const senderAddress = 'SENDER';
292
+ sdk.getSwapParams = jest.fn().mockResolvedValue({});
293
+ sdk.loadConnectDependencies = jest.fn().mockResolvedValue({});
294
+ global.window = {};
295
+ await expect(sdk.executeSwap(swapExecutionData, senderAddress)).resolves.toBeUndefined();
296
+ expect(mockedExecuteSwapHelper).toHaveBeenCalled();
297
+ delete global.window;
298
+ });
299
+ it('should throw if executeSwap is called in Node.js', async () => {
300
+ const swapExecutionData = {
301
+ route: {
302
+ dex_path: [],
303
+ postConditions: {},
304
+ quoteData: { contract: '', function: '', parameters: {} },
305
+ swapData: { contract: '', function: '', parameters: {} },
306
+ token_path: [],
307
+ tokenXDecimals: 6,
308
+ tokenYDecimals: 6,
309
+ },
310
+ amount: 1,
311
+ tokenXDecimals: 6,
312
+ tokenYDecimals: 6,
313
+ };
314
+ const senderAddress = 'SENDER';
315
+ sdk.getSwapParams = jest.fn().mockResolvedValue({});
316
+ sdk.loadConnectDependencies = undefined; // Remove the mock to trigger the Node.js error path
317
+ delete global.window;
318
+ await expect(sdk.executeSwap(swapExecutionData, senderAddress)).rejects.toThrow('only available in browser environments');
319
+ });
320
+ it('should get or create keeper contract', async () => {
321
+ mockedGetOrCreateKeeperContractAPI.mockResolvedValue({ contract: 'keeper-contract' });
322
+ const params = { stacksAddress: 'SOMEADDR', keeperType: types_1.KeeperType.SWAP_BTC_TO_STX };
323
+ const result = await sdk.getOrCreateKeeperContract(params);
324
+ expect(result).toEqual({ contract: 'keeper-contract' });
325
+ expect(mockedGetOrCreateKeeperContractAPI).toHaveBeenCalled();
326
+ });
327
+ it('should get user', async () => {
328
+ mockedGetUserAPI.mockResolvedValue({ user: 'user-data' });
329
+ const result = await sdk.getUser('SOMEADDR');
330
+ expect(result).toEqual({ user: 'user-data' });
331
+ expect(mockedGetUserAPI).toHaveBeenCalled();
332
+ });
333
+ it('should get quote', async () => {
334
+ mockedGetQuoteAPI.mockResolvedValue({ quote: 'quote-data' });
335
+ const params = { stacksAddress: 'SOMEADDR', actionAmount: '1', keeperType: types_1.KeeperType.SWAP_BTC_TO_STX };
336
+ const result = await sdk.getQuote(params);
337
+ expect(result).toEqual({ quote: 'quote-data' });
338
+ expect(mockedGetQuoteAPI).toHaveBeenCalled();
339
+ });
340
+ it('should handle errors in getOrCreateKeeperContract', async () => {
341
+ mockedGetOrCreateKeeperContractAPI.mockRejectedValue(new Error('fail'));
342
+ const params = { stacksAddress: 'SOMEADDR', keeperType: types_1.KeeperType.SWAP_BTC_TO_STX };
343
+ await expect(sdk.getOrCreateKeeperContract(params)).rejects.toThrow('fail');
344
+ });
345
+ it('should handle errors in getUser', async () => {
346
+ mockedGetUserAPI.mockRejectedValue(new Error('fail'));
347
+ await expect(sdk.getUser('SOMEADDR')).rejects.toThrow('fail');
348
+ });
349
+ it('should handle errors in getQuote', async () => {
350
+ mockedGetQuoteAPI.mockRejectedValue(new Error('fail'));
351
+ const params = { stacksAddress: 'SOMEADDR', actionAmount: '1', keeperType: types_1.KeeperType.SWAP_BTC_TO_STX };
352
+ await expect(sdk.getQuote(params)).rejects.toThrow('fail');
353
+ });
354
+ });
355
+ describe('BitflowSDK - Additional Keeper Methods', () => {
356
+ let sdk;
357
+ const mockedGetOrderAPI = keeperAPI.getOrderAPI;
358
+ const mockedCreateOrderAPI = keeperAPI.createOrderAPI;
359
+ const mockedCreateGroupOrderAPI = keeperAPI.createGroupOrderAPI;
360
+ const mockedGetGroupOrderAPI = keeperAPI.getGroupOrderAPI;
361
+ const mockedCancelOrderAPI = keeperAPI.cancelOrderAPI;
362
+ const mockedCancelGroupOrderAPI = keeperAPI.cancelGroupOrderAPI;
363
+ beforeEach(() => {
364
+ jest.clearAllMocks();
365
+ sdk = new BitflowSDK_1.BitflowSDK();
366
+ });
367
+ it('should get order', async () => {
368
+ mockedGetOrderAPI.mockResolvedValue({ order: 'order-data' });
369
+ const result = await sdk.getOrder('order123');
370
+ expect(result).toEqual({ order: 'order-data' });
371
+ expect(mockedGetOrderAPI).toHaveBeenCalledWith('order123');
372
+ });
373
+ it('should handle errors in getOrder', async () => {
374
+ mockedGetOrderAPI.mockRejectedValue(new Error('fail'));
375
+ await expect(sdk.getOrder('order123')).rejects.toThrow('fail');
376
+ });
377
+ it('should create order', async () => {
378
+ mockedCreateOrderAPI.mockResolvedValue({ order: 'created' });
379
+ const params = {
380
+ stacksAddress: 'ST123',
381
+ keeperType: types_1.KeeperType.SWAP_BTC_TO_STX,
382
+ contractIdentifier: 'contract',
383
+ bitcoinAddress: 'btc123',
384
+ actionAmount: '100',
385
+ minReceived: { amount: '50', autoAdjust: true },
386
+ feeRecipient: 'fee',
387
+ tokenOrder: ['token1', 'token2'],
388
+ actionType: 'swap',
389
+ bitcoinTxId: 'txid',
390
+ fundingTokens: { token1: '10' },
391
+ actionAggregatorTokens: { tokenXId: 'token1', tokenYId: 'token2' },
392
+ };
393
+ const result = await sdk.createOrder(params);
394
+ expect(result).toEqual({ order: 'created' });
395
+ expect(mockedCreateOrderAPI).toHaveBeenCalledWith(params);
396
+ });
397
+ it('should handle errors in createOrder', async () => {
398
+ mockedCreateOrderAPI.mockRejectedValue(new Error('fail'));
399
+ const params = {
400
+ stacksAddress: 'ST123',
401
+ keeperType: types_1.KeeperType.SWAP_BTC_TO_STX,
402
+ contractIdentifier: 'contract',
403
+ bitcoinAddress: 'btc123',
404
+ actionAmount: '100',
405
+ minReceived: { amount: '50', autoAdjust: true },
406
+ feeRecipient: 'fee',
407
+ tokenOrder: ['token1', 'token2'],
408
+ actionType: 'swap',
409
+ bitcoinTxId: 'txid',
410
+ fundingTokens: { token1: '10' },
411
+ actionAggregatorTokens: { tokenXId: 'token1', tokenYId: 'token2' },
412
+ };
413
+ await expect(sdk.createOrder(params)).rejects.toThrow('fail');
414
+ });
415
+ it('should create group order', async () => {
416
+ mockedCreateGroupOrderAPI.mockResolvedValue({ groupOrder: 'created' });
417
+ const params = {
418
+ stacksAddress: 'ST123',
419
+ amountPerOrder: 10,
420
+ numberOfOrders: 2,
421
+ executionFrequency: 60,
422
+ feeRecipient: 'fee',
423
+ fundingTokens: { token1: '10' },
424
+ keeperType: types_1.KeeperType.SWAP_BTC_TO_STX,
425
+ minReceived: { amount: '50', autoAdjust: true },
426
+ groupId: 'group1',
427
+ orders: [],
428
+ };
429
+ const result = await sdk.createGroupOrder(params);
430
+ expect(result).toEqual({ groupOrder: 'created' });
431
+ expect(mockedCreateGroupOrderAPI).toHaveBeenCalledWith(params);
432
+ });
433
+ it('should handle errors in createGroupOrder', async () => {
434
+ mockedCreateGroupOrderAPI.mockRejectedValue(new Error('fail'));
435
+ const params = {
436
+ stacksAddress: 'ST123',
437
+ amountPerOrder: 10,
438
+ numberOfOrders: 2,
439
+ executionFrequency: 60,
440
+ feeRecipient: 'fee',
441
+ fundingTokens: { token1: '10' },
442
+ keeperType: types_1.KeeperType.SWAP_BTC_TO_STX,
443
+ minReceived: { amount: '50', autoAdjust: true },
444
+ groupId: 'group1',
445
+ orders: [],
446
+ };
447
+ await expect(sdk.createGroupOrder(params)).rejects.toThrow('fail');
448
+ });
449
+ it('should get group order', async () => {
450
+ mockedGetGroupOrderAPI.mockResolvedValue({ groupOrder: 'data' });
451
+ const result = await sdk.getGroupOrder('group1', true);
452
+ expect(result).toEqual({ groupOrder: 'data' });
453
+ expect(mockedGetGroupOrderAPI).toHaveBeenCalledWith('group1', true);
454
+ });
455
+ it('should handle errors in getGroupOrder', async () => {
456
+ mockedGetGroupOrderAPI.mockRejectedValue(new Error('fail'));
457
+ await expect(sdk.getGroupOrder('group1', true)).rejects.toThrow('fail');
458
+ });
459
+ it('should cancel order', async () => {
460
+ mockedCancelOrderAPI.mockResolvedValue({ cancel: 'ok' });
461
+ const result = await sdk.cancelOrder('order123');
462
+ expect(result).toEqual({ cancel: 'ok' });
463
+ expect(mockedCancelOrderAPI).toHaveBeenCalledWith('order123');
464
+ });
465
+ it('should handle errors in cancelOrder', async () => {
466
+ mockedCancelOrderAPI.mockRejectedValue(new Error('fail'));
467
+ await expect(sdk.cancelOrder('order123')).rejects.toThrow('fail');
468
+ });
469
+ it('should cancel group order', async () => {
470
+ mockedCancelGroupOrderAPI.mockResolvedValue({ cancel: 'ok' });
471
+ const result = await sdk.cancelGroupOrder('group1');
472
+ expect(result).toEqual({ cancel: 'ok' });
473
+ expect(mockedCancelGroupOrderAPI).toHaveBeenCalledWith('group1');
474
+ });
475
+ it('should handle errors in cancelGroupOrder', async () => {
476
+ mockedCancelGroupOrderAPI.mockRejectedValue(new Error('fail'));
477
+ await expect(sdk.cancelGroupOrder('group1')).rejects.toThrow('fail');
478
+ });
479
+ });
480
+ describe('BitflowSDK - Swap Parameter and Preparation Methods', () => {
481
+ let sdk;
482
+ const mockedCallGetSwapParams = callGetSwapParams.executeGetParams;
483
+ const mockedCallReadOnlyNoScale = callReadOnlyHelper.callReadOnlyFunctionHelperWithoutScaling;
484
+ beforeEach(() => {
485
+ jest.clearAllMocks();
486
+ mockedCallGetSwapParams.mockResolvedValue({
487
+ swapData: { data: 'swap-data' },
488
+ parameters: { params: 'params' },
489
+ postConditions: { conditions: 'conditions' }
490
+ });
491
+ // Mock the getKeeperQuoteForRouteWithoutScaling method
492
+ jest.spyOn(BitflowSDK_1.BitflowSDK.prototype, 'getKeeperQuoteForRouteWithoutScaling').mockResolvedValue({
493
+ bestRoute: {
494
+ route: {
495
+ dex_path: ['BITFLOW_XY_2_xyk'],
496
+ token_path: ['token-x', 'token-y'],
497
+ postConditions: {},
498
+ tokenXDecimals: 6,
499
+ tokenYDecimals: 6,
500
+ quoteData: {
501
+ contract: 'SP...contract',
502
+ function: 'get-quote',
503
+ parameters: { amount: 100 },
504
+ },
505
+ swapData: {
506
+ contract: 'SP...swap-contract',
507
+ function: 'swap',
508
+ parameters: {
509
+ 'pool-trait': 'SP1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.xyk-pool-v-1-2',
510
+ },
511
+ },
512
+ },
513
+ quote: 100,
514
+ params: {},
515
+ quoteData: {
516
+ contract: 'SP...contract',
517
+ function: 'get-quote',
518
+ parameters: { amount: 100 },
519
+ },
520
+ swapData: {
521
+ contract: 'SP...swap-contract',
522
+ function: 'swap',
523
+ parameters: {
524
+ 'pool-trait': 'SP1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.xyk-pool-v-1-2',
525
+ },
526
+ },
527
+ dexPath: ['BITFLOW_XY_2_xyk'],
528
+ tokenPath: ['token-x', 'token-y'],
529
+ tokenXDecimals: 6,
530
+ tokenYDecimals: 6,
531
+ },
532
+ allRoutes: [{
533
+ route: {
534
+ dex_path: ['BITFLOW_XY_2_xyk'],
535
+ token_path: ['token-x', 'token-y'],
536
+ postConditions: {},
537
+ tokenXDecimals: 6,
538
+ tokenYDecimals: 6,
539
+ quoteData: {
540
+ contract: 'SP...contract',
541
+ function: 'get-quote',
542
+ parameters: { amount: 100 },
543
+ },
544
+ swapData: {
545
+ contract: 'SP...swap-contract',
546
+ function: 'swap',
547
+ parameters: {
548
+ 'pool-trait': 'SP1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.xyk-pool-v-1-2',
549
+ },
550
+ },
551
+ },
552
+ quote: 100,
553
+ params: {},
554
+ quoteData: {
555
+ contract: 'SP...contract',
556
+ function: 'get-quote',
557
+ parameters: { amount: 100 },
558
+ },
559
+ swapData: {
560
+ contract: 'SP...swap-contract',
561
+ function: 'swap',
562
+ parameters: {
563
+ 'pool-trait': 'SP1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.xyk-pool-v-1-2',
564
+ },
565
+ },
566
+ dexPath: ['BITFLOW_XY_2_xyk'],
567
+ tokenPath: ['token-x', 'token-y'],
568
+ tokenXDecimals: 6,
569
+ tokenYDecimals: 6,
570
+ }],
571
+ inputData: {
572
+ tokenX: 'token-x',
573
+ tokenY: 'token-y',
574
+ amountInput: 100,
575
+ },
576
+ });
577
+ sdk = new BitflowSDK_1.BitflowSDK();
578
+ });
579
+ it('should get swap params', async () => {
580
+ const swapExecutionData = {
581
+ route: {
582
+ dex_path: ['BITFLOW_XY_2'],
583
+ postConditions: {},
584
+ quoteData: { contract: 'SP...contract', function: 'get-quote', parameters: { amount: null } },
585
+ swapData: {
586
+ contract: 'SP...swap-contract',
587
+ function: 'swap',
588
+ parameters: {
589
+ 'pool-trait': 'SP1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.xyk-pool-v-1-2'
590
+ }
591
+ },
592
+ token_path: ['token-x', 'token-y'],
593
+ tokenXDecimals: 6,
594
+ tokenYDecimals: 6,
595
+ },
596
+ amount: 100,
597
+ tokenXDecimals: 6,
598
+ tokenYDecimals: 6,
599
+ };
600
+ const senderAddress = 'SENDER';
601
+ const result = await sdk.getSwapParams(swapExecutionData, senderAddress, 0.015);
602
+ expect(result).toEqual({
603
+ swapData: { data: 'swap-data' },
604
+ parameters: { params: 'params' },
605
+ postConditions: { conditions: 'conditions' }
606
+ });
607
+ expect(mockedCallGetSwapParams).toHaveBeenCalled();
608
+ });
609
+ it('should prepare swap', async () => {
610
+ const swapExecutionData = {
611
+ route: {
612
+ dex_path: ['BITFLOW_XY_2'],
613
+ postConditions: {},
614
+ quoteData: { contract: 'SP...contract', function: 'get-quote', parameters: { amount: null } },
615
+ swapData: {
616
+ contract: 'SP...swap-contract',
617
+ function: 'swap',
618
+ parameters: {
619
+ 'pool-trait': 'SP1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.xyk-pool-v-1-2'
620
+ }
621
+ },
622
+ token_path: ['token-x', 'token-y'],
623
+ tokenXDecimals: 6,
624
+ tokenYDecimals: 6,
625
+ },
626
+ amount: 100,
627
+ tokenXDecimals: 6,
628
+ tokenYDecimals: 6,
629
+ };
630
+ const senderAddress = 'SENDER';
631
+ const result = await sdk.prepareSwap(swapExecutionData, senderAddress, 0.015);
632
+ expect(result).toEqual({
633
+ swapData: { data: 'swap-data' },
634
+ parameters: { params: 'params' },
635
+ postConditions: { conditions: 'conditions' }
636
+ });
637
+ expect(mockedCallGetSwapParams).toHaveBeenCalled();
638
+ });
639
+ it('should get keeper aggregator route data', async () => {
640
+ sdk["context"].availableTokens = [
641
+ {
642
+ base: 'base-x',
643
+ type: 'type-x',
644
+ icon: '',
645
+ name: 'Token X',
646
+ status: 'active',
647
+ symbol: 'TKX',
648
+ tokenId: 'token-x',
649
+ "token-id": 'token-x',
650
+ tokenContract: 'contract-x',
651
+ tokenDecimals: 6,
652
+ tokenName: 'Token X',
653
+ wrapTokens: null,
654
+ isKeeperToken: false,
655
+ bridge: 'FALSE',
656
+ layerOneAsset: null,
657
+ priceData: {
658
+ "1h_change": null,
659
+ "1yr_change": null,
660
+ "24h_change": null,
661
+ "30d_change": null,
662
+ "7d_change": null,
663
+ last_price: null,
664
+ last_updated: null,
665
+ },
666
+ },
667
+ {
668
+ base: 'base-y',
669
+ type: 'type-y',
670
+ icon: '',
671
+ name: 'Token Y',
672
+ status: 'active',
673
+ symbol: 'TKY',
674
+ tokenId: 'token-y',
675
+ "token-id": 'token-y',
676
+ tokenContract: 'contract-y',
677
+ tokenDecimals: 8,
678
+ tokenName: 'Token Y',
679
+ wrapTokens: null,
680
+ isKeeperToken: false,
681
+ bridge: 'FALSE',
682
+ layerOneAsset: null,
683
+ priceData: {
684
+ "1h_change": null,
685
+ "1yr_change": null,
686
+ "24h_change": null,
687
+ "30d_change": null,
688
+ "7d_change": null,
689
+ last_price: null,
690
+ last_updated: null,
691
+ },
692
+ },
693
+ ];
694
+ const result = await sdk.getKeeperAggregatorRouteData('token-x', 'token-y', 100);
695
+ expect(result.xykPoolList).toBeDefined();
696
+ expect(result.actionTrait).toBeDefined();
697
+ });
698
+ it('should handle mixed XYK and stableswap route', async () => {
699
+ sdk["context"].availableTokens = [
700
+ {
701
+ base: 'base-x',
702
+ type: 'type-x',
703
+ icon: '',
704
+ name: 'Token X',
705
+ status: 'active',
706
+ symbol: 'TKX',
707
+ tokenId: 'token-x',
708
+ "token-id": 'token-x',
709
+ tokenContract: 'contract-x',
710
+ tokenDecimals: 6,
711
+ tokenName: 'Token X',
712
+ wrapTokens: null,
713
+ isKeeperToken: false,
714
+ bridge: 'FALSE',
715
+ layerOneAsset: null,
716
+ priceData: {
717
+ "1h_change": null,
718
+ "1yr_change": null,
719
+ "24h_change": null,
720
+ "30d_change": null,
721
+ "7d_change": null,
722
+ last_price: null,
723
+ last_updated: null,
724
+ },
725
+ },
726
+ {
727
+ base: 'base-y',
728
+ type: 'type-y',
729
+ icon: '',
730
+ name: 'Token Y',
731
+ status: 'active',
732
+ symbol: 'TKY',
733
+ tokenId: 'token-y',
734
+ "token-id": 'token-y',
735
+ tokenContract: 'contract-y',
736
+ tokenDecimals: 8,
737
+ tokenName: 'Token Y',
738
+ wrapTokens: null,
739
+ isKeeperToken: false,
740
+ bridge: 'FALSE',
741
+ layerOneAsset: null,
742
+ priceData: {
743
+ "1h_change": null,
744
+ "1yr_change": null,
745
+ "24h_change": null,
746
+ "30d_change": null,
747
+ "7d_change": null,
748
+ last_price: null,
749
+ last_updated: null,
750
+ },
751
+ },
752
+ ];
753
+ // Override the mock for this specific test
754
+ jest.spyOn(BitflowSDK_1.BitflowSDK.prototype, 'getKeeperQuoteForRouteWithoutScaling').mockResolvedValue({
755
+ bestRoute: {
756
+ route: {
757
+ dex_path: ['BITFLOW_XY_2_xyk', 'BITFLOW_STABLE_XY_2_stable'],
758
+ token_path: ['token-x', 'token-y'],
759
+ postConditions: {},
760
+ tokenXDecimals: 6,
761
+ tokenYDecimals: 6,
762
+ quoteData: {
763
+ contract: 'SP...contract',
764
+ function: 'get-quote',
765
+ parameters: { amount: 100 },
766
+ },
767
+ swapData: {
768
+ contract: 'SP...swap-contract',
769
+ function: 'swap',
770
+ parameters: {
771
+ 'xyk-pools': ['pool1', 'pool2'],
772
+ 'stableswap-pools': ['stable-pool1'],
773
+ },
774
+ },
775
+ },
776
+ quote: 100,
777
+ params: {},
778
+ quoteData: {
779
+ contract: 'SP...contract',
780
+ function: 'get-quote',
781
+ parameters: { amount: 100 },
782
+ },
783
+ swapData: {
784
+ contract: 'SP...swap-contract',
785
+ function: 'swap',
786
+ parameters: {
787
+ 'xyk-pools': ['pool1', 'pool2'],
788
+ 'stableswap-pools': ['stable-pool1'],
789
+ },
790
+ },
791
+ dexPath: ['BITFLOW_XY_2_xyk', 'BITFLOW_STABLE_XY_2_stable'],
792
+ tokenPath: ['token-x', 'token-y'],
793
+ tokenXDecimals: 6,
794
+ tokenYDecimals: 6,
795
+ },
796
+ allRoutes: [{
797
+ route: {
798
+ dex_path: ['BITFLOW_XY_2_xyk', 'BITFLOW_STABLE_XY_2_stable'],
799
+ token_path: ['token-x', 'token-y'],
800
+ postConditions: {},
801
+ tokenXDecimals: 6,
802
+ tokenYDecimals: 6,
803
+ quoteData: {
804
+ contract: 'SP...contract',
805
+ function: 'get-quote',
806
+ parameters: { amount: 100 },
807
+ },
808
+ swapData: {
809
+ contract: 'SP...swap-contract',
810
+ function: 'swap',
811
+ parameters: {
812
+ 'xyk-pools': ['pool1', 'pool2'],
813
+ 'stableswap-pools': ['stable-pool1'],
814
+ },
815
+ },
816
+ },
817
+ quote: 100,
818
+ params: {},
819
+ quoteData: {
820
+ contract: 'SP...contract',
821
+ function: 'get-quote',
822
+ parameters: { amount: 100 },
823
+ },
824
+ swapData: {
825
+ contract: 'SP...swap-contract',
826
+ function: 'swap',
827
+ parameters: {
828
+ 'xyk-pools': ['pool1', 'pool2'],
829
+ 'stableswap-pools': ['stable-pool1'],
830
+ },
831
+ },
832
+ dexPath: ['BITFLOW_XY_2_xyk', 'BITFLOW_STABLE_XY_2_stable'],
833
+ tokenPath: ['token-x', 'token-y'],
834
+ tokenXDecimals: 6,
835
+ tokenYDecimals: 6,
836
+ }],
837
+ inputData: {
838
+ tokenX: 'token-x',
839
+ tokenY: 'token-y',
840
+ amountInput: 100,
841
+ },
842
+ });
843
+ const result = await sdk.getKeeperAggregatorRouteData('token-x', 'token-y', 100);
844
+ expect(result.xykPoolList).toBeDefined();
845
+ expect(result.stableswapPoolList).toBeDefined();
846
+ });
847
+ it('should handle swaps-reversed parameter', async () => {
848
+ sdk["context"].availableTokens = [
849
+ {
850
+ base: 'base-x',
851
+ type: 'type-x',
852
+ icon: '',
853
+ name: 'Token X',
854
+ status: 'active',
855
+ symbol: 'TKX',
856
+ tokenId: 'token-x',
857
+ "token-id": 'token-x',
858
+ tokenContract: 'contract-x',
859
+ tokenDecimals: 6,
860
+ tokenName: 'Token X',
861
+ wrapTokens: null,
862
+ isKeeperToken: false,
863
+ bridge: 'FALSE',
864
+ layerOneAsset: null,
865
+ priceData: {
866
+ "1h_change": null,
867
+ "1yr_change": null,
868
+ "24h_change": null,
869
+ "30d_change": null,
870
+ "7d_change": null,
871
+ last_price: null,
872
+ last_updated: null,
873
+ },
874
+ },
875
+ {
876
+ base: 'base-y',
877
+ type: 'type-y',
878
+ icon: '',
879
+ name: 'Token Y',
880
+ status: 'active',
881
+ symbol: 'TKY',
882
+ tokenId: 'token-y',
883
+ "token-id": 'token-y',
884
+ tokenContract: 'contract-y',
885
+ tokenDecimals: 8,
886
+ tokenName: 'Token Y',
887
+ wrapTokens: null,
888
+ isKeeperToken: false,
889
+ bridge: 'FALSE',
890
+ layerOneAsset: null,
891
+ priceData: {
892
+ "1h_change": null,
893
+ "1yr_change": null,
894
+ "24h_change": null,
895
+ "30d_change": null,
896
+ "7d_change": null,
897
+ last_price: null,
898
+ last_updated: null,
899
+ },
900
+ },
901
+ ];
902
+ // Override the mock for this specific test
903
+ jest.spyOn(BitflowSDK_1.BitflowSDK.prototype, 'getKeeperQuoteForRouteWithoutScaling').mockResolvedValue({
904
+ bestRoute: {
905
+ route: {
906
+ dex_path: ['BITFLOW_XY_2_xyk'],
907
+ token_path: ['token-x', 'token-y'],
908
+ postConditions: {},
909
+ tokenXDecimals: 6,
910
+ tokenYDecimals: 6,
911
+ quoteData: {
912
+ contract: 'SP...contract',
913
+ function: 'get-quote',
914
+ parameters: { amount: 100 },
915
+ },
916
+ swapData: {
917
+ contract: 'SP...swap-contract',
918
+ function: 'swap',
919
+ parameters: {
920
+ 'pool-trait': 'SP1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.xyk-pool-v-1-2',
921
+ 'swaps-reversed': true,
922
+ },
923
+ },
924
+ },
925
+ quote: 100,
926
+ params: {},
927
+ quoteData: {
928
+ contract: 'SP...contract',
929
+ function: 'get-quote',
930
+ parameters: { amount: 100 },
931
+ },
932
+ swapData: {
933
+ contract: 'SP...swap-contract',
934
+ function: 'swap',
935
+ parameters: {
936
+ 'pool-trait': 'SP1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.xyk-pool-v-1-2',
937
+ 'swaps-reversed': true,
938
+ },
939
+ },
940
+ dexPath: ['BITFLOW_XY_2_xyk'],
941
+ tokenPath: ['token-x', 'token-y'],
942
+ tokenXDecimals: 6,
943
+ tokenYDecimals: 6,
944
+ },
945
+ allRoutes: [{
946
+ route: {
947
+ dex_path: ['BITFLOW_XY_2_xyk'],
948
+ token_path: ['token-x', 'token-y'],
949
+ postConditions: {},
950
+ tokenXDecimals: 6,
951
+ tokenYDecimals: 6,
952
+ quoteData: {
953
+ contract: 'SP...contract',
954
+ function: 'get-quote',
955
+ parameters: { amount: 100 },
956
+ },
957
+ swapData: {
958
+ contract: 'SP...swap-contract',
959
+ function: 'swap',
960
+ parameters: {
961
+ 'pool-trait': 'SP1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.xyk-pool-v-1-2',
962
+ 'swaps-reversed': true,
963
+ },
964
+ },
965
+ },
966
+ quote: 100,
967
+ params: {},
968
+ quoteData: {
969
+ contract: 'SP...contract',
970
+ function: 'get-quote',
971
+ parameters: { amount: 100 },
972
+ },
973
+ swapData: {
974
+ contract: 'SP...swap-contract',
975
+ function: 'swap',
976
+ parameters: {
977
+ 'pool-trait': 'SP1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.xyk-pool-v-1-2',
978
+ 'swaps-reversed': true,
979
+ },
980
+ },
981
+ dexPath: ['BITFLOW_XY_2_xyk'],
982
+ tokenPath: ['token-x', 'token-y'],
983
+ tokenXDecimals: 6,
984
+ tokenYDecimals: 6,
985
+ }],
986
+ inputData: {
987
+ tokenX: 'token-x',
988
+ tokenY: 'token-y',
989
+ amountInput: 100,
990
+ },
991
+ });
992
+ const result = await sdk.getKeeperAggregatorRouteData('token-x', 'token-y', 100);
993
+ expect(result.xykPoolList).toBeDefined();
994
+ expect(result.actionTrait).toBeDefined();
995
+ expect(result.boolList).toBeDefined();
996
+ });
997
+ it('should handle unsupported DEX path in mapDexPathToActionTrait', async () => {
998
+ // Patch availableTokens so contract identifier lookup does not fail
999
+ sdk["context"].availableTokens = [
1000
+ {
1001
+ base: 'base-x',
1002
+ type: 'type-x',
1003
+ icon: '',
1004
+ name: 'Token X',
1005
+ status: 'active',
1006
+ symbol: 'TKX',
1007
+ tokenId: 'token-x',
1008
+ "token-id": 'token-x',
1009
+ tokenContract: 'contract-x',
1010
+ tokenDecimals: 6,
1011
+ tokenName: 'Token X',
1012
+ wrapTokens: null,
1013
+ isKeeperToken: false,
1014
+ bridge: 'FALSE',
1015
+ layerOneAsset: null,
1016
+ priceData: {
1017
+ "1h_change": null,
1018
+ "1yr_change": null,
1019
+ "24h_change": null,
1020
+ "30d_change": null,
1021
+ "7d_change": null,
1022
+ last_price: null,
1023
+ last_updated: null,
1024
+ },
1025
+ },
1026
+ {
1027
+ base: 'base-y',
1028
+ type: 'type-y',
1029
+ icon: '',
1030
+ name: 'Token Y',
1031
+ status: 'active',
1032
+ symbol: 'TKY',
1033
+ tokenId: 'token-y',
1034
+ "token-id": 'token-y',
1035
+ tokenContract: 'contract-y',
1036
+ tokenDecimals: 8,
1037
+ tokenName: 'Token Y',
1038
+ wrapTokens: null,
1039
+ isKeeperToken: false,
1040
+ bridge: 'FALSE',
1041
+ layerOneAsset: null,
1042
+ priceData: {
1043
+ "1h_change": null,
1044
+ "1yr_change": null,
1045
+ "24h_change": null,
1046
+ "30d_change": null,
1047
+ "7d_change": null,
1048
+ last_price: null,
1049
+ last_updated: null,
1050
+ },
1051
+ },
1052
+ ];
1053
+ // Mock getKeeperQuoteForRouteWithoutScaling to return a route with unsupported DEX path
1054
+ jest.spyOn(sdk, 'getKeeperQuoteForRouteWithoutScaling').mockResolvedValue({
1055
+ bestRoute: {
1056
+ route: {
1057
+ dex_path: ['unsupported-dex'],
1058
+ token_path: ['token-x', 'token-y'],
1059
+ postConditions: {},
1060
+ quoteData: { contract: 'test', function: 'test', parameters: {} },
1061
+ swapData: { contract: 'test', function: 'test', parameters: {} },
1062
+ tokenXDecimals: 6,
1063
+ tokenYDecimals: 8,
1064
+ },
1065
+ quote: 100,
1066
+ params: {},
1067
+ quoteData: { contract: 'test', function: 'test', parameters: {} },
1068
+ swapData: { contract: 'test', function: 'test', parameters: {} },
1069
+ dexPath: ['unsupported-dex'],
1070
+ tokenPath: ['token-x', 'token-y'],
1071
+ tokenXDecimals: 6,
1072
+ tokenYDecimals: 8,
1073
+ },
1074
+ allRoutes: [
1075
+ {
1076
+ route: {
1077
+ dex_path: ['unsupported-dex'],
1078
+ token_path: ['token-x', 'token-y'],
1079
+ postConditions: {},
1080
+ quoteData: { contract: 'test', function: 'test', parameters: {} },
1081
+ swapData: { contract: 'test', function: 'test', parameters: {} },
1082
+ tokenXDecimals: 6,
1083
+ tokenYDecimals: 8,
1084
+ },
1085
+ quote: 100,
1086
+ params: {},
1087
+ quoteData: { contract: 'test', function: 'test', parameters: {} },
1088
+ swapData: { contract: 'test', function: 'test', parameters: {} },
1089
+ dexPath: ['unsupported-dex'],
1090
+ tokenPath: ['token-x', 'token-y'],
1091
+ tokenXDecimals: 6,
1092
+ tokenYDecimals: 8,
1093
+ },
1094
+ ],
1095
+ inputData: { tokenX: 'token-x', tokenY: 'token-y', amountInput: 100 },
1096
+ });
1097
+ await expect(sdk.getKeeperAggregatorRouteData('token-x', 'token-y', 100)).rejects.toThrow('Unsupported DEX path: unsupported-dex');
1098
+ });
1099
+ it('should handle no routes found in getKeeperAggregatorRouteData', async () => {
1100
+ // Mock getKeeperQuoteForRouteWithoutScaling to return no routes
1101
+ jest.spyOn(sdk, 'getKeeperQuoteForRouteWithoutScaling').mockResolvedValue({
1102
+ bestRoute: null,
1103
+ allRoutes: [],
1104
+ inputData: { tokenX: 'token-x', tokenY: 'token-y', amountInput: 100 },
1105
+ });
1106
+ await expect(sdk.getKeeperAggregatorRouteData('token-x', 'token-y', 100)).rejects.toThrow('No routes found');
1107
+ });
1108
+ it('should handle no best route found in getKeeperAggregatorRouteData', async () => {
1109
+ // Mock getKeeperQuoteForRouteWithoutScaling to return routes but no best route
1110
+ jest.spyOn(sdk, 'getKeeperQuoteForRouteWithoutScaling').mockResolvedValue({
1111
+ bestRoute: null,
1112
+ allRoutes: [
1113
+ {
1114
+ route: {
1115
+ dex_path: ['xyk'],
1116
+ token_path: ['token-x', 'token-y'],
1117
+ postConditions: {},
1118
+ quoteData: { contract: 'test', function: 'test', parameters: {} },
1119
+ swapData: { contract: 'test', function: 'test', parameters: {} },
1120
+ tokenXDecimals: 6,
1121
+ tokenYDecimals: 8,
1122
+ },
1123
+ quote: 100,
1124
+ params: {},
1125
+ quoteData: { contract: 'test', function: 'test', parameters: {} },
1126
+ swapData: { contract: 'test', function: 'test', parameters: {} },
1127
+ dexPath: ['xyk'],
1128
+ tokenPath: ['token-x', 'token-y'],
1129
+ tokenXDecimals: 6,
1130
+ tokenYDecimals: 8,
1131
+ },
1132
+ ],
1133
+ inputData: { tokenX: 'token-x', tokenY: 'token-y', amountInput: 100 },
1134
+ });
1135
+ await expect(sdk.getKeeperAggregatorRouteData('token-x', 'token-y', 100)).rejects.toThrow('No best route found for keeper-compatible DEX paths');
1136
+ });
1137
+ it('should handle error in getKeeperAggregatorRouteData', async () => {
1138
+ // Mock getKeeperQuoteForRouteWithoutScaling to throw an error
1139
+ jest.spyOn(sdk, 'getKeeperQuoteForRouteWithoutScaling').mockRejectedValue(new Error('API Error'));
1140
+ await expect(sdk.getKeeperAggregatorRouteData('token-x', 'token-y', 100)).rejects.toThrow('API Error');
1141
+ });
1142
+ });
1143
+ //# sourceMappingURL=BitflowSDK.test.js.map