@bitflowlabs/core-sdk 2.3.0 → 2.3.2-beta.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/dist/src/config.d.ts +1 -1
- package/dist/src/config.js +12 -10
- package/dist/src/config.js.map +1 -1
- package/dist/src/helpers/callReadOnlyHelper.js +18 -17
- package/dist/src/helpers/callReadOnlyHelper.js.map +1 -1
- package/package.json +2 -3
- package/src/BitflowSDK.ts +0 -1244
- package/src/config.ts +0 -41
- package/src/helpers/callGetSwapParams.ts +0 -122
- package/src/helpers/callReadOnlyHelper.ts +0 -482
- package/src/helpers/callSwapHelper.ts +0 -67
- package/src/helpers/constructFunctionArgs.ts +0 -24
- package/src/helpers/convertValuesHelper.ts +0 -220
- package/src/helpers/fetchContractInterfaceHelper.ts +0 -27
- package/src/helpers/fetchDataHelper.ts +0 -88
- package/src/helpers/fetchPossibleSwap.ts +0 -39
- package/src/helpers/getContractInterfaceAndFunction.ts +0 -20
- package/src/helpers/getFunctionArgs.ts +0 -12
- package/src/helpers/getTokenDecimalsHelper.ts +0 -33
- package/src/helpers/getTokenNameHelper.ts +0 -26
- package/src/helpers/handleResultHelper.ts +0 -84
- package/src/helpers/newPostConditionsHelper.ts +0 -172
- package/src/helpers/postConditionsHelper.ts +0 -298
- package/src/index.ts +0 -3
- package/src/keeper/keeperAPI.ts +0 -365
- package/src/keeper/types.ts +0 -310
- package/src/test/testMethods.ts +0 -246
- package/src/types.ts +0 -168
package/src/BitflowSDK.ts
DELETED
|
@@ -1,1244 +0,0 @@
|
|
|
1
|
-
import { STACKS_MAINNET } from '@stacks/network';
|
|
2
|
-
import type { StacksProvider } from '@stacks/connect';
|
|
3
|
-
import { validateStacksAddress } from '@stacks/transactions';
|
|
4
|
-
import {
|
|
5
|
-
SwapContext,
|
|
6
|
-
Token,
|
|
7
|
-
SwapOptions,
|
|
8
|
-
SelectedSwapRoute,
|
|
9
|
-
QuoteResult,
|
|
10
|
-
SwapExecutionData,
|
|
11
|
-
RouteQuote,
|
|
12
|
-
SwapDataParamsAndPostConditions,
|
|
13
|
-
BitflowSDKConfig,
|
|
14
|
-
} from './types';
|
|
15
|
-
import { fetchAllTokensFromAPI } from './helpers/fetchDataHelper';
|
|
16
|
-
import { executeSwapHelper } from './helpers/callSwapHelper';
|
|
17
|
-
import {
|
|
18
|
-
callReadOnlyFunctionHelper,
|
|
19
|
-
callReadOnlyFunctionHelperWithoutScaling,
|
|
20
|
-
} from './helpers/callReadOnlyHelper';
|
|
21
|
-
import { fetchPossibleSwapsFromAPI } from './helpers/fetchPossibleSwap';
|
|
22
|
-
import { getContractInterfaceAndFunction } from './helpers/getContractInterfaceAndFunction';
|
|
23
|
-
import { configs, validateConfig } from './config';
|
|
24
|
-
import { executeGetParams } from './helpers/callGetSwapParams';
|
|
25
|
-
import {
|
|
26
|
-
ActionFunctionArgs,
|
|
27
|
-
AggregatorRouteData,
|
|
28
|
-
ALLOWED_CONTRACTS,
|
|
29
|
-
CancelGroupOrderResponse,
|
|
30
|
-
CancelOrderResponse,
|
|
31
|
-
CreateGroupOrderParams,
|
|
32
|
-
CreateGroupOrderResponse,
|
|
33
|
-
CreateOrderParams,
|
|
34
|
-
CreateOrderResponse,
|
|
35
|
-
GetGroupOrderResponse,
|
|
36
|
-
GetKeeperContractParams,
|
|
37
|
-
GetKeeperContractResponse,
|
|
38
|
-
GetOrderResponse,
|
|
39
|
-
GetQuoteParams,
|
|
40
|
-
GetQuoteResponse,
|
|
41
|
-
GetUserResponse,
|
|
42
|
-
KeeperOrderRouteData,
|
|
43
|
-
} from './keeper/types';
|
|
44
|
-
import {
|
|
45
|
-
cancelGroupOrderAPI,
|
|
46
|
-
cancelOrderAPI,
|
|
47
|
-
createGroupOrderAPI,
|
|
48
|
-
createOrderAPI,
|
|
49
|
-
getGroupOrderAPI,
|
|
50
|
-
getOrCreateKeeperContractAPI,
|
|
51
|
-
getOrderAPI,
|
|
52
|
-
getQuoteAPI,
|
|
53
|
-
getUserAPI,
|
|
54
|
-
} from './keeper/keeperAPI';
|
|
55
|
-
|
|
56
|
-
export const safeStringify = (obj: any, indent = 2) => {
|
|
57
|
-
return JSON.stringify(
|
|
58
|
-
obj,
|
|
59
|
-
(_, value) => (typeof value === 'bigint' ? value.toString() + 'n' : value),
|
|
60
|
-
indent
|
|
61
|
-
);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export class BitflowSDK {
|
|
65
|
-
private context: SwapContext;
|
|
66
|
-
private stacksProvider: StacksProvider | undefined;
|
|
67
|
-
|
|
68
|
-
constructor(config?: Partial<BitflowSDKConfig>) {
|
|
69
|
-
if (config) Object.assign(configs, config);
|
|
70
|
-
|
|
71
|
-
validateConfig();
|
|
72
|
-
|
|
73
|
-
this.context = {
|
|
74
|
-
availableTokens: [],
|
|
75
|
-
contractInterfaces: {},
|
|
76
|
-
functionArgs: {},
|
|
77
|
-
network: STACKS_MAINNET,
|
|
78
|
-
swapOptions: {},
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
this.initializeContext();
|
|
82
|
-
}
|
|
83
|
-
private async loadConnectDependencies() {
|
|
84
|
-
if (typeof window === 'undefined') {
|
|
85
|
-
throw new Error(
|
|
86
|
-
'Connect features are only available in browser environments'
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
const { getStacksProvider } = await import('@stacks/connect');
|
|
92
|
-
if (!this.stacksProvider) {
|
|
93
|
-
this.stacksProvider = await getStacksProvider();
|
|
94
|
-
}
|
|
95
|
-
return this.stacksProvider;
|
|
96
|
-
} catch (error) {
|
|
97
|
-
console.error('Error loading Stacks Connect:', error);
|
|
98
|
-
throw new Error('Failed to load Stacks Connect dependencies');
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
private async initializeContext(): Promise<void> {
|
|
102
|
-
this.context.availableTokens = await fetchAllTokensFromAPI();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
public async getAvailableTokens(): Promise<Token[]> {
|
|
106
|
-
if (this.context.availableTokens.length === 0) {
|
|
107
|
-
await this.initializeContext();
|
|
108
|
-
}
|
|
109
|
-
return this.context.availableTokens;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
public async getKeeperTokens(): Promise<Token[]> {
|
|
113
|
-
if (this.context.availableTokens.length === 0) {
|
|
114
|
-
await this.initializeContext();
|
|
115
|
-
}
|
|
116
|
-
return this.context.availableTokens.filter((token) => token.isKeeperToken);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
public async getPossibleSwaps(tokenX: string): Promise<SwapOptions> {
|
|
120
|
-
if (!this.context.swapOptions[tokenX]) {
|
|
121
|
-
this.context.swapOptions[tokenX] = await fetchPossibleSwapsFromAPI(
|
|
122
|
-
tokenX
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
return this.context.swapOptions[tokenX];
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
public async getKeeperPossibleSwaps(tokenX: string): Promise<SwapOptions> {
|
|
129
|
-
// Use a separate cache key for keeper routes
|
|
130
|
-
const cacheKey = `keeper_${tokenX}`;
|
|
131
|
-
if (!this.context.swapOptions[cacheKey]) {
|
|
132
|
-
this.context.swapOptions[cacheKey] = await fetchPossibleSwapsFromAPI(
|
|
133
|
-
tokenX,
|
|
134
|
-
'KEEPER'
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
return this.context.swapOptions[cacheKey];
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
public async getAllPossibleTokenY(tokenX: string): Promise<string[]> {
|
|
141
|
-
const swapOptions = await this.getPossibleSwaps(tokenX);
|
|
142
|
-
return Object.keys(swapOptions);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
public async getAllKeeperPossibleTokenY(tokenX: string): Promise<string[]> {
|
|
146
|
-
const swapOptions = await this.getKeeperPossibleSwaps(tokenX);
|
|
147
|
-
return Object.keys(swapOptions);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
public async getAllPossibleTokenYRoutes(
|
|
151
|
-
tokenX: string,
|
|
152
|
-
tokenY: string
|
|
153
|
-
): Promise<SelectedSwapRoute[]> {
|
|
154
|
-
const swapOptions = await this.getPossibleSwaps(tokenX);
|
|
155
|
-
return swapOptions[tokenY] || [];
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
public async getAllKeeperPossibleTokenYRoutes(
|
|
159
|
-
tokenX: string,
|
|
160
|
-
tokenY: string
|
|
161
|
-
): Promise<SelectedSwapRoute[]> {
|
|
162
|
-
const swapOptions = await this.getKeeperPossibleSwaps(tokenX);
|
|
163
|
-
return swapOptions[tokenY] || [];
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
public async getQuoteForRoute(
|
|
167
|
-
tokenX: string,
|
|
168
|
-
tokenY: string,
|
|
169
|
-
amountInput: number
|
|
170
|
-
): Promise<QuoteResult> {
|
|
171
|
-
const providerAddress = configs.BITFLOW_PROVIDER_ADDRESS;
|
|
172
|
-
|
|
173
|
-
const routes = await this.getAllPossibleTokenYRoutes(tokenX, tokenY);
|
|
174
|
-
const allRoutes: RouteQuote[] = [];
|
|
175
|
-
|
|
176
|
-
for (let routeIndex = 0; routeIndex < routes.length; routeIndex++) {
|
|
177
|
-
const route = routes[routeIndex];
|
|
178
|
-
try {
|
|
179
|
-
if (!route.quoteData) {
|
|
180
|
-
console.warn(
|
|
181
|
-
`Skipping route ${routeIndex + 1} due to null quoteData:`,
|
|
182
|
-
route
|
|
183
|
-
);
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const {
|
|
188
|
-
contract,
|
|
189
|
-
function: functionName,
|
|
190
|
-
parameters,
|
|
191
|
-
} = route.quoteData;
|
|
192
|
-
|
|
193
|
-
if (!contract || !functionName || !parameters) {
|
|
194
|
-
console.warn(
|
|
195
|
-
`Skipping route ${
|
|
196
|
-
routeIndex + 1
|
|
197
|
-
} due to missing required properties:`,
|
|
198
|
-
route.quoteData
|
|
199
|
-
);
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const [contractDeployer, contractName] = contract.split('.');
|
|
204
|
-
|
|
205
|
-
if (!this.context.contractInterfaces[contract]) {
|
|
206
|
-
this.context.contractInterfaces[contract] =
|
|
207
|
-
await getContractInterfaceAndFunction(
|
|
208
|
-
contractDeployer,
|
|
209
|
-
contractName,
|
|
210
|
-
functionName
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
const { interface: contractInterface, functionArgs } =
|
|
214
|
-
this.context.contractInterfaces[contract];
|
|
215
|
-
|
|
216
|
-
const params = { ...parameters };
|
|
217
|
-
|
|
218
|
-
if ('dx' in params && params.dx === null) {
|
|
219
|
-
params.dx = amountInput;
|
|
220
|
-
} else if ('dy' in params && params.dy === null) {
|
|
221
|
-
params.dy = amountInput;
|
|
222
|
-
} else if ('amount' in params && params.amount === null) {
|
|
223
|
-
params.amount = amountInput;
|
|
224
|
-
} else if ('amt-in' in params && params['amt-in'] === null) {
|
|
225
|
-
params['amt-in'] = amountInput;
|
|
226
|
-
} else if ('amt-in-max' in params && params['amt-in-max'] === null) {
|
|
227
|
-
params['amt-in-max'] = amountInput;
|
|
228
|
-
} else if ('y-amount' in params && params['y-amount'] === null) {
|
|
229
|
-
params['y-amount'] = amountInput;
|
|
230
|
-
params['x-amount'] = amountInput;
|
|
231
|
-
} else if ('x-amount' in params && params['x-amount'] === null) {
|
|
232
|
-
params['x-amount'] = amountInput;
|
|
233
|
-
} else {
|
|
234
|
-
params.dx = amountInput;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Robust provider check
|
|
238
|
-
const functionRequiresProvider = functionArgs.some((arg: { name: string; }) => arg.name === 'provider');
|
|
239
|
-
if (
|
|
240
|
-
functionRequiresProvider &&
|
|
241
|
-
(params.provider === null || params.provider === undefined) &&
|
|
242
|
-
providerAddress && validateStacksAddress(providerAddress)
|
|
243
|
-
) {
|
|
244
|
-
params.provider = providerAddress;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const { convertedResult, rawResult, tokenXDecimals, tokenYDecimals } =
|
|
248
|
-
await callReadOnlyFunctionHelper(
|
|
249
|
-
contractDeployer,
|
|
250
|
-
contractName,
|
|
251
|
-
functionName,
|
|
252
|
-
params,
|
|
253
|
-
contractDeployer,
|
|
254
|
-
tokenX,
|
|
255
|
-
tokenY,
|
|
256
|
-
route.swapData,
|
|
257
|
-
this.context
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
if (typeof convertedResult === 'number' && convertedResult > 0) {
|
|
261
|
-
const updatedQuoteData = {
|
|
262
|
-
...route.quoteData,
|
|
263
|
-
parameters: { ...params },
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
const updatedSwapData = {
|
|
267
|
-
...route.swapData,
|
|
268
|
-
parameters: {
|
|
269
|
-
...route.swapData.parameters,
|
|
270
|
-
amount:
|
|
271
|
-
params.amount ||
|
|
272
|
-
params.dx ||
|
|
273
|
-
params['amt-in'] ||
|
|
274
|
-
params['amt-in-max'] ||
|
|
275
|
-
params['y-amount'] ||
|
|
276
|
-
params['x-amount'] ||
|
|
277
|
-
params.dy,
|
|
278
|
-
dx:
|
|
279
|
-
params.amount ||
|
|
280
|
-
params.dx ||
|
|
281
|
-
params['amt-in'] ||
|
|
282
|
-
params['amt-in-max'] ||
|
|
283
|
-
params['y-amount'] ||
|
|
284
|
-
params['x-amount'] ||
|
|
285
|
-
params.dy,
|
|
286
|
-
dy:
|
|
287
|
-
params.amount ||
|
|
288
|
-
params.dx ||
|
|
289
|
-
params['amt-in'] ||
|
|
290
|
-
params['amt-in-max'] ||
|
|
291
|
-
params['y-amount'] ||
|
|
292
|
-
params['x-amount'] ||
|
|
293
|
-
params.dy,
|
|
294
|
-
'amt-in':
|
|
295
|
-
params.amount ||
|
|
296
|
-
params.dx ||
|
|
297
|
-
params['amt-in'] ||
|
|
298
|
-
params['amt-in-max'] ||
|
|
299
|
-
params['y-amount'] ||
|
|
300
|
-
params['x-amount'] ||
|
|
301
|
-
params.dy,
|
|
302
|
-
'amt-in-max':
|
|
303
|
-
params.amount ||
|
|
304
|
-
params.dx ||
|
|
305
|
-
params['amt-in'] ||
|
|
306
|
-
params['amt-in-max'] ||
|
|
307
|
-
params['y-amount'] ||
|
|
308
|
-
params['x-amount'] ||
|
|
309
|
-
params.dy,
|
|
310
|
-
'y-amount':
|
|
311
|
-
params.amount ||
|
|
312
|
-
params.dx ||
|
|
313
|
-
params['amt-in'] ||
|
|
314
|
-
params['amt-in-max'] ||
|
|
315
|
-
params['y-amount'] ||
|
|
316
|
-
params['x-amount'] ||
|
|
317
|
-
params.dy,
|
|
318
|
-
'x-amount':
|
|
319
|
-
params.amount ||
|
|
320
|
-
params.dx ||
|
|
321
|
-
params['amt-in'] ||
|
|
322
|
-
params['amt-in-max'] ||
|
|
323
|
-
params['y-amount'] ||
|
|
324
|
-
params['x-amount'] ||
|
|
325
|
-
params.dy,
|
|
326
|
-
|
|
327
|
-
'min-received': rawResult,
|
|
328
|
-
'min-dy': rawResult,
|
|
329
|
-
'min-dz': rawResult,
|
|
330
|
-
'min-dw': rawResult,
|
|
331
|
-
'amt-out': rawResult,
|
|
332
|
-
'amt-out-min': rawResult,
|
|
333
|
-
'min-x-amount': rawResult,
|
|
334
|
-
'min-dv': rawResult,
|
|
335
|
-
'min-y-amount': rawResult,
|
|
336
|
-
'min-dx': rawResult,
|
|
337
|
-
'provider':
|
|
338
|
-
providerAddress ||
|
|
339
|
-
params.provider
|
|
340
|
-
},
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
const quoteResult: RouteQuote = {
|
|
344
|
-
route: {
|
|
345
|
-
...route,
|
|
346
|
-
quoteData: updatedQuoteData,
|
|
347
|
-
swapData: updatedSwapData,
|
|
348
|
-
},
|
|
349
|
-
quote: convertedResult,
|
|
350
|
-
params: params,
|
|
351
|
-
quoteData: updatedQuoteData,
|
|
352
|
-
swapData: updatedSwapData,
|
|
353
|
-
dexPath: route.dex_path,
|
|
354
|
-
tokenPath: route.token_path,
|
|
355
|
-
tokenXDecimals: tokenXDecimals,
|
|
356
|
-
tokenYDecimals: tokenYDecimals,
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
allRoutes.push(quoteResult);
|
|
360
|
-
} else {
|
|
361
|
-
throw new Error('Invalid quote result');
|
|
362
|
-
}
|
|
363
|
-
} catch (error) {
|
|
364
|
-
console.warn(
|
|
365
|
-
`Failed to get quote for route ${routeIndex + 1}:`,
|
|
366
|
-
route,
|
|
367
|
-
error
|
|
368
|
-
);
|
|
369
|
-
allRoutes.push({
|
|
370
|
-
route,
|
|
371
|
-
quote: null,
|
|
372
|
-
params: route.quoteData
|
|
373
|
-
? { ...route.quoteData.parameters, amountInput }
|
|
374
|
-
: { amountInput },
|
|
375
|
-
quoteData: route.quoteData,
|
|
376
|
-
swapData: route.swapData,
|
|
377
|
-
dexPath: route.dex_path,
|
|
378
|
-
tokenPath: route.token_path,
|
|
379
|
-
tokenXDecimals: route.tokenXDecimals,
|
|
380
|
-
tokenYDecimals: route.tokenYDecimals,
|
|
381
|
-
error: (error as Error).message,
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
allRoutes.sort((a, b) => (b.quote || 0) - (a.quote || 0));
|
|
387
|
-
|
|
388
|
-
const result = {
|
|
389
|
-
bestRoute: allRoutes[0]?.quote !== null ? allRoutes[0] : null,
|
|
390
|
-
allRoutes,
|
|
391
|
-
inputData: { tokenX, tokenY, amountInput, provider: providerAddress },
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
return result;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
public async getKeeperQuoteForRoute(
|
|
398
|
-
tokenX: string,
|
|
399
|
-
tokenY: string,
|
|
400
|
-
amountInput: number
|
|
401
|
-
): Promise<QuoteResult> {
|
|
402
|
-
const COMPATIBLE_DEX_PATHS = new Set([
|
|
403
|
-
'BITFLOW_STABLE_XY_2',
|
|
404
|
-
'BITFLOW_XYK_XY_2',
|
|
405
|
-
]);
|
|
406
|
-
|
|
407
|
-
const isCompatibleRoute = (dexPath: string[]): boolean => {
|
|
408
|
-
return dexPath.every((path) => COMPATIBLE_DEX_PATHS.has(path));
|
|
409
|
-
};
|
|
410
|
-
|
|
411
|
-
let routes = await this.getAllPossibleTokenYRoutes(tokenX, tokenY);
|
|
412
|
-
routes = routes.filter((route) => isCompatibleRoute(route.dex_path));
|
|
413
|
-
|
|
414
|
-
const allRoutes: RouteQuote[] = [];
|
|
415
|
-
|
|
416
|
-
for (let routeIndex = 0; routeIndex < routes.length; routeIndex++) {
|
|
417
|
-
const route = routes[routeIndex];
|
|
418
|
-
|
|
419
|
-
try {
|
|
420
|
-
if (!route.quoteData) {
|
|
421
|
-
console.warn(
|
|
422
|
-
`Skipping route ${routeIndex + 1} due to null quoteData:`,
|
|
423
|
-
route
|
|
424
|
-
);
|
|
425
|
-
continue;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const {
|
|
429
|
-
contract,
|
|
430
|
-
function: functionName,
|
|
431
|
-
parameters,
|
|
432
|
-
} = route.quoteData;
|
|
433
|
-
|
|
434
|
-
if (!contract || !functionName || !parameters) {
|
|
435
|
-
console.warn(
|
|
436
|
-
`Skipping route ${
|
|
437
|
-
routeIndex + 1
|
|
438
|
-
} due to missing required properties:`,
|
|
439
|
-
route.quoteData
|
|
440
|
-
);
|
|
441
|
-
continue;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
const [contractDeployer, contractName] = contract.split('.');
|
|
445
|
-
|
|
446
|
-
if (!this.context.contractInterfaces[contract]) {
|
|
447
|
-
this.context.contractInterfaces[contract] =
|
|
448
|
-
await getContractInterfaceAndFunction(
|
|
449
|
-
contractDeployer,
|
|
450
|
-
contractName,
|
|
451
|
-
functionName
|
|
452
|
-
);
|
|
453
|
-
}
|
|
454
|
-
const { interface: contractInterface, functionArgs } =
|
|
455
|
-
this.context.contractInterfaces[contract];
|
|
456
|
-
|
|
457
|
-
const params = { ...parameters };
|
|
458
|
-
|
|
459
|
-
if ('dx' in params && params.dx === null) {
|
|
460
|
-
params.dx = amountInput;
|
|
461
|
-
} else if ('dy' in params && params.dy === null) {
|
|
462
|
-
params.dy = amountInput;
|
|
463
|
-
} else if ('amount' in params && params.amount === null) {
|
|
464
|
-
params.amount = amountInput;
|
|
465
|
-
} else if ('amt-in' in params && params['amt-in'] === null) {
|
|
466
|
-
params['amt-in'] = amountInput;
|
|
467
|
-
} else if ('amt-in-max' in params && params['amt-in-max'] === null) {
|
|
468
|
-
params['amt-in-max'] = amountInput;
|
|
469
|
-
} else if ('y-amount' in params && params['y-amount'] === null) {
|
|
470
|
-
params['y-amount'] = amountInput;
|
|
471
|
-
params['x-amount'] = amountInput;
|
|
472
|
-
} else if ('x-amount' in params && params['x-amount'] === null) {
|
|
473
|
-
params['x-amount'] = amountInput;
|
|
474
|
-
} else {
|
|
475
|
-
params.dx = amountInput;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
const { convertedResult, rawResult, tokenXDecimals, tokenYDecimals } =
|
|
479
|
-
await callReadOnlyFunctionHelper(
|
|
480
|
-
contractDeployer,
|
|
481
|
-
contractName,
|
|
482
|
-
functionName,
|
|
483
|
-
params,
|
|
484
|
-
contractDeployer,
|
|
485
|
-
tokenX,
|
|
486
|
-
tokenY,
|
|
487
|
-
route.swapData,
|
|
488
|
-
this.context
|
|
489
|
-
);
|
|
490
|
-
|
|
491
|
-
if (typeof convertedResult === 'number' && convertedResult > 0) {
|
|
492
|
-
const updatedQuoteData = {
|
|
493
|
-
...route.quoteData,
|
|
494
|
-
parameters: { ...params },
|
|
495
|
-
};
|
|
496
|
-
|
|
497
|
-
const updatedSwapData = {
|
|
498
|
-
...route.swapData,
|
|
499
|
-
parameters: {
|
|
500
|
-
...route.swapData.parameters,
|
|
501
|
-
amount:
|
|
502
|
-
params.amount ||
|
|
503
|
-
params.dx ||
|
|
504
|
-
params['amt-in'] ||
|
|
505
|
-
params['amt-in-max'] ||
|
|
506
|
-
params['y-amount'] ||
|
|
507
|
-
params['x-amount'] ||
|
|
508
|
-
params.dy,
|
|
509
|
-
dx:
|
|
510
|
-
params.amount ||
|
|
511
|
-
params.dx ||
|
|
512
|
-
params['amt-in'] ||
|
|
513
|
-
params['amt-in-max'] ||
|
|
514
|
-
params['y-amount'] ||
|
|
515
|
-
params['x-amount'] ||
|
|
516
|
-
params.dy,
|
|
517
|
-
'amt-in':
|
|
518
|
-
params.amount ||
|
|
519
|
-
params.dx ||
|
|
520
|
-
params['amt-in'] ||
|
|
521
|
-
params['amt-in-max'] ||
|
|
522
|
-
params['y-amount'] ||
|
|
523
|
-
params['x-amount'] ||
|
|
524
|
-
params.dy,
|
|
525
|
-
'amt-in-max':
|
|
526
|
-
params.amount ||
|
|
527
|
-
params.dx ||
|
|
528
|
-
params['amt-in'] ||
|
|
529
|
-
params['amt-in-max'] ||
|
|
530
|
-
params['y-amount'] ||
|
|
531
|
-
params['x-amount'] ||
|
|
532
|
-
params.dy,
|
|
533
|
-
'y-amount':
|
|
534
|
-
params.amount ||
|
|
535
|
-
params.dx ||
|
|
536
|
-
params['amt-in'] ||
|
|
537
|
-
params['amt-in-max'] ||
|
|
538
|
-
params['y-amount'] ||
|
|
539
|
-
params['x-amount'] ||
|
|
540
|
-
params.dy,
|
|
541
|
-
'x-amount':
|
|
542
|
-
params.amount ||
|
|
543
|
-
params.dx ||
|
|
544
|
-
params['amt-in'] ||
|
|
545
|
-
params['amt-in-max'] ||
|
|
546
|
-
params['y-amount'] ||
|
|
547
|
-
params['x-amount'] ||
|
|
548
|
-
params.dy,
|
|
549
|
-
'min-received': rawResult,
|
|
550
|
-
'min-dy': rawResult,
|
|
551
|
-
'min-dz': rawResult,
|
|
552
|
-
'min-dw': rawResult,
|
|
553
|
-
'amt-out': rawResult,
|
|
554
|
-
'amt-out-min': rawResult,
|
|
555
|
-
'min-x-amount': rawResult,
|
|
556
|
-
'min-dv': rawResult,
|
|
557
|
-
'min-y-amount': rawResult,
|
|
558
|
-
'min-dx': rawResult,
|
|
559
|
-
},
|
|
560
|
-
};
|
|
561
|
-
|
|
562
|
-
const quoteResult: RouteQuote = {
|
|
563
|
-
route: {
|
|
564
|
-
...route,
|
|
565
|
-
quoteData: updatedQuoteData,
|
|
566
|
-
swapData: updatedSwapData,
|
|
567
|
-
},
|
|
568
|
-
quote: convertedResult,
|
|
569
|
-
params: params,
|
|
570
|
-
quoteData: updatedQuoteData,
|
|
571
|
-
swapData: updatedSwapData,
|
|
572
|
-
dexPath: route.dex_path,
|
|
573
|
-
tokenPath: route.token_path,
|
|
574
|
-
tokenXDecimals: tokenXDecimals,
|
|
575
|
-
tokenYDecimals: tokenYDecimals,
|
|
576
|
-
};
|
|
577
|
-
|
|
578
|
-
allRoutes.push(quoteResult);
|
|
579
|
-
} else {
|
|
580
|
-
throw new Error('Invalid quote result');
|
|
581
|
-
}
|
|
582
|
-
} catch (error) {
|
|
583
|
-
console.warn(
|
|
584
|
-
`Failed to get quote for route ${routeIndex + 1}:`,
|
|
585
|
-
route,
|
|
586
|
-
error
|
|
587
|
-
);
|
|
588
|
-
allRoutes.push({
|
|
589
|
-
route,
|
|
590
|
-
quote: null,
|
|
591
|
-
params: route.quoteData
|
|
592
|
-
? { ...route.quoteData.parameters, amountInput }
|
|
593
|
-
: { amountInput },
|
|
594
|
-
quoteData: route.quoteData,
|
|
595
|
-
swapData: route.swapData,
|
|
596
|
-
dexPath: route.dex_path,
|
|
597
|
-
tokenPath: route.token_path,
|
|
598
|
-
tokenXDecimals: route.tokenXDecimals,
|
|
599
|
-
tokenYDecimals: route.tokenYDecimals,
|
|
600
|
-
error: (error as Error).message,
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
allRoutes.sort((a, b) => (b.quote || 0) - (a.quote || 0));
|
|
606
|
-
const result = {
|
|
607
|
-
bestRoute: allRoutes[0]?.quote !== null ? allRoutes[0] : null,
|
|
608
|
-
allRoutes,
|
|
609
|
-
inputData: { tokenX, tokenY, amountInput },
|
|
610
|
-
};
|
|
611
|
-
|
|
612
|
-
return result;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
public async getKeeperQuoteForRouteWithoutScaling(
|
|
616
|
-
tokenX: string,
|
|
617
|
-
tokenY: string,
|
|
618
|
-
amountInput: number
|
|
619
|
-
): Promise<QuoteResult> {
|
|
620
|
-
const COMPATIBLE_DEX_PATHS = new Set([
|
|
621
|
-
'BITFLOW_STABLE_XY_2',
|
|
622
|
-
'BITFLOW_XYK_XY_2',
|
|
623
|
-
]);
|
|
624
|
-
|
|
625
|
-
const isCompatibleRoute = (dexPath: string[]): boolean => {
|
|
626
|
-
return dexPath.every((path) => COMPATIBLE_DEX_PATHS.has(path));
|
|
627
|
-
};
|
|
628
|
-
|
|
629
|
-
let routes = await this.getAllPossibleTokenYRoutes(tokenX, tokenY);
|
|
630
|
-
routes = routes.filter((route) => isCompatibleRoute(route.dex_path));
|
|
631
|
-
|
|
632
|
-
const allRoutes: RouteQuote[] = [];
|
|
633
|
-
|
|
634
|
-
for (let routeIndex = 0; routeIndex < routes.length; routeIndex++) {
|
|
635
|
-
const route = routes[routeIndex];
|
|
636
|
-
|
|
637
|
-
try {
|
|
638
|
-
if (!route.quoteData) {
|
|
639
|
-
console.warn(
|
|
640
|
-
`Skipping route ${routeIndex + 1} due to null quoteData:`,
|
|
641
|
-
route
|
|
642
|
-
);
|
|
643
|
-
continue;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
const {
|
|
647
|
-
contract,
|
|
648
|
-
function: functionName,
|
|
649
|
-
parameters,
|
|
650
|
-
} = route.quoteData;
|
|
651
|
-
|
|
652
|
-
if (!contract || !functionName || !parameters) {
|
|
653
|
-
console.warn(
|
|
654
|
-
`Skipping route ${
|
|
655
|
-
routeIndex + 1
|
|
656
|
-
} due to missing required properties:`,
|
|
657
|
-
route.quoteData
|
|
658
|
-
);
|
|
659
|
-
continue;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
const [contractDeployer, contractName] = contract.split('.');
|
|
663
|
-
|
|
664
|
-
if (!this.context.contractInterfaces[contract]) {
|
|
665
|
-
this.context.contractInterfaces[contract] =
|
|
666
|
-
await getContractInterfaceAndFunction(
|
|
667
|
-
contractDeployer,
|
|
668
|
-
contractName,
|
|
669
|
-
functionName
|
|
670
|
-
);
|
|
671
|
-
}
|
|
672
|
-
const { interface: contractInterface, functionArgs } =
|
|
673
|
-
this.context.contractInterfaces[contract];
|
|
674
|
-
|
|
675
|
-
const params = { ...parameters };
|
|
676
|
-
|
|
677
|
-
if ('dx' in params && params.dx === null) {
|
|
678
|
-
params.dx = amountInput;
|
|
679
|
-
} else if ('dy' in params && params.dy === null) {
|
|
680
|
-
params.dy = amountInput;
|
|
681
|
-
} else if ('amount' in params && params.amount === null) {
|
|
682
|
-
params.amount = amountInput;
|
|
683
|
-
} else if ('amt-in' in params && params['amt-in'] === null) {
|
|
684
|
-
params['amt-in'] = amountInput;
|
|
685
|
-
} else if ('amt-in-max' in params && params['amt-in-max'] === null) {
|
|
686
|
-
params['amt-in-max'] = amountInput;
|
|
687
|
-
} else if ('y-amount' in params && params['y-amount'] === null) {
|
|
688
|
-
params['y-amount'] = amountInput;
|
|
689
|
-
params['x-amount'] = amountInput;
|
|
690
|
-
} else if ('x-amount' in params && params['x-amount'] === null) {
|
|
691
|
-
params['x-amount'] = amountInput;
|
|
692
|
-
} else {
|
|
693
|
-
params.dx = amountInput;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
const { convertedResult, rawResult, tokenXDecimals, tokenYDecimals } =
|
|
697
|
-
await callReadOnlyFunctionHelperWithoutScaling(
|
|
698
|
-
contractDeployer,
|
|
699
|
-
contractName,
|
|
700
|
-
functionName,
|
|
701
|
-
params,
|
|
702
|
-
contractDeployer,
|
|
703
|
-
tokenX,
|
|
704
|
-
tokenY,
|
|
705
|
-
route.swapData,
|
|
706
|
-
this.context
|
|
707
|
-
);
|
|
708
|
-
|
|
709
|
-
if (typeof convertedResult === 'number' && convertedResult > 0) {
|
|
710
|
-
const updatedQuoteData = {
|
|
711
|
-
...route.quoteData,
|
|
712
|
-
parameters: { ...params },
|
|
713
|
-
};
|
|
714
|
-
|
|
715
|
-
const updatedSwapData = {
|
|
716
|
-
...route.swapData,
|
|
717
|
-
parameters: {
|
|
718
|
-
...route.swapData.parameters,
|
|
719
|
-
amount:
|
|
720
|
-
params.amount ||
|
|
721
|
-
params.dx ||
|
|
722
|
-
params['amt-in'] ||
|
|
723
|
-
params['amt-in-max'] ||
|
|
724
|
-
params['y-amount'] ||
|
|
725
|
-
params['x-amount'] ||
|
|
726
|
-
params.dy,
|
|
727
|
-
dx:
|
|
728
|
-
params.amount ||
|
|
729
|
-
params.dx ||
|
|
730
|
-
params['amt-in'] ||
|
|
731
|
-
params['amt-in-max'] ||
|
|
732
|
-
params['y-amount'] ||
|
|
733
|
-
params['x-amount'] ||
|
|
734
|
-
params.dy,
|
|
735
|
-
'amt-in':
|
|
736
|
-
params.amount ||
|
|
737
|
-
params.dx ||
|
|
738
|
-
params['amt-in'] ||
|
|
739
|
-
params['amt-in-max'] ||
|
|
740
|
-
params['y-amount'] ||
|
|
741
|
-
params['x-amount'] ||
|
|
742
|
-
params.dy,
|
|
743
|
-
'amt-in-max':
|
|
744
|
-
params.amount ||
|
|
745
|
-
params.dx ||
|
|
746
|
-
params['amt-in'] ||
|
|
747
|
-
params['amt-in-max'] ||
|
|
748
|
-
params['y-amount'] ||
|
|
749
|
-
params['x-amount'] ||
|
|
750
|
-
params.dy,
|
|
751
|
-
'y-amount':
|
|
752
|
-
params.amount ||
|
|
753
|
-
params.dx ||
|
|
754
|
-
params['amt-in'] ||
|
|
755
|
-
params['amt-in-max'] ||
|
|
756
|
-
params['y-amount'] ||
|
|
757
|
-
params['x-amount'] ||
|
|
758
|
-
params.dy,
|
|
759
|
-
'x-amount':
|
|
760
|
-
params.amount ||
|
|
761
|
-
params.dx ||
|
|
762
|
-
params['amt-in'] ||
|
|
763
|
-
params['amt-in-max'] ||
|
|
764
|
-
params['y-amount'] ||
|
|
765
|
-
params['x-amount'] ||
|
|
766
|
-
params.dy,
|
|
767
|
-
'min-received': rawResult,
|
|
768
|
-
'min-dy': rawResult,
|
|
769
|
-
'min-dz': rawResult,
|
|
770
|
-
'min-dw': rawResult,
|
|
771
|
-
'amt-out': rawResult,
|
|
772
|
-
'amt-out-min': rawResult,
|
|
773
|
-
'min-x-amount': rawResult,
|
|
774
|
-
'min-dv': rawResult,
|
|
775
|
-
'min-y-amount': rawResult,
|
|
776
|
-
'min-dx': rawResult,
|
|
777
|
-
},
|
|
778
|
-
};
|
|
779
|
-
|
|
780
|
-
const quoteResult: RouteQuote = {
|
|
781
|
-
route: {
|
|
782
|
-
...route,
|
|
783
|
-
quoteData: updatedQuoteData,
|
|
784
|
-
swapData: updatedSwapData,
|
|
785
|
-
},
|
|
786
|
-
quote: convertedResult,
|
|
787
|
-
params: params,
|
|
788
|
-
quoteData: updatedQuoteData,
|
|
789
|
-
swapData: updatedSwapData,
|
|
790
|
-
dexPath: route.dex_path,
|
|
791
|
-
tokenPath: route.token_path,
|
|
792
|
-
tokenXDecimals: tokenXDecimals,
|
|
793
|
-
tokenYDecimals: tokenYDecimals,
|
|
794
|
-
};
|
|
795
|
-
|
|
796
|
-
allRoutes.push(quoteResult);
|
|
797
|
-
} else {
|
|
798
|
-
throw new Error('Invalid quote result');
|
|
799
|
-
}
|
|
800
|
-
} catch (error) {
|
|
801
|
-
console.warn(
|
|
802
|
-
`Failed to get quote for route ${routeIndex + 1}:`,
|
|
803
|
-
route,
|
|
804
|
-
error
|
|
805
|
-
);
|
|
806
|
-
allRoutes.push({
|
|
807
|
-
route,
|
|
808
|
-
quote: null,
|
|
809
|
-
params: route.quoteData
|
|
810
|
-
? { ...route.quoteData.parameters, amountInput }
|
|
811
|
-
: { amountInput },
|
|
812
|
-
quoteData: route.quoteData,
|
|
813
|
-
swapData: route.swapData,
|
|
814
|
-
dexPath: route.dex_path,
|
|
815
|
-
tokenPath: route.token_path,
|
|
816
|
-
tokenXDecimals: route.tokenXDecimals,
|
|
817
|
-
tokenYDecimals: route.tokenYDecimals,
|
|
818
|
-
error: (error as Error).message,
|
|
819
|
-
});
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
allRoutes.sort((a, b) => (b.quote || 0) - (a.quote || 0));
|
|
824
|
-
const result = {
|
|
825
|
-
bestRoute: allRoutes[0]?.quote !== null ? allRoutes[0] : null,
|
|
826
|
-
allRoutes,
|
|
827
|
-
inputData: { tokenX, tokenY, amountInput },
|
|
828
|
-
};
|
|
829
|
-
|
|
830
|
-
return result;
|
|
831
|
-
}
|
|
832
|
-
public async getSwapParams(
|
|
833
|
-
swapExecutionData: SwapExecutionData,
|
|
834
|
-
senderAddress: string,
|
|
835
|
-
slippageTolerance: number = 0.015
|
|
836
|
-
): Promise<SwapDataParamsAndPostConditions> {
|
|
837
|
-
const { route, amount, tokenXDecimals, tokenYDecimals } = swapExecutionData;
|
|
838
|
-
|
|
839
|
-
const executionData: SwapExecutionData = {
|
|
840
|
-
route: {
|
|
841
|
-
...route,
|
|
842
|
-
swapData: {
|
|
843
|
-
...route.swapData,
|
|
844
|
-
parameters: {
|
|
845
|
-
...route.swapData.parameters,
|
|
846
|
-
amount:
|
|
847
|
-
route.swapData.parameters.amount ||
|
|
848
|
-
amount ||
|
|
849
|
-
route.swapData.parameters['amt-in'] ||
|
|
850
|
-
route.swapData.parameters['amt-in-max'] ||
|
|
851
|
-
route.swapData.parameters['y-amount'],
|
|
852
|
-
dx:
|
|
853
|
-
route.swapData.parameters.dx ||
|
|
854
|
-
amount ||
|
|
855
|
-
route.swapData.parameters['amt-in'] ||
|
|
856
|
-
route.swapData.parameters['amt-in-max'] ||
|
|
857
|
-
route.swapData.parameters['y-amount'],
|
|
858
|
-
'amt-in':
|
|
859
|
-
route.swapData.parameters.dx ||
|
|
860
|
-
amount ||
|
|
861
|
-
route.swapData.parameters['amt-in'] ||
|
|
862
|
-
route.swapData.parameters['amt-in-max'] ||
|
|
863
|
-
route.swapData.parameters['y-amount'] ||
|
|
864
|
-
route.swapData.parameters.dy,
|
|
865
|
-
'amt-in-max':
|
|
866
|
-
route.swapData.parameters.dx ||
|
|
867
|
-
amount ||
|
|
868
|
-
route.swapData.parameters['amt-in'] ||
|
|
869
|
-
route.swapData.parameters['amt-in-max'] ||
|
|
870
|
-
route.swapData.parameters['y-amount'] ||
|
|
871
|
-
route.swapData.parameters.dy,
|
|
872
|
-
'y-amount':
|
|
873
|
-
route.swapData.parameters.dx ||
|
|
874
|
-
amount ||
|
|
875
|
-
route.swapData.parameters['amt-in'] ||
|
|
876
|
-
route.swapData.parameters['amt-in-max'] ||
|
|
877
|
-
route.swapData.parameters['y-amount'] ||
|
|
878
|
-
route.swapData.parameters.dy,
|
|
879
|
-
dy:
|
|
880
|
-
route.swapData.parameters.dy ||
|
|
881
|
-
amount ||
|
|
882
|
-
route.swapData.parameters['amt-in'] ||
|
|
883
|
-
route.swapData.parameters['amt-in-max'] ||
|
|
884
|
-
route.swapData.parameters['y-amount'] ||
|
|
885
|
-
route.swapData.parameters.dy,
|
|
886
|
-
provider:
|
|
887
|
-
route.swapData.parameters.provider
|
|
888
|
-
},
|
|
889
|
-
},
|
|
890
|
-
},
|
|
891
|
-
amount,
|
|
892
|
-
tokenXDecimals: tokenXDecimals,
|
|
893
|
-
tokenYDecimals: tokenYDecimals,
|
|
894
|
-
};
|
|
895
|
-
|
|
896
|
-
return await executeGetParams(
|
|
897
|
-
executionData,
|
|
898
|
-
senderAddress,
|
|
899
|
-
slippageTolerance,
|
|
900
|
-
this.context
|
|
901
|
-
);
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
public async executeSwap(
|
|
905
|
-
swapExecutionData: SwapExecutionData,
|
|
906
|
-
senderAddress: string,
|
|
907
|
-
slippageTolerance: number = 0.015,
|
|
908
|
-
stacksProvider?: StacksProvider,
|
|
909
|
-
onFinish?: (data: any) => void,
|
|
910
|
-
onCancel?: () => void
|
|
911
|
-
): Promise<void> {
|
|
912
|
-
const swapParams = await this.getSwapParams(
|
|
913
|
-
swapExecutionData,
|
|
914
|
-
senderAddress,
|
|
915
|
-
slippageTolerance
|
|
916
|
-
);
|
|
917
|
-
|
|
918
|
-
if (typeof window === 'undefined') {
|
|
919
|
-
throw new Error(
|
|
920
|
-
'executeSwap is only available in browser environments. ' +
|
|
921
|
-
'For Node.js environments, use getSwapParams to get the transaction parameters ' +
|
|
922
|
-
'and handle the transaction execution separately.'
|
|
923
|
-
);
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
try {
|
|
927
|
-
let provider: StacksProvider;
|
|
928
|
-
if (stacksProvider) {
|
|
929
|
-
provider = stacksProvider;
|
|
930
|
-
} else {
|
|
931
|
-
const loadedProvider = await this.loadConnectDependencies();
|
|
932
|
-
if (!loadedProvider) {
|
|
933
|
-
throw new Error('Failed to initialize Stacks provider');
|
|
934
|
-
}
|
|
935
|
-
provider = loadedProvider;
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
await executeSwapHelper(
|
|
939
|
-
swapParams,
|
|
940
|
-
senderAddress,
|
|
941
|
-
this.context,
|
|
942
|
-
provider,
|
|
943
|
-
onFinish,
|
|
944
|
-
onCancel
|
|
945
|
-
);
|
|
946
|
-
} catch (error) {
|
|
947
|
-
if (
|
|
948
|
-
error instanceof Error &&
|
|
949
|
-
error.message.includes('only available in browser environments')
|
|
950
|
-
) {
|
|
951
|
-
throw error;
|
|
952
|
-
}
|
|
953
|
-
console.error('Error executing swap:', error);
|
|
954
|
-
throw new Error(
|
|
955
|
-
`Failed to execute swap: ${
|
|
956
|
-
error instanceof Error ? error.message : 'Unknown error'
|
|
957
|
-
}`
|
|
958
|
-
);
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
public async prepareSwap(
|
|
963
|
-
swapExecutionData: SwapExecutionData,
|
|
964
|
-
senderAddress: string,
|
|
965
|
-
slippageTolerance: number = 0.015
|
|
966
|
-
): Promise<SwapDataParamsAndPostConditions> {
|
|
967
|
-
return await this.getSwapParams(
|
|
968
|
-
swapExecutionData,
|
|
969
|
-
senderAddress,
|
|
970
|
-
slippageTolerance
|
|
971
|
-
);
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
public async getOrCreateKeeperContract(
|
|
975
|
-
params: GetKeeperContractParams
|
|
976
|
-
): Promise<GetKeeperContractResponse> {
|
|
977
|
-
try {
|
|
978
|
-
return await getOrCreateKeeperContractAPI(params);
|
|
979
|
-
} catch (error) {
|
|
980
|
-
console.error('Error in BitflowSDK.getOrCreateKeeperContract:', error);
|
|
981
|
-
throw error;
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
public async getOrder(orderId: string): Promise<GetOrderResponse> {
|
|
986
|
-
try {
|
|
987
|
-
return await getOrderAPI(orderId);
|
|
988
|
-
} catch (error) {
|
|
989
|
-
console.error('Error in BitflowSDK.getOrder:', error);
|
|
990
|
-
throw error;
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
public async getUser(stacksAddress: string): Promise<GetUserResponse> {
|
|
995
|
-
try {
|
|
996
|
-
return await getUserAPI(stacksAddress);
|
|
997
|
-
} catch (error) {
|
|
998
|
-
console.error('Error in BitflowSDK.getUser:', error);
|
|
999
|
-
throw error;
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
public async createOrder(
|
|
1004
|
-
params: CreateOrderParams
|
|
1005
|
-
): Promise<CreateOrderResponse> {
|
|
1006
|
-
try {
|
|
1007
|
-
return await createOrderAPI(params);
|
|
1008
|
-
} catch (error) {
|
|
1009
|
-
console.error('Error in BitflowSDK.createOrder:', error);
|
|
1010
|
-
throw error;
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
public async getQuote(params: GetQuoteParams): Promise<GetQuoteResponse> {
|
|
1015
|
-
try {
|
|
1016
|
-
return await getQuoteAPI(params);
|
|
1017
|
-
} catch (error) {
|
|
1018
|
-
console.error('Error in BitflowSDK.getQuote:', error);
|
|
1019
|
-
throw error;
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
private async transformRouteToActionArgs(
|
|
1024
|
-
route: RouteQuote
|
|
1025
|
-
): Promise<ActionFunctionArgs> {
|
|
1026
|
-
const actionFunctionArgs: ActionFunctionArgs = {
|
|
1027
|
-
tokenList: {},
|
|
1028
|
-
xykPoolList: {},
|
|
1029
|
-
stableswapPoolList: {},
|
|
1030
|
-
boolList: {},
|
|
1031
|
-
};
|
|
1032
|
-
|
|
1033
|
-
const swapData = route.swapData;
|
|
1034
|
-
if (!swapData || !swapData.parameters) {
|
|
1035
|
-
throw new Error(
|
|
1036
|
-
'Invalid route data - missing swapData or swapData.parameters'
|
|
1037
|
-
);
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
try {
|
|
1041
|
-
const availableTokens = await this.getAvailableTokens();
|
|
1042
|
-
const tokenPath = route.tokenPath;
|
|
1043
|
-
const expandedTokenPath: string[] = [];
|
|
1044
|
-
|
|
1045
|
-
// Expand token path
|
|
1046
|
-
for (let i = 0; i < tokenPath.length - 1; i++) {
|
|
1047
|
-
expandedTokenPath.push(tokenPath[i]);
|
|
1048
|
-
if (i > 0 && i < tokenPath.length - 1) {
|
|
1049
|
-
expandedTokenPath.push(tokenPath[i]);
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
expandedTokenPath.push(tokenPath[tokenPath.length - 1]);
|
|
1053
|
-
|
|
1054
|
-
// Map tokens to contract identifiers
|
|
1055
|
-
const tokenList: Record<string, string> = {};
|
|
1056
|
-
for (let index = 0; index < expandedTokenPath.length; index++) {
|
|
1057
|
-
const tokenId = expandedTokenPath[index];
|
|
1058
|
-
let contractIdentifier: string;
|
|
1059
|
-
|
|
1060
|
-
if (tokenId === 'token-stx') {
|
|
1061
|
-
contractIdentifier =
|
|
1062
|
-
'SM1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.token-stx-v-1-2';
|
|
1063
|
-
} else {
|
|
1064
|
-
const token = availableTokens.find((t) => t.tokenId === tokenId);
|
|
1065
|
-
if (!token || !token.tokenContract) {
|
|
1066
|
-
throw new Error(
|
|
1067
|
-
`Could not find contract identifier for token ${tokenId}`
|
|
1068
|
-
);
|
|
1069
|
-
}
|
|
1070
|
-
contractIdentifier = token.tokenContract;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
const key = String.fromCharCode(97 + index); // 97 = 'a'
|
|
1074
|
-
tokenList[key] = contractIdentifier;
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
actionFunctionArgs.tokenList = tokenList;
|
|
1078
|
-
|
|
1079
|
-
// Determine if this is a stableswap or XYK route
|
|
1080
|
-
const dexPath = route.dexPath;
|
|
1081
|
-
const isStableswapRoute = dexPath.some((path) =>
|
|
1082
|
-
path.toLowerCase().includes('stable')
|
|
1083
|
-
);
|
|
1084
|
-
|
|
1085
|
-
// Transform pool lists
|
|
1086
|
-
const transformPoolList = (pools: any): Record<string, string> => {
|
|
1087
|
-
if (typeof pools === 'string') {
|
|
1088
|
-
return { a: pools };
|
|
1089
|
-
}
|
|
1090
|
-
if (Array.isArray(pools)) {
|
|
1091
|
-
const poolObject: Record<string, string> = {};
|
|
1092
|
-
const letters = 'abcdefghijklmnopqrstuvwxyz';
|
|
1093
|
-
pools.forEach((pool, index) => {
|
|
1094
|
-
poolObject[letters[index]] = pool;
|
|
1095
|
-
});
|
|
1096
|
-
return poolObject;
|
|
1097
|
-
}
|
|
1098
|
-
if (typeof pools === 'object' && pools !== null) {
|
|
1099
|
-
return pools;
|
|
1100
|
-
}
|
|
1101
|
-
return {};
|
|
1102
|
-
};
|
|
1103
|
-
|
|
1104
|
-
// Handle pools based on whether they're stableswap or XYK
|
|
1105
|
-
if (swapData.parameters['pool-trait']) {
|
|
1106
|
-
const poolIdentifier = swapData.parameters['pool-trait'];
|
|
1107
|
-
const transformedPool = transformPoolList(poolIdentifier);
|
|
1108
|
-
|
|
1109
|
-
// Check if the pool name contains 'stableswap'
|
|
1110
|
-
if (
|
|
1111
|
-
isStableswapRoute ||
|
|
1112
|
-
(typeof poolIdentifier === 'string' &&
|
|
1113
|
-
poolIdentifier.toLowerCase().includes('stableswap'))
|
|
1114
|
-
) {
|
|
1115
|
-
actionFunctionArgs.stableswapPoolList = transformedPool;
|
|
1116
|
-
} else {
|
|
1117
|
-
actionFunctionArgs.xykPoolList = transformedPool;
|
|
1118
|
-
}
|
|
1119
|
-
} else {
|
|
1120
|
-
// Handle specific pool types
|
|
1121
|
-
if (swapData.parameters['xyk-pools']) {
|
|
1122
|
-
actionFunctionArgs.xykPoolList = transformPoolList(
|
|
1123
|
-
swapData.parameters['xyk-pools']
|
|
1124
|
-
);
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
if (swapData.parameters['stableswap-pools']) {
|
|
1128
|
-
actionFunctionArgs.stableswapPoolList = transformPoolList(
|
|
1129
|
-
swapData.parameters['stableswap-pools']
|
|
1130
|
-
);
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
if ('swaps-reversed' in swapData.parameters) {
|
|
1135
|
-
actionFunctionArgs.boolList = {
|
|
1136
|
-
a: swapData.parameters['swaps-reversed'].toString(),
|
|
1137
|
-
};
|
|
1138
|
-
} else {
|
|
1139
|
-
delete actionFunctionArgs.boolList;
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
const trait = this.mapDexPathToActionTrait(dexPath);
|
|
1143
|
-
actionFunctionArgs.actionTrait = trait;
|
|
1144
|
-
|
|
1145
|
-
return actionFunctionArgs;
|
|
1146
|
-
} catch (error) {
|
|
1147
|
-
throw error;
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
private mapDexPathToActionTrait(dexPath: string[]): string {
|
|
1152
|
-
const isXYK = dexPath.some((d) => d.toLowerCase().includes('xyk'));
|
|
1153
|
-
const isStable = dexPath.some((d) => d.toLowerCase().includes('stable'));
|
|
1154
|
-
|
|
1155
|
-
if (isXYK && !isStable) {
|
|
1156
|
-
return 'SM1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.keeper-action-1-v-1-1';
|
|
1157
|
-
}
|
|
1158
|
-
if (isXYK && isStable) {
|
|
1159
|
-
return 'SM1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.keeper-action-2-v-1-1';
|
|
1160
|
-
}
|
|
1161
|
-
if (!isXYK && isStable) {
|
|
1162
|
-
return 'SM1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.keeper-action-3-v-1-1';
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
throw new Error(`Unsupported DEX path: ${dexPath.join(', ')}`);
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
public async getKeeperAggregatorRouteData(
|
|
1169
|
-
tokenX: string,
|
|
1170
|
-
tokenY: string,
|
|
1171
|
-
amountX: number
|
|
1172
|
-
): Promise<ActionFunctionArgs> {
|
|
1173
|
-
try {
|
|
1174
|
-
const quoteResult = await this.getKeeperQuoteForRouteWithoutScaling(
|
|
1175
|
-
tokenX,
|
|
1176
|
-
tokenY,
|
|
1177
|
-
amountX
|
|
1178
|
-
);
|
|
1179
|
-
|
|
1180
|
-
if (
|
|
1181
|
-
!quoteResult ||
|
|
1182
|
-
!quoteResult.allRoutes ||
|
|
1183
|
-
quoteResult.allRoutes.length === 0
|
|
1184
|
-
) {
|
|
1185
|
-
throw new Error('No routes found');
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
const { bestRoute } = quoteResult;
|
|
1189
|
-
if (!bestRoute) {
|
|
1190
|
-
throw new Error('No best route found for keeper-compatible DEX paths');
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
const actionFunctionArgs = await this.transformRouteToActionArgs(
|
|
1194
|
-
bestRoute
|
|
1195
|
-
);
|
|
1196
|
-
return actionFunctionArgs;
|
|
1197
|
-
} catch (error) {
|
|
1198
|
-
throw error;
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
|
-
public async createGroupOrder(
|
|
1203
|
-
params: CreateGroupOrderParams
|
|
1204
|
-
): Promise<CreateGroupOrderResponse> {
|
|
1205
|
-
try {
|
|
1206
|
-
return await createGroupOrderAPI(params);
|
|
1207
|
-
} catch (error) {
|
|
1208
|
-
console.error('Error in BitflowSDK.createGroupOrder:', error);
|
|
1209
|
-
throw error;
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
public async getGroupOrder(
|
|
1214
|
-
groupId: string,
|
|
1215
|
-
includeOrders?: boolean
|
|
1216
|
-
): Promise<GetGroupOrderResponse> {
|
|
1217
|
-
try {
|
|
1218
|
-
return await getGroupOrderAPI(groupId, includeOrders);
|
|
1219
|
-
} catch (error) {
|
|
1220
|
-
console.error('Error in BitflowSDK.getGroupOrder:', error);
|
|
1221
|
-
throw error;
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
public async cancelOrder(orderId: string): Promise<CancelOrderResponse> {
|
|
1226
|
-
try {
|
|
1227
|
-
return await cancelOrderAPI(orderId);
|
|
1228
|
-
} catch (error) {
|
|
1229
|
-
console.error('Error in BitflowSDK.cancelOrder:', error);
|
|
1230
|
-
throw error;
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
public async cancelGroupOrder(
|
|
1235
|
-
groupId: string
|
|
1236
|
-
): Promise<CancelGroupOrderResponse> {
|
|
1237
|
-
try {
|
|
1238
|
-
return await cancelGroupOrderAPI(groupId);
|
|
1239
|
-
} catch (error) {
|
|
1240
|
-
console.error('Error in BitflowSDK.cancelGroupOrder:', error);
|
|
1241
|
-
throw error;
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
}
|